diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml index a0ae205..7492e99 100644 --- a/.github/FUNDING.yml +++ b/.github/FUNDING.yml @@ -1 +1 @@ -custom: https://www.paypal.me/typemill +custom: https://www.paypal.me/typemill diff --git a/.gitignore b/.gitignore index 80bc914..958b7ff 100644 --- a/.gitignore +++ b/.gitignore @@ -1,21 +1,22 @@ -cache -plugins/admin -plugins/contactform -plugins/demo -plugins/disqus -plugins/download -plugins/finalwords -plugins/joblistings -plugins/landingpage -plugins/mail -plugins/newsletter -plugins/textadds -plugins/version -settings/settings.yaml -settings/formdata.yaml -settings/users -system/vendor -tests -themes/monograf -zips +cache +plugins/admin +plugins/contactform +plugins/demo +plugins/disqus +plugins/download +plugins/finalwords +plubins/hyer +plugins/joblistings +plugins/landingpage +plugins/mail +plugins/newsletter +plugins/textadds +plugins/version +settings/settings.yaml +settings/formdata.yaml +settings/users +system/vendor +tests +themes/monograf +zips build.php \ No newline at end of file diff --git a/.htaccess b/.htaccess index 92e92fa..586f7af 100644 --- a/.htaccess +++ b/.htaccess @@ -1,41 +1,41 @@ -RewriteEngine On - -# If your homepage is http://yourdomain.com/yoursite -# Set the RewriteBase to: -# RewriteBase /yoursite - -# In some environements, an empty RewriteBase is required: -# RewriteBase / - -# Protect your system files from prying eyes -RewriteRule ^(system\/author\/) - [L] -RewriteRule ^(system) - [F,L] -RewriteRule ^(content) - [F,L] -RewriteRule ^(settings) - [F,L] -RewriteRule ^(.*)?\.yml$ - [F,L] -Rewriterule ^(.*)?\.yaml$ - [F,L] -RewriteRule ^(.*)?\.txt$ - [F,L] -RewriteRule ^(.*)?\.example$ - [F,L] -RewriteRule ^(.*/)?\.git+ - [F,L] - -# Use this to redirect HTTP to HTTPS on apache servers -# RewriteCond %{HTTPS} off -# RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] - -# Use this to redirect www to non-wwww on apache servers -# RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC] -# RewriteRule ^(.*)$ http://%1/$1 [R=301,L] - -# Use this to redirect slash/ to no slash urls on apache servers -# RewriteCond %{REQUEST_FILENAME} !-d -# RewriteRule ^(.*)/$ /$1 [R=301,L] - -# Removes index.php -RewriteCond %{THE_REQUEST} ^GET.*index\.php [NC] -RewriteRule (.*?)index\.php/*(.*) /$1$2 [R=301,NE,L] - -# Directs all web requests through the site index file -RewriteCond %{REQUEST_URI} !^/index\.php -RewriteCond %{REQUEST_FILENAME} !-f -RewriteCond %{REQUEST_FILENAME} !-d +RewriteEngine On + +# If your homepage is http://yourdomain.com/yoursite +# Set the RewriteBase to: +# RewriteBase /yoursite + +# In some environements, an empty RewriteBase is required: +# RewriteBase / + +# Protect your system files from prying eyes +RewriteRule ^(system\/author\/) - [L] +RewriteRule ^(system) - [F,L] +RewriteRule ^(content) - [F,L] +RewriteRule ^(settings) - [F,L] +RewriteRule ^(.*)?\.yml$ - [F,L] +Rewriterule ^(.*)?\.yaml$ - [F,L] +RewriteRule ^(.*)?\.txt$ - [F,L] +RewriteRule ^(.*)?\.example$ - [F,L] +RewriteRule ^(.*/)?\.git+ - [F,L] + +# Use this to redirect HTTP to HTTPS on apache servers +# RewriteCond %{HTTPS} off +# RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L] + +# Use this to redirect www to non-wwww on apache servers +# RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC] +# RewriteRule ^(.*)$ http://%1/$1 [R=301,L] + +# Use this to redirect slash/ to no slash urls on apache servers +# RewriteCond %{REQUEST_FILENAME} !-d +# RewriteRule ^(.*)/$ /$1 [R=301,L] + +# Removes index.php +RewriteCond %{THE_REQUEST} ^GET.*index\.php [NC] +RewriteRule (.*?)index\.php/*(.*) /$1$2 [R=301,NE,L] + +# Directs all web requests through the site index file +RewriteCond %{REQUEST_URI} !^/index\.php +RewriteCond %{REQUEST_FILENAME} !-f +RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^ index.php [QSA,L] \ No newline at end of file diff --git a/cache/lastCache.txt b/cache/lastCache.txt index c8848f5..66d6729 100644 --- a/cache/lastCache.txt +++ b/cache/lastCache.txt @@ -1 +1 @@ -1559707094 \ No newline at end of file +1571553470 \ No newline at end of file diff --git a/composer.json b/composer.json index 1e9b594..39bd758 100644 --- a/composer.json +++ b/composer.json @@ -1,32 +1,32 @@ -{ - "name": "trendschau/typemill", - "type": "project", - "description": "A crazy simple tool to create web-documentations and online manuals with markdown files.", - "keywords": ["documentations","manuals","flat-file","Markdown","php"], - "homepage": "http://typemill.net", - "license": "MIT", - "config": { - "vendor-dir": "system/vendor" - }, - "require": { - "slim/slim": "~3.7", - "twig/twig": "~1.18", - "slim/twig-view": "~2.3", - "slim/flash": "~0.4", - "Slim/csrf": "~0.8", - "symfony/yaml": "~2.8", - "symfony/event-dispatcher": "~3.3", - "erusev/parsedown": "~1.4", - "erusev/parsedown-extra": "dev-master", - "jbroadway/urlify": "~1.1", - "vlucas/valitron": "dev-master" - }, - "autoload": { - "psr-4": { - "Typemill\\": "system/", - "Plugins\\": "plugins/" - } - }, - "minimum-stability": "dev", - "prefer-stable": true +{ + "name": "trendschau/typemill", + "type": "project", + "description": "A crazy simple tool to create web-documentations and online manuals with markdown files.", + "keywords": ["documentations","manuals","flat-file","Markdown","php"], + "homepage": "http://typemill.net", + "license": "MIT", + "config": { + "vendor-dir": "system/vendor" + }, + "require": { + "slim/slim": "~3.7", + "twig/twig": "~1.18", + "slim/twig-view": "~2.3", + "slim/flash": "~0.4", + "Slim/csrf": "~0.8", + "symfony/yaml": "~2.8", + "symfony/event-dispatcher": "~3.3", + "erusev/parsedown": "~1.4", + "erusev/parsedown-extra": "dev-master", + "jbroadway/urlify": "~1.1", + "vlucas/valitron": "dev-master" + }, + "autoload": { + "psr-4": { + "Typemill\\": "system/", + "Plugins\\": "plugins/" + } + }, + "minimum-stability": "dev", + "prefer-stable": true } \ No newline at end of file diff --git a/content/00-Welcome/03-Markdown-Test.md b/content/00-Welcome/03-Markdown-Test.md index a4b0883..6b1912d 100644 --- a/content/00-Welcome/03-Markdown-Test.md +++ b/content/00-Welcome/03-Markdown-Test.md @@ -1,306 +1,306 @@ -# Markdown Reference and Test Page - -Markdown is a simple and universal syntax for text formatting. More and more writers switch to markdown, because they can format their text during the writing process without using any format-buttons. Once they are familiar with the markdown syntax, they can write formatted text much easier and faster than with any standard HTML-editor. - -Developers love markdown, because it is much cleaner and saver than HTML. And they can easily convert markdown to a lot of other document formats like HTML and others. - -If you develop a theme for TYPEMILL, please take care that all elements on this page are designed properly. - -## Table of Contents - -To create a table of contents, simply write `[TOC]` in a separate line. It will be replaced with a table of contents like this automatically. - -[TOC] - -## Headlines - -``` -Headlines are simply done with hash chars like this: -# First Level Headline -## Second Level Headline -### Third Level Headline -#### Fourth Level Headline -##### Fifth Level Headline -###### Sixth Level Headline -``` - -### Third Level Headline {.myclass} - -A third headline is more decent and lower prioritized than a second level headline. - -#### Fourth Level Headline - -A fourth level headline is more decent and lower prioritized than a third level headline. - -##### Fifth Level Headline - -A fifth level headline is more decent and lower prioritized than a fourth level headline. - -##### Sixth Level Headline - -A sixth level headline is more decent and lower prioritized than a fifths level headline. - -##Paragraph - -```` -A paragraph is a simple text-block separated with a new line above and below. -```` - -A paragraph is a simple text-block separated with a new line above and below. - -## Soft Linebreak - -```` -For a soft linebreak (eg. for dialoges in literature), add two spaces at the end of a line and use a simple return. -She said: "Hello" -He said: "again" -```` - -For a soft linebreak (eg. for dialoges in literature), add two spaces at the end of a line and use a simple return. - -She said: "Hello" -He said: "again" - -##Emphasis - -```` -For italic text use one *asterix* or one _underscore_. -For bold text use two **asterix** or two __underscores__. -```` - -For italic text use one *asterix* or one _underscore_. - -For bold text use two **asterix** or two __underscores__. - -##Lists - -```` -For an unordered list use a dash -- like -- this -Or use one asterix -* like -* this -For an ordered list use whatever number you want and add a dot: -1. like -1. this -```` - -For an unordered list use a dash - -- like -- this - -Or use one asterix - -* like -* this - -For an ordered list use whatever number you want and add a dot: - -1. like -2. this - -## Horizontal Rule - -``` -Easily created for example with three dashes like this: ---- -``` - -Easily created for example with three dashes like this: - ---- - -##Links - -```` -This is an ordinary [Link](http://typemill.net). -Links can also be [relative](/info). -You can also add a [title](http://typemill.net "typemill"). -You can even add [ids or classes](http://typemill.net){#myid .myclass}. -Or you can use a shortcut like http://typemill.net. -```` - -This is an ordinary [Link](http://typemill.net). - -Links can also be [relative](/info). - -You can also add a [title](http://typemill.net "typemill"). - -You can even add [ids or classes](http://typemill.net){#myid .myclass}. - -Or you can use a shortcut like http://typemill.net. - -##Images - -```` -The same rules as with links, but with a ! -![alt-text](media/markdown.png) -![alt-text](media/markdown.png "my title"){#myid .imgClass} -![alt-text](media/markdown.png "my title"){#myid .otherclass width=150px} -```` - -The same rules as with links, but with a ! - -![alt-text](media/markdown.png) - -![alt-text](media/markdown.png "my title"){#myid .imgClass} - -![alt-text](media/markdown.png "my title"){#myid .otherclass width=150px} - -## Linked Images - -```` -You can link an image with a nested syntax like this: -[![alt-text](media/markdown.png)](https://typemill.net) -```` - -You can link an image with a nested syntax like this: - -[![alt-text](media/markdown.png){.imgClass}](https://typemill.net) - -## Image Position - -```` -You can controll the image position with the classes .left, .right and .middle like this: -![alt-text](media/markdown.png){.left} -![alt-text](media/markdown.png){.right} -![alt-text](media/markdown.png){.middle} -```` - -![image float left](media/markdown.png){.left} - -The first image should float on the left side of this paragraph. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "middle". You can add these classes manually in the raw mode or you can assign them in the visual mode when you edit a picture (double click on it to open the dialog.) - -![image float right](media/markdown.png){.right} - -The second image should float on the right side of this paragraph. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "middle". You can add these classes manually in the raw mode or you can assign them in the visual mode when you edit a picture (double click on it to open the dialog.) - -![image middle](media/markdown.png){.middle} - -The thirds image should be placed above this paragraph and centered to the middle of the content area. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "middle". - -## Blockquote - -``` -There are always some women and men with wise words -> But I usually don't read them, to be honest. -``` - -There always some women and men with wise words - -> But I usually don't read them, to be honest. - -##Footnotes - -```` -You can write footnotes[^1] with markdown. -Scroll down to the end of the page[^2] and look for the footnotes. -Add the footnote text at the bottom of the page like this: -[^1]: Thank you for scrolling. -[^2]: This is the end of the page. -```` - -You can write footnotes[^1] with markdown. - -Scroll down to the end of the page[^2] and look for the footnotes. - -Footnotes won't work with the visual editor right now, so please use the raw mode for them. - -## Abbreviations - -```` -*[HTML]: Hyper Text Markup Language -*[W3C]: World Wide Web Consortium -```` - -You won't see the abbreviation directly, but if you write HTML or W3C somewhere, then you can see the tooltip with the explanation. - -*[HTML]: Hyper Text Markup Language - -*[W3C]: World Wide Web Consortium - -## Definition List - -```` -Apple -: Pomaceous fruit of plants of the genus Malus in the family Rosaceae. -Orange -: The fruit of an evergreen tree of the genus Citrus. -```` - -Apple -: Pomaceous fruit of plants of the genus Malus in -the family Rosaceae. - -Orange -: The fruit of an evergreen tree of the genus Citrus. - - - -## Tables - -```` -|name |usage | -|-----------|-----------| -| My Name | For Me | -| Your Name | For You | -```` - -| Name | Usage | -| --------- | ------- | -| My Name | For Me | -| Your Name | For You | - -## Code - -```` -Let us create some `` like this -```` - -Let us create some `` and now let us check, if a codeblock works: - -```` -Use four apostroph like this: -\```` - -\```` -```` - -## Math - -Please activate the math-plugin to use mathematical expressions with LaTeX syntax. You can choose between MathJax or the newer KaTeX library. MathJax is included from a CDN, KaTeX is included in the plugin. So if you don't want to fetch code from a CDN, use KaTeX instead. The markdown syntax in TYPEMILL is the same for both libraries. - -```` -Write inline math with \(...\) or $...$ syntax. -inline $x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)$ math -inline \(x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)\) math -```` - -inline $x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)$ math - -inline \(x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)\) math - -```` -Write display math with $$...$$ or \[...\] syntax. -$$ -x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a) -$$ -\[ -x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a) -\] -```` - -$$ -x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a) -$$ - -Das war es dann aber auch. - -[^1]: Thank you for scrolling. -[^2]: This is the end of the page. - +# Markdown Reference and Test Page + +Markdown is a simple and universal syntax for text formatting. More and more writers switch to markdown, because they can format their text during the writing process without using any format-buttons. Once they are familiar with the markdown syntax, they can write formatted text much easier and faster than with any standard HTML-editor. + +Developers love markdown, because it is much cleaner and saver than HTML. And they can easily convert markdown to a lot of other document formats like HTML and others. + +If you develop a theme for TYPEMILL, please take care that all elements on this page are designed properly. + +## Table of Contents + +To create a table of contents, simply write `[TOC]` in a separate line. It will be replaced with a table of contents like this automatically. + +[TOC] + +## Headlines + +``` +Headlines are simply done with hash chars like this: +# First Level Headline +## Second Level Headline +### Third Level Headline +#### Fourth Level Headline +##### Fifth Level Headline +###### Sixth Level Headline +``` + +### Third Level Headline {.myclass} + +A third headline is more decent and lower prioritized than a second level headline. + +#### Fourth Level Headline + +A fourth level headline is more decent and lower prioritized than a third level headline. + +##### Fifth Level Headline + +A fifth level headline is more decent and lower prioritized than a fourth level headline. + +##### Sixth Level Headline + +A sixth level headline is more decent and lower prioritized than a fifths level headline. + +##Paragraph + +```` +A paragraph is a simple text-block separated with a new line above and below. +```` + +A paragraph is a simple text-block separated with a new line above and below. + +## Soft Linebreak + +```` +For a soft linebreak (eg. for dialoges in literature), add two spaces at the end of a line and use a simple return. +She said: "Hello" +He said: "again" +```` + +For a soft linebreak (eg. for dialoges in literature), add two spaces at the end of a line and use a simple return. + +She said: "Hello" +He said: "again" + +##Emphasis + +```` +For italic text use one *asterix* or one _underscore_. +For bold text use two **asterix** or two __underscores__. +```` + +For italic text use one *asterix* or one _underscore_. + +For bold text use two **asterix** or two __underscores__. + +##Lists + +```` +For an unordered list use a dash +- like +- this +Or use one asterix +* like +* this +For an ordered list use whatever number you want and add a dot: +1. like +1. this +```` + +For an unordered list use a dash + +- like +- this + +Or use one asterix + +* like +* this + +For an ordered list use whatever number you want and add a dot: + +1. like +2. this + +## Horizontal Rule + +``` +Easily created for example with three dashes like this: +--- +``` + +Easily created for example with three dashes like this: + +--- + +##Links + +```` +This is an ordinary [Link](http://typemill.net). +Links can also be [relative](/info). +You can also add a [title](http://typemill.net "typemill"). +You can even add [ids or classes](http://typemill.net){#myid .myclass}. +Or you can use a shortcut like http://typemill.net. +```` + +This is an ordinary [Link](http://typemill.net). + +Links can also be [relative](/info). + +You can also add a [title](http://typemill.net "typemill"). + +You can even add [ids or classes](http://typemill.net){#myid .myclass}. + +Or you can use a shortcut like http://typemill.net. + +##Images + +```` +The same rules as with links, but with a ! +![alt-text](media/markdown.png) +![alt-text](media/markdown.png "my title"){#myid .imgClass} +![alt-text](media/markdown.png "my title"){#myid .otherclass width=150px} +```` + +The same rules as with links, but with a ! + +![alt-text](media/markdown.png) + +![alt-text](media/markdown.png "my title"){#myid .imgClass} + +![alt-text](media/markdown.png "my title"){#myid .otherclass width=150px} + +## Linked Images + +```` +You can link an image with a nested syntax like this: +[![alt-text](media/markdown.png)](https://typemill.net) +```` + +You can link an image with a nested syntax like this: + +[![alt-text](media/markdown.png){.imgClass}](https://typemill.net) + +## Image Position + +```` +You can controll the image position with the classes .left, .right and .middle like this: +![alt-text](media/markdown.png){.left} +![alt-text](media/markdown.png){.right} +![alt-text](media/markdown.png){.middle} +```` + +![image float left](media/markdown.png){.left} + +The first image should float on the left side of this paragraph. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "middle". You can add these classes manually in the raw mode or you can assign them in the visual mode when you edit a picture (double click on it to open the dialog.) + +![image float right](media/markdown.png){.right} + +The second image should float on the right side of this paragraph. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "middle". You can add these classes manually in the raw mode or you can assign them in the visual mode when you edit a picture (double click on it to open the dialog.) + +![image middle](media/markdown.png){.middle} + +The thirds image should be placed above this paragraph and centered to the middle of the content area. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes "left", "right" and "middle". + +## Blockquote + +``` +There are always some women and men with wise words +> But I usually don't read them, to be honest. +``` + +There always some women and men with wise words + +> But I usually don't read them, to be honest. + +##Footnotes + +```` +You can write footnotes[^1] with markdown. +Scroll down to the end of the page[^2] and look for the footnotes. +Add the footnote text at the bottom of the page like this: +[^1]: Thank you for scrolling. +[^2]: This is the end of the page. +```` + +You can write footnotes[^1] with markdown. + +Scroll down to the end of the page[^2] and look for the footnotes. + +Footnotes won't work with the visual editor right now, so please use the raw mode for them. + +## Abbreviations + +```` +*[HTML]: Hyper Text Markup Language +*[W3C]: World Wide Web Consortium +```` + +You won't see the abbreviation directly, but if you write HTML or W3C somewhere, then you can see the tooltip with the explanation. + +*[HTML]: Hyper Text Markup Language + +*[W3C]: World Wide Web Consortium + +## Definition List + +```` +Apple +: Pomaceous fruit of plants of the genus Malus in the family Rosaceae. +Orange +: The fruit of an evergreen tree of the genus Citrus. +```` + +Apple +: Pomaceous fruit of plants of the genus Malus in +the family Rosaceae. + +Orange +: The fruit of an evergreen tree of the genus Citrus. + + + +## Tables + +```` +|name |usage | +|-----------|-----------| +| My Name | For Me | +| Your Name | For You | +```` + +| Name | Usage | +| --------- | ------- | +| My Name | For Me | +| Your Name | For You | + +## Code + +```` +Let us create some `` like this +```` + +Let us create some `` and now let us check, if a codeblock works: + +```` +Use four apostroph like this: +\```` + +\```` +```` + +## Math + +Please activate the math-plugin to use mathematical expressions with LaTeX syntax. You can choose between MathJax or the newer KaTeX library. MathJax is included from a CDN, KaTeX is included in the plugin. So if you don't want to fetch code from a CDN, use KaTeX instead. The markdown syntax in TYPEMILL is the same for both libraries. + +```` +Write inline math with \(...\) or $...$ syntax. +inline $x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)$ math +inline \(x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)\) math +```` + +inline $x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)$ math + +inline \(x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)\) math + +```` +Write display math with $$...$$ or \[...\] syntax. +$$ +x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a) +$$ +\[ +x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a) +\] +```` + +$$ +x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a) +$$ + +Das war es dann aber auch. + +[^1]: Thank you for scrolling. +[^2]: This is the end of the page. + diff --git a/content/00-Welcome/03-Markdown-Test.txt b/content/00-Welcome/03-Markdown-Test.txt new file mode 100644 index 0000000..3719411 --- /dev/null +++ b/content/00-Welcome/03-Markdown-Test.txt @@ -0,0 +1 @@ +["# Markdown Reference and Test Page","Markdown is a simple and universal syntax for text formatting. More and more writers switch to markdown, because they can format their text during the writing process without using any format-buttons. Once they are familiar with the markdown syntax, they can write formatted text much easier and faster than with any standard HTML-editor.","Developers love markdown, because it is much cleaner and saver than HTML. And they can easily convert markdown to a lot of other document formats like HTML and others.","If you develop a theme for TYPEMILL, please take care that all elements on this page are designed properly.","## Table of Contents","To create a table of contents, simply write `[TOC]` in a separate line. It will be replaced with a table of contents like this automatically.","[TOC]","## Headlines","```\nHeadlines are simply done with hash chars like this:\n# First Level Headline\n## Second Level Headline\n### Third Level Headline\n#### Fourth Level Headline\n##### Fifth Level Headline\n###### Sixth Level Headline\n```","### Third Level Headline {.myclass}","A third headline is more decent and lower prioritized than a second level headline.","#### Fourth Level Headline","A fourth level headline is more decent and lower prioritized than a third level headline.","##### Fifth Level Headline","A fifth level headline is more decent and lower prioritized than a fourth level headline.","##### Sixth Level Headline","A sixth level headline is more decent and lower prioritized than a fifths level headline.","##Paragraph","````\nA paragraph is a simple text-block separated with a new line above and below.\n````","A paragraph is a simple text-block separated with a new line above and below.","## Soft Linebreak","````\nFor a soft linebreak (eg. for dialoges in literature), add two spaces at the end of a line and use a simple return.\nShe said: \"Hello\" \nHe said: \"again\"\n````","For a soft linebreak (eg. for dialoges in literature), add two spaces at the end of a line and use a simple return.","She said: \"Hello\" \nHe said: \"again\"","##Emphasis","````\nFor italic text use one *asterix* or one _underscore_.\nFor bold text use two **asterix** or two __underscores__.\n````","For italic text use one *asterix* or one _underscore_.","For bold text use two **asterix** or two __underscores__.","##Lists","````\nFor an unordered list use a dash\n- like \n- this\nOr use one asterix\n* like\n* this\nFor an ordered list use whatever number you want and add a dot:\n1. like\n1. this\n````","For an unordered list use a dash","- like \n- this","Or use one asterix","* like\n* this","For an ordered list use whatever number you want and add a dot:","1. like\n2. this","## Horizontal Rule","```\nEasily created for example with three dashes like this:\n---\n```","Easily created for example with three dashes like this:","---","##Links","````\nThis is an ordinary [Link](http:\/\/typemill.net).\nLinks can also be [relative](\/info).\nYou can also add a [title](http:\/\/typemill.net \"typemill\").\nYou can even add [ids or classes](http:\/\/typemill.net){#myid .myclass}.\nOr you can use a shortcut like http:\/\/typemill.net.\n````","This is an ordinary [Link](http:\/\/typemill.net).","Links can also be [relative](\/info).","You can also add a [title](http:\/\/typemill.net \"typemill\").","You can even add [ids or classes](http:\/\/typemill.net){#myid .myclass}.","Or you can use a shortcut like http:\/\/typemill.net.","##Images","````\nThe same rules as with links, but with a !\n![alt-text](media\/markdown.png)\n![alt-text](media\/markdown.png \"my title\"){#myid .imgClass}\n![alt-text](media\/markdown.png \"my title\"){#myid .otherclass width=150px}\n````","The same rules as with links, but with a !","![alt-text](media\/markdown.png)","![alt-text](media\/markdown.png \"my title\"){#myid .imgClass}","![alt-text](media\/markdown.png \"my title\"){#myid .otherclass width=150px}","## Linked Images","````\nYou can link an image with a nested syntax like this:\n[![alt-text](media\/markdown.png)](https:\/\/typemill.net)\n````","You can link an image with a nested syntax like this:","[![alt-text](media\/markdown.png){.imgClass}](https:\/\/typemill.net)","## Image Position","````\nYou can controll the image position with the classes .left, .right and .middle like this:\n![alt-text](media\/markdown.png){.left}\n![alt-text](media\/markdown.png){.right}\n![alt-text](media\/markdown.png){.middle}\n````","![image float left](media\/markdown.png){.left}","The first image should float on the left side of this paragraph. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes \"left\", \"right\" and \"middle\". You can add these classes manually in the raw mode or you can assign them in the visual mode when you edit a picture (double click on it to open the dialog.)","![image float right](media\/markdown.png){.right}","The second image should float on the right side of this paragraph. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes \"left\", \"right\" and \"middle\". You can add these classes manually in the raw mode or you can assign them in the visual mode when you edit a picture (double click on it to open the dialog.)","![image middle](media\/markdown.png){.middle}","The thirds image should be placed above this paragraph and centered to the middle of the content area. This might not work with all themes. If you are a theme developer, please ensure that you support the image classes \"left\", \"right\" and \"middle\".","## Blockquote","```\nThere are always some women and men with wise words\n> But I usually don't read them, to be honest.\n```","There always some women and men with wise words","> But I usually don't read them, to be honest.","##Footnotes","````\nYou can write footnotes[^1] with markdown. \nScroll down to the end of the page[^2] and look for the footnotes.\nAdd the footnote text at the bottom of the page like this:\n[^1]: Thank you for scrolling.\n[^2]: This is the end of the page.\n````","You can write footnotes[^1] with markdown. ","Scroll down to the end of the page[^2] and look for the footnotes. ","Footnotes won't work with the visual editor right now, so please use the raw mode for them.","## Abbreviations","````\n*[HTML]: Hyper Text Markup Language\n*[W3C]: World Wide Web Consortium\n````","You won't see the abbreviation directly, but if you write HTML or W3C somewhere, then you can see the tooltip with the explanation.","*[HTML]: Hyper Text Markup Language","*[W3C]: World Wide Web Consortium","## Definition List","````\nApple\n: Pomaceous fruit of plants of the genus Malus in the family Rosaceae.\nOrange\n: The fruit of an evergreen tree of the genus Citrus.\n````","Apple\n: Pomaceous fruit of plants of the genus Malus in \nthe family Rosaceae.\n\nOrange\n: The fruit of an evergreen tree of the genus Citrus.\n\n","## Tables","````\n|name |usage |\n|-----------|-----------|\n| My Name | For Me |\n| Your Name | For You |\n````","| Name | Usage |\n| --------- | ------- |\n| My Name | For Me |\n| Your Name | For You |","## Code","````\nLet us create some `` like this\n````","Let us create some `` and now let us check, if a codeblock works:","````\nUse four apostroph like this: \n\\````\n \n\\````\n````","## Math","Please activate the math-plugin to use mathematical expressions with LaTeX syntax. You can choose between MathJax or the newer KaTeX library. MathJax is included from a CDN, KaTeX is included in the plugin. So if you don't want to fetch code from a CDN, use KaTeX instead. The markdown syntax in TYPEMILL is the same for both libraries.","````\nWrite inline math with \\(...\\) or $...$ syntax.\ninline $x = \\int_{0^1}^1(-b \\pm \\sqrt{b^2-4ac})\/(2a)$ math\ninline \\(x = \\int_{0^1}^1(-b \\pm \\sqrt{b^2-4ac})\/(2a)\\) math\n````","inline $x = \\int_{0^1}^1(-b \\pm \\sqrt{b^2-4ac})\/(2a)$ math","inline \\(x = \\int_{0^1}^1(-b \\pm \\sqrt{b^2-4ac})\/(2a)\\) math","````\nWrite display math with $$...$$ or \\[...\\] syntax. \n$$\nx = \\int_{0^1}^1(-b \\pm \\sqrt{b^2-4ac})\/(2a)\n$$\n\\[\nx = \\int_{0^1}^1(-b \\pm \\sqrt{b^2-4ac})\/(2a)\n\\]\n````","$$\nx = \\int_{0^1}^1(-b \\pm \\sqrt{b^2-4ac})\/(2a)\n$$","Das war es dann aber auch.","[^1]: Thank you for scrolling.\n[^2]: This is the end of the page."] \ No newline at end of file diff --git a/content/00-Welcome/index.md b/content/00-Welcome/index.md index dcbb59a..d855f77 100644 --- a/content/00-Welcome/index.md +++ b/content/00-Welcome/index.md @@ -1,4 +1,3 @@ -# Welcome to Typemill - -Great that you give Typemill a try!! Typemill is a small open source cms and a project in work. You will probably miss some important features, but I am working hard to add everything that is needed for a handy and productive writing-system. - +# Welcome to Typemill + +Great that you give Typemill a try!! Typemill is a small open source cms and a project in work. You will probably miss some important features, but I am working hard to add everything that is needed for a handy and productive writing-system. \ No newline at end of file diff --git a/content/index.txt b/content/index.txt new file mode 100644 index 0000000..550066d --- /dev/null +++ b/content/index.txt @@ -0,0 +1 @@ +["# Typemill","*Typemill is a user-friendly and lightweight open source CMS for publishing text-works like prose, lyrics, manuals, documentations, studies and more. Just download and start.* bla"] \ No newline at end of file diff --git a/index.php b/index.php index b35a424..2fe58d1 100644 --- a/index.php +++ b/index.php @@ -1,7 +1,7 @@ -run(); \ No newline at end of file diff --git a/licence.md b/licence.md index 1ffd235..aa84d80 100644 --- a/licence.md +++ b/licence.md @@ -1,7 +1,7 @@ -Copyright (c) 2017 Sebastian Schrmanns - -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. - +Copyright (c) 2017 Sebastian Schrmanns + +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. \ No newline at end of file diff --git a/plugins/analytics/analytics.php b/plugins/analytics/analytics.php index 8228196..d6c785f 100644 --- a/plugins/analytics/analytics.php +++ b/plugins/analytics/analytics.php @@ -1,47 +1,47 @@ - '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)); - } - } - } + '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)); + } + } + } } \ No newline at end of file diff --git a/plugins/analytics/analytics.yaml b/plugins/analytics/analytics.yaml index e00586c..9dceb43 100644 --- a/plugins/analytics/analytics.yaml +++ b/plugins/analytics/analytics.yaml @@ -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' \ No newline at end of file diff --git a/plugins/analytics/templates/googleanalytics.twig b/plugins/analytics/templates/googleanalytics.twig index 3c66349..bb794cd 100644 --- a/plugins/analytics/templates/googleanalytics.twig +++ b/plugins/analytics/templates/googleanalytics.twig @@ -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 }}'); \ No newline at end of file diff --git a/plugins/analytics/templates/piwikanalytics.twig b/plugins/analytics/templates/piwikanalytics.twig index 1b33351..a427bf1 100644 --- a/plugins/analytics/templates/piwikanalytics.twig +++ b/plugins/analytics/templates/piwikanalytics.twig @@ -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); })(); \ No newline at end of file diff --git a/plugins/cookieconsent/cookieconsent.php b/plugins/cookieconsent/cookieconsent.php index 2ea9637..f451bd2 100644 --- a/plugins/cookieconsent/cookieconsent.php +++ b/plugins/cookieconsent/cookieconsent.php @@ -1,38 +1,38 @@ - '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)); - } + '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)); + } } \ No newline at end of file diff --git a/plugins/cookieconsent/cookieconsent.yaml b/plugins/cookieconsent/cookieconsent.yaml index 75ca845..f214e8b 100644 --- a/plugins/cookieconsent/cookieconsent.yaml +++ b/plugins/cookieconsent/cookieconsent.yaml @@ -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 \ No newline at end of file diff --git a/plugins/cookieconsent/public/cookieconsent.min.css b/plugins/cookieconsent/public/cookieconsent.min.css index 820b31a..c62bb41 100644 --- a/plugins/cookieconsent/public/cookieconsent.min.css +++ b/plugins/cookieconsent/public/cookieconsent.min.css @@ -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} \ No newline at end of file diff --git a/plugins/cookieconsent/templates/cookieconsent.twig b/plugins/cookieconsent/templates/cookieconsent.twig index 50f790e..1ab1c07 100644 --- a/plugins/cookieconsent/templates/cookieconsent.twig +++ b/plugins/cookieconsent/templates/cookieconsent.twig @@ -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 }}" + } + }) +}); diff --git a/plugins/highlight/highlight.php b/plugins/highlight/highlight.php index b8a51f2..ab06725 100644 --- a/plugins/highlight/highlight.php +++ b/plugins/highlight/highlight.php @@ -1,28 +1,28 @@ - '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();'); - } + '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();'); + } } \ No newline at end of file diff --git a/plugins/highlight/highlight.yaml b/plugins/highlight/highlight.yaml index be0185b..917f5de 100644 --- a/plugins/highlight/highlight.yaml +++ b/plugins/highlight/highlight.yaml @@ -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 \ No newline at end of file diff --git a/plugins/highlight/public/LICENSE b/plugins/highlight/public/LICENSE index 422deb7..19b69e1 100644 --- a/plugins/highlight/public/LICENSE +++ b/plugins/highlight/public/LICENSE @@ -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. diff --git a/plugins/highlight/public/default.css b/plugins/highlight/public/default.css index f1bfade..c0ca742 100644 --- a/plugins/highlight/public/default.css +++ b/plugins/highlight/public/default.css @@ -1,99 +1,99 @@ -/* - -Original highlight.js style (c) Ivan Sagalaev - -*/ - -.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 + +*/ + +.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; +} diff --git a/plugins/highlight/public/highlight.pack.js b/plugins/highlight/public/highlight.pack.js index c4507df..519710a 100644 --- a/plugins/highlight/public/highlight.pack.js +++ b/plugins/highlight/public/highlight.pack.js @@ -1,2 +1,2 @@ -/*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */ +/*! highlight.js v9.12.0 | BSD3 License | git.io/hljslicense */ !function(e){var n="object"==typeof window&&window||"object"==typeof self&&self;"undefined"!=typeof exports?e(exports):n&&(n.hljs=e({}),"function"==typeof define&&define.amd&&define([],function(){return n.hljs}))}(function(e){function n(e){return e.replace(/&/g,"&").replace(//g,">")}function t(e){return e.nodeName.toLowerCase()}function r(e,n){var t=e&&e.exec(n);return t&&0===t.index}function a(e){return k.test(e)}function i(e){var n,t,r,i,o=e.className+" ";if(o+=e.parentNode?e.parentNode.className:"",t=B.exec(o))return w(t[1])?t[1]:"no-highlight";for(o=o.split(/\s+/),n=0,r=o.length;r>n;n++)if(i=o[n],a(i)||w(i))return i}function o(e){var n,t={},r=Array.prototype.slice.call(arguments,1);for(n in e)t[n]=e[n];return r.forEach(function(e){for(n in e)t[n]=e[n]}),t}function u(e){var n=[];return function r(e,a){for(var i=e.firstChild;i;i=i.nextSibling)3===i.nodeType?a+=i.nodeValue.length:1===i.nodeType&&(n.push({event:"start",offset:a,node:i}),a=r(i,a),t(i).match(/br|hr|img|input/)||n.push({event:"stop",offset:a,node:i}));return a}(e,0),n}function c(e,r,a){function i(){return e.length&&r.length?e[0].offset!==r[0].offset?e[0].offset"}function u(e){s+=""}function c(e){("start"===e.event?o:u)(e.node)}for(var l=0,s="",f=[];e.length||r.length;){var g=i();if(s+=n(a.substring(l,g[0].offset)),l=g[0].offset,g===e){f.reverse().forEach(u);do c(g.splice(0,1)[0]),g=i();while(g===e&&g.length&&g[0].offset===l);f.reverse().forEach(o)}else"start"===g[0].event?f.push(g[0].node):f.pop(),c(g.splice(0,1)[0])}return s+n(a.substr(l))}function l(e){return e.v&&!e.cached_variants&&(e.cached_variants=e.v.map(function(n){return o(e,{v:null},n)})),e.cached_variants||e.eW&&[o(e)]||[e]}function s(e){function n(e){return e&&e.source||e}function t(t,r){return new RegExp(n(t),"m"+(e.cI?"i":"")+(r?"g":""))}function r(a,i){if(!a.compiled){if(a.compiled=!0,a.k=a.k||a.bK,a.k){var o={},u=function(n,t){e.cI&&(t=t.toLowerCase()),t.split(" ").forEach(function(e){var t=e.split("|");o[t[0]]=[n,t[1]?Number(t[1]):1]})};"string"==typeof a.k?u("keyword",a.k):x(a.k).forEach(function(e){u(e,a.k[e])}),a.k=o}a.lR=t(a.l||/\w+/,!0),i&&(a.bK&&(a.b="\\b("+a.bK.split(" ").join("|")+")\\b"),a.b||(a.b=/\B|\b/),a.bR=t(a.b),a.e||a.eW||(a.e=/\B|\b/),a.e&&(a.eR=t(a.e)),a.tE=n(a.e)||"",a.eW&&i.tE&&(a.tE+=(a.e?"|":"")+i.tE)),a.i&&(a.iR=t(a.i)),null==a.r&&(a.r=1),a.c||(a.c=[]),a.c=Array.prototype.concat.apply([],a.c.map(function(e){return l("self"===e?a:e)})),a.c.forEach(function(e){r(e,a)}),a.starts&&r(a.starts,i);var c=a.c.map(function(e){return e.bK?"\\.?("+e.b+")\\.?":e.b}).concat([a.tE,a.i]).map(n).filter(Boolean);a.t=c.length?t(c.join("|"),!0):{exec:function(){return null}}}}r(e)}function f(e,t,a,i){function o(e,n){var t,a;for(t=0,a=n.c.length;a>t;t++)if(r(n.c[t].bR,e))return n.c[t]}function u(e,n){if(r(e.eR,n)){for(;e.endsParent&&e.parent;)e=e.parent;return e}return e.eW?u(e.parent,n):void 0}function c(e,n){return!a&&r(n.iR,e)}function l(e,n){var t=N.cI?n[0].toLowerCase():n[0];return e.k.hasOwnProperty(t)&&e.k[t]}function p(e,n,t,r){var a=r?"":I.classPrefix,i='',i+n+o}function h(){var e,t,r,a;if(!E.k)return n(k);for(a="",t=0,E.lR.lastIndex=0,r=E.lR.exec(k);r;)a+=n(k.substring(t,r.index)),e=l(E,r),e?(B+=e[1],a+=p(e[0],n(r[0]))):a+=n(r[0]),t=E.lR.lastIndex,r=E.lR.exec(k);return a+n(k.substr(t))}function d(){var e="string"==typeof E.sL;if(e&&!y[E.sL])return n(k);var t=e?f(E.sL,k,!0,x[E.sL]):g(k,E.sL.length?E.sL:void 0);return E.r>0&&(B+=t.r),e&&(x[E.sL]=t.top),p(t.language,t.value,!1,!0)}function b(){L+=null!=E.sL?d():h(),k=""}function v(e){L+=e.cN?p(e.cN,"",!0):"",E=Object.create(e,{parent:{value:E}})}function m(e,n){if(k+=e,null==n)return b(),0;var t=o(n,E);if(t)return t.skip?k+=n:(t.eB&&(k+=n),b(),t.rB||t.eB||(k=n)),v(t,n),t.rB?0:n.length;var r=u(E,n);if(r){var a=E;a.skip?k+=n:(a.rE||a.eE||(k+=n),b(),a.eE&&(k=n));do E.cN&&(L+=C),E.skip||(B+=E.r),E=E.parent;while(E!==r.parent);return r.starts&&v(r.starts,""),a.rE?0:n.length}if(c(n,E))throw new Error('Illegal lexeme "'+n+'" for mode "'+(E.cN||"")+'"');return k+=n,n.length||1}var N=w(e);if(!N)throw new Error('Unknown language: "'+e+'"');s(N);var R,E=i||N,x={},L="";for(R=E;R!==N;R=R.parent)R.cN&&(L=p(R.cN,"",!0)+L);var k="",B=0;try{for(var M,j,O=0;;){if(E.t.lastIndex=O,M=E.t.exec(t),!M)break;j=m(t.substring(O,M.index),M[0]),O=M.index+j}for(m(t.substr(O)),R=E;R.parent;R=R.parent)R.cN&&(L+=C);return{r:B,value:L,language:e,top:E}}catch(T){if(T.message&&-1!==T.message.indexOf("Illegal"))return{r:0,value:n(t)};throw T}}function g(e,t){t=t||I.languages||x(y);var r={r:0,value:n(e)},a=r;return t.filter(w).forEach(function(n){var t=f(n,e,!1);t.language=n,t.r>a.r&&(a=t),t.r>r.r&&(a=r,r=t)}),a.language&&(r.second_best=a),r}function p(e){return I.tabReplace||I.useBR?e.replace(M,function(e,n){return I.useBR&&"\n"===e?"
":I.tabReplace?n.replace(/\t/g,I.tabReplace):""}):e}function h(e,n,t){var r=n?L[n]:t,a=[e.trim()];return e.match(/\bhljs\b/)||a.push("hljs"),-1===e.indexOf(r)&&a.push(r),a.join(" ").trim()}function d(e){var n,t,r,o,l,s=i(e);a(s)||(I.useBR?(n=document.createElementNS("http://www.w3.org/1999/xhtml","div"),n.innerHTML=e.innerHTML.replace(/\n/g,"").replace(//g,"\n")):n=e,l=n.textContent,r=s?f(s,l,!0):g(l),t=u(n),t.length&&(o=document.createElementNS("http://www.w3.org/1999/xhtml","div"),o.innerHTML=r.value,r.value=c(t,u(o),l)),r.value=p(r.value),e.innerHTML=r.value,e.className=h(e.className,s,r.language),e.result={language:r.language,re:r.r},r.second_best&&(e.second_best={language:r.second_best.language,re:r.second_best.r}))}function b(e){I=o(I,e)}function v(){if(!v.called){v.called=!0;var e=document.querySelectorAll("pre code");E.forEach.call(e,d)}}function m(){addEventListener("DOMContentLoaded",v,!1),addEventListener("load",v,!1)}function N(n,t){var r=y[n]=t(e);r.aliases&&r.aliases.forEach(function(e){L[e]=n})}function R(){return x(y)}function w(e){return e=(e||"").toLowerCase(),y[e]||y[L[e]]}var E=[],x=Object.keys,y={},L={},k=/^(no-?highlight|plain|text)$/i,B=/\blang(?:uage)?-([\w-]+)\b/i,M=/((^(<[^>]+>|\t|)+|(?:\n)))/gm,C="
",I={classPrefix:"hljs-",tabReplace:null,useBR:!1,languages:void 0};return e.highlight=f,e.highlightAuto=g,e.fixMarkup=p,e.highlightBlock=d,e.configure=b,e.initHighlighting=v,e.initHighlightingOnLoad=m,e.registerLanguage=N,e.listLanguages=R,e.getLanguage=w,e.inherit=o,e.IR="[a-zA-Z]\\w*",e.UIR="[a-zA-Z_]\\w*",e.NR="\\b\\d+(\\.\\d+)?",e.CNR="(-?)(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)",e.BNR="\\b(0b[01]+)",e.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|-|-=|/=|/|:|;|<<|<<=|<=|<|===|==|=|>>>=|>>=|>=|>>>|>>|>|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~",e.BE={b:"\\\\[\\s\\S]",r:0},e.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[e.BE]},e.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[e.BE]},e.PWM={b:/\b(a|an|the|are|I'm|isn't|don't|doesn't|won't|but|just|should|pretty|simply|enough|gonna|going|wtf|so|such|will|you|your|they|like|more)\b/},e.C=function(n,t,r){var a=e.inherit({cN:"comment",b:n,e:t,c:[]},r||{});return a.c.push(e.PWM),a.c.push({cN:"doctag",b:"(?:TODO|FIXME|NOTE|BUG|XXX):",r:0}),a},e.CLCM=e.C("//","$"),e.CBCM=e.C("/\\*","\\*/"),e.HCM=e.C("#","$"),e.NM={cN:"number",b:e.NR,r:0},e.CNM={cN:"number",b:e.CNR,r:0},e.BNM={cN:"number",b:e.BNR,r:0},e.CSSNM={cN:"number",b:e.NR+"(%|em|ex|ch|rem|vw|vh|vmin|vmax|cm|mm|in|pt|pc|px|deg|grad|rad|turn|s|ms|Hz|kHz|dpi|dpcm|dppx)?",r:0},e.RM={cN:"regexp",b:/\//,e:/\/[gimuy]*/,i:/\n/,c:[e.BE,{b:/\[/,e:/\]/,r:0,c:[e.BE]}]},e.TM={cN:"title",b:e.IR,r:0},e.UTM={cN:"title",b:e.UIR,r:0},e.METHOD_GUARD={b:"\\.\\s*"+e.UIR,r:0},e});hljs.registerLanguage("css",function(e){var c="[a-zA-Z-][a-zA-Z0-9_-]*",t={b:/[A-Z\_\.\-]+\s*:/,rB:!0,e:";",eW:!0,c:[{cN:"attribute",b:/\S/,e:":",eE:!0,starts:{eW:!0,eE:!0,c:[{b:/[\w-]+\(/,rB:!0,c:[{cN:"built_in",b:/[\w-]+/},{b:/\(/,e:/\)/,c:[e.ASM,e.QSM]}]},e.CSSNM,e.QSM,e.ASM,e.CBCM,{cN:"number",b:"#[0-9A-Fa-f]+"},{cN:"meta",b:"!important"}]}}]};return{cI:!0,i:/[=\/|'\$]/,c:[e.CBCM,{cN:"selector-id",b:/#[A-Za-z0-9_-]+/},{cN:"selector-class",b:/\.[A-Za-z0-9_-]+/},{cN:"selector-attr",b:/\[/,e:/\]/,i:"$"},{cN:"selector-pseudo",b:/:(:)?[a-zA-Z0-9\_\-\+\(\)"'.]+/},{b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{b:"@",e:"[{;]",i:/:/,c:[{cN:"keyword",b:/\w+/},{b:/\s/,eW:!0,eE:!0,r:0,c:[e.ASM,e.QSM,e.CSSNM]}]},{cN:"selector-tag",b:c,r:0},{b:"{",e:"}",i:/\S/,c:[e.CBCM,t]}]}});hljs.registerLanguage("nginx",function(e){var r={cN:"variable",v:[{b:/\$\d+/},{b:/\$\{/,e:/}/},{b:"[\\$\\@]"+e.UIR}]},b={eW:!0,l:"[a-z/_]+",k:{literal:"on off yes no true false none blocked debug info notice warn error crit select break last permanent redirect kqueue rtsig epoll poll /dev/poll"},r:0,i:"=>",c:[e.HCM,{cN:"string",c:[e.BE,r],v:[{b:/"/,e:/"/},{b:/'/,e:/'/}]},{b:"([a-z]+):/",e:"\\s",eW:!0,eE:!0,c:[r]},{cN:"regexp",c:[e.BE,r],v:[{b:"\\s\\^",e:"\\s|{|;",rE:!0},{b:"~\\*?\\s+",e:"\\s|{|;",rE:!0},{b:"\\*(\\.[a-z\\-]+)+"},{b:"([a-z\\-]+\\.)+\\*"}]},{cN:"number",b:"\\b\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}\\.\\d{1,3}(:\\d{1,5})?\\b"},{cN:"number",b:"\\b\\d+[kKmMgGdshdwy]*\\b",r:0},r]};return{aliases:["nginxconf"],c:[e.HCM,{b:e.UIR+"\\s+{",rB:!0,e:"{",c:[{cN:"section",b:e.UIR}],r:0},{b:e.UIR+"\\s",e:";|{",rB:!0,c:[{cN:"attribute",b:e.UIR,starts:b}],r:0}],i:"[^\\s\\}]"}});hljs.registerLanguage("ini",function(e){var b={cN:"string",c:[e.BE],v:[{b:"'''",e:"'''",r:10},{b:'"""',e:'"""',r:10},{b:'"',e:'"'},{b:"'",e:"'"}]};return{aliases:["toml"],cI:!0,i:/\S/,c:[e.C(";","$"),e.HCM,{cN:"section",b:/^\s*\[+/,e:/\]+/},{b:/^[a-z0-9\[\]_-]+\s*=\s*/,e:"$",rB:!0,c:[{cN:"attr",b:/[a-z0-9\[\]_-]+/},{b:/=/,eW:!0,r:0,c:[{cN:"literal",b:/\bon|off|true|false|yes|no\b/},{cN:"variable",v:[{b:/\$[\w\d"][\w\d_]*/},{b:/\$\{(.*?)}/}]},b,{cN:"number",b:/([\+\-]+)?[\d]+_[\d_]+/},e.NM]}]}]}});hljs.registerLanguage("ruby",function(e){var b="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?",r={keyword:"and then defined module in return redo if BEGIN retry end for self when next until do begin unless END rescue else break undef not super class case require yield alias while ensure elsif or include attr_reader attr_writer attr_accessor",literal:"true false nil"},c={cN:"doctag",b:"@[A-Za-z]+"},a={b:"#<",e:">"},s=[e.C("#","$",{c:[c]}),e.C("^\\=begin","^\\=end",{c:[c],r:10}),e.C("^__END__","\\n$")],n={cN:"subst",b:"#\\{",e:"}",k:r},t={cN:"string",c:[e.BE,n],v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/`/,e:/`/},{b:"%[qQwWx]?\\(",e:"\\)"},{b:"%[qQwWx]?\\[",e:"\\]"},{b:"%[qQwWx]?{",e:"}"},{b:"%[qQwWx]?<",e:">"},{b:"%[qQwWx]?/",e:"/"},{b:"%[qQwWx]?%",e:"%"},{b:"%[qQwWx]?-",e:"-"},{b:"%[qQwWx]?\\|",e:"\\|"},{b:/\B\?(\\\d{1,3}|\\x[A-Fa-f0-9]{1,2}|\\u[A-Fa-f0-9]{4}|\\?\S)\b/},{b:/<<(-?)\w+$/,e:/^\s*\w+$/}]},i={cN:"params",b:"\\(",e:"\\)",endsParent:!0,k:r},d=[t,a,{cN:"class",bK:"class module",e:"$|;",i:/=/,c:[e.inherit(e.TM,{b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?"}),{b:"<\\s*",c:[{b:"("+e.IR+"::)?"+e.IR}]}].concat(s)},{cN:"function",bK:"def",e:"$|;",c:[e.inherit(e.TM,{b:b}),i].concat(s)},{b:e.IR+"::"},{cN:"symbol",b:e.UIR+"(\\!|\\?)?:",r:0},{cN:"symbol",b:":(?!\\s)",c:[t,{b:b}],r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},{cN:"params",b:/\|/,e:/\|/,k:r},{b:"("+e.RSR+"|unless)\\s*",k:"unless",c:[a,{cN:"regexp",c:[e.BE,n],i:/\n/,v:[{b:"/",e:"/[a-z]*"},{b:"%r{",e:"}[a-z]*"},{b:"%r\\(",e:"\\)[a-z]*"},{b:"%r!",e:"![a-z]*"},{b:"%r\\[",e:"\\][a-z]*"}]}].concat(s),r:0}].concat(s);n.c=d,i.c=d;var l="[>?]>",o="[\\w#]+\\(\\w+\\):\\d+:\\d+>",u="(\\w+-)?\\d+\\.\\d+\\.\\d(p\\d+)?[^>]+>",w=[{b:/^\s*=>/,starts:{e:"$",c:d}},{cN:"meta",b:"^("+l+"|"+o+"|"+u+")",starts:{e:"$",c:d}}];return{aliases:["rb","gemspec","podspec","thor","irb"],k:r,i:/\/\*/,c:s.concat(w).concat(d)}});hljs.registerLanguage("xml",function(s){var e="[A-Za-z0-9\\._:-]+",t={eW:!0,i:/`]+/}]}]}]};return{aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist"],cI:!0,c:[{cN:"meta",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},s.C("",{r:10}),{b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{b:/<\?(php)?/,e:/\?>/,sL:"php",c:[{b:"/\\*",e:"\\*/",skip:!0}]},{cN:"tag",b:"|$)",e:">",k:{name:"style"},c:[t],starts:{e:"",rE:!0,sL:["css","xml"]}},{cN:"tag",b:"|$)",e:">",k:{name:"script"},c:[t],starts:{e:"",rE:!0,sL:["actionscript","javascript","handlebars","xml"]}},{cN:"meta",v:[{b:/<\?xml/,e:/\?>/,r:10},{b:/<\?\w+/,e:/\?>/}]},{cN:"tag",b:"",c:[{cN:"name",b:/[^\/><\s]+/,r:0},t]}]}});hljs.registerLanguage("markdown",function(e){return{aliases:["md","mkdown","mkd"],c:[{cN:"section",v:[{b:"^#{1,6}",e:"$"},{b:"^.+?\\n[=-]{2,}$"}]},{b:"<",e:">",sL:"xml",r:0},{cN:"bullet",b:"^([*+-]|(\\d+\\.))\\s+"},{cN:"strong",b:"[*_]{2}.+?[*_]{2}"},{cN:"emphasis",v:[{b:"\\*.+?\\*"},{b:"_.+?_",r:0}]},{cN:"quote",b:"^>\\s+",e:"$"},{cN:"code",v:[{b:"^```w*s*$",e:"^```s*$"},{b:"`.+?`"},{b:"^( {4}| )",e:"$",r:0}]},{b:"^[-\\*]{3,}",e:"$"},{b:"\\[.+?\\][\\(\\[].*?[\\)\\]]",rB:!0,c:[{cN:"string",b:"\\[",e:"\\]",eB:!0,rE:!0,r:0},{cN:"link",b:"\\]\\(",e:"\\)",eB:!0,eE:!0},{cN:"symbol",b:"\\]\\[",e:"\\]",eB:!0,eE:!0}],r:10},{b:/^\[[^\n]+\]:/,rB:!0,c:[{cN:"symbol",b:/\[/,e:/\]/,eB:!0,eE:!0},{cN:"link",b:/:\s*/,e:/$/,eB:!0}]}]}});hljs.registerLanguage("json",function(e){var i={literal:"true false null"},n=[e.QSM,e.CNM],r={e:",",eW:!0,eE:!0,c:n,k:i},t={b:"{",e:"}",c:[{cN:"attr",b:/"/,e:/"/,c:[e.BE],i:"\\n"},e.inherit(r,{b:/:/})],i:"\\S"},c={b:"\\[",e:"\\]",c:[e.inherit(r)],i:"\\S"};return n.splice(n.length,0,t,c),{c:n,k:i,i:"\\S"}});hljs.registerLanguage("java",function(e){var a="[À-ʸa-zA-Z_$][À-ʸa-zA-Z_$0-9]*",t=a+"(<"+a+"(\\s*,\\s*"+a+")*>)?",r="false synchronized int abstract float private char boolean static null if const for true while long strictfp finally protected import native final void enum else break transient catch instanceof byte super volatile case assert short package default double public try this switch continue throws protected public private module requires exports do",s="\\b(0[bB]([01]+[01_]+[01]+|[01]+)|0[xX]([a-fA-F0-9]+[a-fA-F0-9_]+[a-fA-F0-9]+|[a-fA-F0-9]+)|(([\\d]+[\\d_]+[\\d]+|[\\d]+)(\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))?|\\.([\\d]+[\\d_]+[\\d]+|[\\d]+))([eE][-+]?\\d+)?)[lLfF]?",c={cN:"number",b:s,r:0};return{aliases:["jsp"],k:r,i:/<\/|#/,c:[e.C("/\\*\\*","\\*/",{r:0,c:[{b:/\w+@/,r:0},{cN:"doctag",b:"@[A-Za-z]+"}]}),e.CLCM,e.CBCM,e.ASM,e.QSM,{cN:"class",bK:"class interface",e:/[{;=]/,eE:!0,k:"class interface",i:/[:"\[\]]/,c:[{bK:"extends implements"},e.UTM]},{bK:"new throw return else",r:0},{cN:"function",b:"("+t+"\\s+)+"+e.UIR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:r,c:[{b:e.UIR+"\\s*\\(",rB:!0,r:0,c:[e.UTM]},{cN:"params",b:/\(/,e:/\)/,k:r,r:0,c:[e.ASM,e.QSM,e.CNM,e.CBCM]},e.CLCM,e.CBCM]},c,{cN:"meta",b:"@[A-Za-z]+"}]}});hljs.registerLanguage("apache",function(e){var r={cN:"number",b:"[\\$%]\\d+"};return{aliases:["apacheconf"],cI:!0,c:[e.HCM,{cN:"section",b:""},{cN:"attribute",b:/\w+/,r:0,k:{nomarkup:"order deny allow setenv rewriterule rewriteengine rewritecond documentroot sethandler errordocument loadmodule options header listen serverroot servername"},starts:{e:/$/,r:0,k:{literal:"on off all"},c:[{cN:"meta",b:"\\s\\[",e:"\\]$"},{cN:"variable",b:"[\\$%]\\{",e:"\\}",c:["self",r]},r,e.QSM]}}],i:/\S/}});hljs.registerLanguage("cpp",function(t){var e={cN:"keyword",b:"\\b[a-z\\d_]*_t\\b"},r={cN:"string",v:[{b:'(u8?|U)?L?"',e:'"',i:"\\n",c:[t.BE]},{b:'(u8?|U)?R"',e:'"',c:[t.BE]},{b:"'\\\\?.",e:"'",i:"."}]},s={cN:"number",v:[{b:"\\b(0b[01']+)"},{b:"(-?)\\b([\\d']+(\\.[\\d']*)?|\\.[\\d']+)(u|U|l|L|ul|UL|f|F|b|B)"},{b:"(-?)(\\b0[xX][a-fA-F0-9']+|(\\b[\\d']+(\\.[\\d']*)?|\\.[\\d']+)([eE][-+]?[\\d']+)?)"}],r:0},i={cN:"meta",b:/#\s*[a-z]+\b/,e:/$/,k:{"meta-keyword":"if else elif endif define undef warning error line pragma ifdef ifndef include"},c:[{b:/\\\n/,r:0},t.inherit(r,{cN:"meta-string"}),{cN:"meta-string",b:/<[^\n>]*>/,e:/$/,i:"\\n"},t.CLCM,t.CBCM]},a=t.IR+"\\s*\\(",c={keyword:"int float while private char catch import module export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const for static_cast|10 union namespace unsigned long volatile static protected bool template mutable if public friend do goto auto void enum else break extern using asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue inline delete alignof constexpr decltype noexcept static_assert thread_local restrict _Bool complex _Complex _Imaginary atomic_bool atomic_char atomic_schar atomic_uchar atomic_short atomic_ushort atomic_int atomic_uint atomic_long atomic_ulong atomic_llong atomic_ullong new throw return and or not",built_in:"std string cin cout cerr clog stdin stdout stderr stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr abort abs acos asin atan2 atan calloc ceil cosh cos exit exp fabs floor fmod fprintf fputs free frexp fscanf isalnum isalpha iscntrl isdigit isgraph islower isprint ispunct isspace isupper isxdigit tolower toupper labs ldexp log10 log malloc realloc memchr memcmp memcpy memset modf pow printf putchar puts scanf sinh sin snprintf sprintf sqrt sscanf strcat strchr strcmp strcpy strcspn strlen strncat strncmp strncpy strpbrk strrchr strspn strstr tanh tan vfprintf vprintf vsprintf endl initializer_list unique_ptr",literal:"true false nullptr NULL"},n=[e,t.CLCM,t.CBCM,s,r];return{aliases:["c","cc","h","c++","h++","hpp"],k:c,i:"",k:c,c:["self",e]},{b:t.IR+"::",k:c},{v:[{b:/=/,e:/;/},{b:/\(/,e:/\)/},{bK:"new throw return else",e:/;/}],k:c,c:n.concat([{b:/\(/,e:/\)/,k:c,c:n.concat(["self"]),r:0}]),r:0},{cN:"function",b:"("+t.IR+"[\\*&\\s]+)+"+a,rB:!0,e:/[{;=]/,eE:!0,k:c,i:/[^\w\s\*&]/,c:[{b:a,rB:!0,c:[t.TM],r:0},{cN:"params",b:/\(/,e:/\)/,k:c,r:0,c:[t.CLCM,t.CBCM,r,s,e]},t.CLCM,t.CBCM,i]},{cN:"class",bK:"class struct",e:/[{;:]/,c:[{b://,c:["self"]},t.TM]}]),exports:{preprocessor:i,strings:r,k:c}}});hljs.registerLanguage("php",function(e){var c={b:"\\$+[a-zA-Z_-ÿ][a-zA-Z0-9_-ÿ]*"},i={cN:"meta",b:/<\?(php)?|\?>/},t={cN:"string",c:[e.BE,i],v:[{b:'b"',e:'"'},{b:"b'",e:"'"},e.inherit(e.ASM,{i:null}),e.inherit(e.QSM,{i:null})]},a={v:[e.BNM,e.CNM]};return{aliases:["php3","php4","php5","php6"],cI:!0,k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception default die require __FUNCTION__ enddeclare final try switch continue endfor endif declare unset true false trait goto instanceof insteadof __DIR__ __NAMESPACE__ yield finally",c:[e.HCM,e.C("//","$",{c:[i]}),e.C("/\\*","\\*/",{c:[{cN:"doctag",b:"@[A-Za-z]+"}]}),e.C("__halt_compiler.+?;",!1,{eW:!0,k:"__halt_compiler",l:e.UIR}),{cN:"string",b:/<<<['"]?\w+['"]?$/,e:/^\w+;?$/,c:[e.BE,{cN:"subst",v:[{b:/\$\w+/},{b:/\{\$/,e:/\}/}]}]},i,{cN:"keyword",b:/\$this\b/},c,{b:/(::|->)+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/},{cN:"function",bK:"function",e:/[;{]/,eE:!0,i:"\\$|\\[|%",c:[e.UTM,{cN:"params",b:"\\(",e:"\\)",c:["self",c,e.CBCM,t,a]}]},{cN:"class",bK:"class interface",e:"{",eE:!0,i:/[:\(\$"]/,c:[{bK:"extends implements"},e.UTM]},{bK:"namespace",e:";",i:/[\.']/,c:[e.UTM]},{bK:"use",e:";",c:[e.UTM]},{b:"=>"},t,a]}});hljs.registerLanguage("javascript",function(e){var r="[A-Za-z$_][0-9A-Za-z$_]*",t={keyword:"in of if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete let yield const export super debugger as async await static import from as",literal:"true false null undefined NaN Infinity",built_in:"eval isFinite isNaN parseFloat parseInt decodeURI decodeURIComponent encodeURI encodeURIComponent escape unescape Object Function Boolean Error EvalError InternalError RangeError ReferenceError StopIteration SyntaxError TypeError URIError Number Math Date String RegExp Array Float32Array Float64Array Int16Array Int32Array Int8Array Uint16Array Uint32Array Uint8Array Uint8ClampedArray ArrayBuffer DataView JSON Intl arguments require module console window document Symbol Set Map WeakSet WeakMap Proxy Reflect Promise"},a={cN:"number",v:[{b:"\\b(0[bB][01]+)"},{b:"\\b(0[oO][0-7]+)"},{b:e.CNR}],r:0},n={cN:"subst",b:"\\$\\{",e:"\\}",k:t,c:[]},c={cN:"string",b:"`",e:"`",c:[e.BE,n]};n.c=[e.ASM,e.QSM,c,a,e.RM];var s=n.c.concat([e.CBCM,e.CLCM]);return{aliases:["js","jsx"],k:t,c:[{cN:"meta",r:10,b:/^\s*['"]use (strict|asm)['"]/},{cN:"meta",b:/^#!/,e:/$/},e.ASM,e.QSM,c,e.CLCM,e.CBCM,a,{b:/[{,]\s*/,r:0,c:[{b:r+"\\s*:",rB:!0,r:0,c:[{cN:"attr",b:r,r:0}]}]},{b:"("+e.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[e.CLCM,e.CBCM,e.RM,{cN:"function",b:"(\\(.*?\\)|"+r+")\\s*=>",rB:!0,e:"\\s*=>",c:[{cN:"params",v:[{b:r},{b:/\(\s*\)/},{b:/\(/,e:/\)/,eB:!0,eE:!0,k:t,c:s}]}]},{b://,sL:"xml",c:[{b:/<\w+\s*\/>/,skip:!0},{b:/<\w+/,e:/(\/\w+|\w+\/)>/,skip:!0,c:[{b:/<\w+\s*\/>/,skip:!0},"self"]}]}],r:0},{cN:"function",bK:"function",e:/\{/,eE:!0,c:[e.inherit(e.TM,{b:r}),{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,c:s}],i:/\[|%/},{b:/\$[(.]/},e.METHOD_GUARD,{cN:"class",bK:"class",e:/[{;=]/,eE:!0,i:/[:"\[\]]/,c:[{bK:"extends"},e.UTM]},{bK:"constructor",e:/\{/,eE:!0}],i:/#(?!!)/}});hljs.registerLanguage("yaml",function(e){var b="true false yes no null",a="^[ \\-]*",r="[a-zA-Z_][\\w\\-]*",t={cN:"attr",v:[{b:a+r+":"},{b:a+'"'+r+'":'},{b:a+"'"+r+"':"}]},c={cN:"template-variable",v:[{b:"{{",e:"}}"},{b:"%{",e:"}"}]},l={cN:"string",r:0,v:[{b:/'/,e:/'/},{b:/"/,e:/"/},{b:/\S+/}],c:[e.BE,c]};return{cI:!0,aliases:["yml","YAML","yaml"],c:[t,{cN:"meta",b:"^---s*$",r:10},{cN:"string",b:"[\\|>] *$",rE:!0,c:l.c,e:t.v[0].b},{b:"<%[%=-]?",e:"[%-]?%>",sL:"ruby",eB:!0,eE:!0,r:0},{cN:"type",b:"!!"+e.UIR},{cN:"meta",b:"&"+e.UIR+"$"},{cN:"meta",b:"\\*"+e.UIR+"$"},{cN:"bullet",b:"^ *-",r:0},e.HCM,{bK:b,k:{literal:b}},e.CNM,l]}});hljs.registerLanguage("objectivec",function(e){var t={cN:"built_in",b:"\\b(AV|CA|CF|CG|CI|CL|CM|CN|CT|MK|MP|MTK|MTL|NS|SCN|SK|UI|WK|XC)\\w+"},_={keyword:"int float while char export sizeof typedef const struct for union unsigned long volatile static bool mutable if do return goto void enum else break extern asm case short default double register explicit signed typename this switch continue wchar_t inline readonly assign readwrite self @synchronized id typeof nonatomic super unichar IBOutlet IBAction strong weak copy in out inout bycopy byref oneway __strong __weak __block __autoreleasing @private @protected @public @try @property @end @throw @catch @finally @autoreleasepool @synthesize @dynamic @selector @optional @required @encode @package @import @defs @compatibility_alias __bridge __bridge_transfer __bridge_retained __bridge_retain __covariant __contravariant __kindof _Nonnull _Nullable _Null_unspecified __FUNCTION__ __PRETTY_FUNCTION__ __attribute__ getter setter retain unsafe_unretained nonnull nullable null_unspecified null_resettable class instancetype NS_DESIGNATED_INITIALIZER NS_UNAVAILABLE NS_REQUIRES_SUPER NS_RETURNS_INNER_POINTER NS_INLINE NS_AVAILABLE NS_DEPRECATED NS_ENUM NS_OPTIONS NS_SWIFT_UNAVAILABLE NS_ASSUME_NONNULL_BEGIN NS_ASSUME_NONNULL_END NS_REFINED_FOR_SWIFT NS_SWIFT_NAME NS_SWIFT_NOTHROW NS_DURING NS_HANDLER NS_ENDHANDLER NS_VALUERETURN NS_VOIDRETURN",literal:"false true FALSE TRUE nil YES NO NULL",built_in:"BOOL dispatch_once_t dispatch_queue_t dispatch_sync dispatch_async dispatch_once"},i=/[a-zA-Z@][a-zA-Z0-9_]*/,n="@interface @class @protocol @implementation";return{aliases:["mm","objc","obj-c"],k:_,l:i,i:""}]}]},{cN:"class",b:"("+n.split(" ").join("|")+")\\b",e:"({|$)",eE:!0,k:n,l:i,c:[e.UTM]},{b:"\\."+e.UIR,r:0}]}});hljs.registerLanguage("bash",function(e){var t={cN:"variable",v:[{b:/\$[\w\d#@][\w\d_]*/},{b:/\$\{(.*?)}/}]},s={cN:"string",b:/"/,e:/"/,c:[e.BE,t,{cN:"variable",b:/\$\(/,e:/\)/,c:[e.BE]}]},a={cN:"string",b:/'/,e:/'/};return{aliases:["sh","zsh"],l:/\b-?[a-z\._]+\b/,k:{keyword:"if then else elif fi for while in do done case esac function",literal:"true false",built_in:"break cd continue eval exec exit export getopts hash pwd readonly return shift test times trap umask unset alias bind builtin caller command declare echo enable help let local logout mapfile printf read readarray source type typeset ulimit unalias set shopt autoload bg bindkey bye cap chdir clone comparguments compcall compctl compdescribe compfiles compgroups compquote comptags comptry compvalues dirs disable disown echotc echoti emulate fc fg float functions getcap getln history integer jobs kill limit log noglob popd print pushd pushln rehash sched setcap setopt stat suspend ttyctl unfunction unhash unlimit unsetopt vared wait whence where which zcompile zformat zftp zle zmodload zparseopts zprof zpty zregexparse zsocket zstyle ztcp",_:"-ne -eq -lt -gt -f -d -e -s -l -a"},c:[{cN:"meta",b:/^#![^\n]+sh\s*$/,r:10},{cN:"function",b:/\w[\w\d_]*\s*\(\s*\)\s*\{/,rB:!0,c:[e.inherit(e.TM,{b:/\w[\w\d_]*/})],r:0},e.HCM,s,a,t]}});hljs.registerLanguage("coffeescript",function(e){var c={keyword:"in if for while finally new do return else break catch instanceof throw try this switch continue typeof delete debugger super yield import export from as default await then unless until loop of by when and or is isnt not",literal:"true false null undefined yes no on off",built_in:"npm require console print module global window document"},n="[A-Za-z$_][0-9A-Za-z$_]*",r={cN:"subst",b:/#\{/,e:/}/,k:c},i=[e.BNM,e.inherit(e.CNM,{starts:{e:"(\\s*/)?",r:0}}),{cN:"string",v:[{b:/'''/,e:/'''/,c:[e.BE]},{b:/'/,e:/'/,c:[e.BE]},{b:/"""/,e:/"""/,c:[e.BE,r]},{b:/"/,e:/"/,c:[e.BE,r]}]},{cN:"regexp",v:[{b:"///",e:"///",c:[r,e.HCM]},{b:"//[gim]*",r:0},{b:/\/(?![ *])(\\\/|.)*?\/[gim]*(?=\W|$)/}]},{b:"@"+n},{sL:"javascript",eB:!0,eE:!0,v:[{b:"```",e:"```"},{b:"`",e:"`"}]}];r.c=i;var s=e.inherit(e.TM,{b:n}),t="(\\(.*\\))?\\s*\\B[-=]>",o={cN:"params",b:"\\([^\\(]",rB:!0,c:[{b:/\(/,e:/\)/,k:c,c:["self"].concat(i)}]};return{aliases:["coffee","cson","iced"],k:c,i:/\/\*/,c:i.concat([e.C("###","###"),e.HCM,{cN:"function",b:"^\\s*"+n+"\\s*=\\s*"+t,e:"[-=]>",rB:!0,c:[s,o]},{b:/[:\(,=]\s*/,r:0,c:[{cN:"function",b:t,e:"[-=]>",rB:!0,c:[o]}]},{cN:"class",bK:"class",e:"$",i:/[:="\[\]]/,c:[{bK:"extends",eW:!0,i:/[:="\[\]]/,c:[s]},s]},{b:n+":",e:":",rB:!0,rE:!0,r:0}])}});hljs.registerLanguage("sql",function(e){var t=e.C("--","$");return{cI:!0,i:/[<>{}*#]/,c:[{bK:"begin end start commit rollback savepoint lock alter create drop rename call delete do handler insert load replace select truncate update set show pragma grant merge describe use explain help declare prepare execute deallocate release unlock purge reset change stop analyze cache flush optimize repair kill install uninstall checksum restore check backup revoke comment",e:/;/,eW:!0,l:/[\w\.]+/,k:{keyword:"abort abs absolute acc acce accep accept access accessed accessible account acos action activate add addtime admin administer advanced advise aes_decrypt aes_encrypt after agent aggregate ali alia alias allocate allow alter always analyze ancillary and any anydata anydataset anyschema anytype apply archive archived archivelog are as asc ascii asin assembly assertion associate asynchronous at atan atn2 attr attri attrib attribu attribut attribute attributes audit authenticated authentication authid authors auto autoallocate autodblink autoextend automatic availability avg backup badfile basicfile before begin beginning benchmark between bfile bfile_base big bigfile bin binary_double binary_float binlog bit_and bit_count bit_length bit_or bit_xor bitmap blob_base block blocksize body both bound buffer_cache buffer_pool build bulk by byte byteordermark bytes cache caching call calling cancel capacity cascade cascaded case cast catalog category ceil ceiling chain change changed char_base char_length character_length characters characterset charindex charset charsetform charsetid check checksum checksum_agg child choose chr chunk class cleanup clear client clob clob_base clone close cluster_id cluster_probability cluster_set clustering coalesce coercibility col collate collation collect colu colum column column_value columns columns_updated comment commit compact compatibility compiled complete composite_limit compound compress compute concat concat_ws concurrent confirm conn connec connect connect_by_iscycle connect_by_isleaf connect_by_root connect_time connection consider consistent constant constraint constraints constructor container content contents context contributors controlfile conv convert convert_tz corr corr_k corr_s corresponding corruption cos cost count count_big counted covar_pop covar_samp cpu_per_call cpu_per_session crc32 create creation critical cross cube cume_dist curdate current current_date current_time current_timestamp current_user cursor curtime customdatum cycle data database databases datafile datafiles datalength date_add date_cache date_format date_sub dateadd datediff datefromparts datename datepart datetime2fromparts day day_to_second dayname dayofmonth dayofweek dayofyear days db_role_change dbtimezone ddl deallocate declare decode decompose decrement decrypt deduplicate def defa defau defaul default defaults deferred defi defin define degrees delayed delegate delete delete_all delimited demand dense_rank depth dequeue des_decrypt des_encrypt des_key_file desc descr descri describ describe descriptor deterministic diagnostics difference dimension direct_load directory disable disable_all disallow disassociate discardfile disconnect diskgroup distinct distinctrow distribute distributed div do document domain dotnet double downgrade drop dumpfile duplicate duration each edition editionable editions element ellipsis else elsif elt empty enable enable_all enclosed encode encoding encrypt end end-exec endian enforced engine engines enqueue enterprise entityescaping eomonth error errors escaped evalname evaluate event eventdata events except exception exceptions exchange exclude excluding execu execut execute exempt exists exit exp expire explain export export_set extended extent external external_1 external_2 externally extract failed failed_login_attempts failover failure far fast feature_set feature_value fetch field fields file file_name_convert filesystem_like_logging final finish first first_value fixed flash_cache flashback floor flush following follows for forall force form forma format found found_rows freelist freelists freepools fresh from from_base64 from_days ftp full function general generated get get_format get_lock getdate getutcdate global global_name globally go goto grant grants greatest group group_concat group_id grouping grouping_id groups gtid_subtract guarantee guard handler hash hashkeys having hea head headi headin heading heap help hex hierarchy high high_priority hosts hour http id ident_current ident_incr ident_seed identified identity idle_time if ifnull ignore iif ilike ilm immediate import in include including increment index indexes indexing indextype indicator indices inet6_aton inet6_ntoa inet_aton inet_ntoa infile initial initialized initially initrans inmemory inner innodb input insert install instance instantiable instr interface interleaved intersect into invalidate invisible is is_free_lock is_ipv4 is_ipv4_compat is_not is_not_null is_used_lock isdate isnull isolation iterate java join json json_exists keep keep_duplicates key keys kill language large last last_day last_insert_id last_value lax lcase lead leading least leaves left len lenght length less level levels library like like2 like4 likec limit lines link list listagg little ln load load_file lob lobs local localtime localtimestamp locate locator lock locked log log10 log2 logfile logfiles logging logical logical_reads_per_call logoff logon logs long loop low low_priority lower lpad lrtrim ltrim main make_set makedate maketime managed management manual map mapping mask master master_pos_wait match matched materialized max maxextents maximize maxinstances maxlen maxlogfiles maxloghistory maxlogmembers maxsize maxtrans md5 measures median medium member memcompress memory merge microsecond mid migration min minextents minimum mining minus minute minvalue missing mod mode model modification modify module monitoring month months mount move movement multiset mutex name name_const names nan national native natural nav nchar nclob nested never new newline next nextval no no_write_to_binlog noarchivelog noaudit nobadfile nocheck nocompress nocopy nocycle nodelay nodiscardfile noentityescaping noguarantee nokeep nologfile nomapping nomaxvalue nominimize nominvalue nomonitoring none noneditionable nonschema noorder nopr nopro noprom nopromp noprompt norely noresetlogs noreverse normal norowdependencies noschemacheck noswitch not nothing notice notrim novalidate now nowait nth_value nullif nulls num numb numbe nvarchar nvarchar2 object ocicoll ocidate ocidatetime ociduration ociinterval ociloblocator ocinumber ociref ocirefcursor ocirowid ocistring ocitype oct octet_length of off offline offset oid oidindex old on online only opaque open operations operator optimal optimize option optionally or oracle oracle_date oradata ord ordaudio orddicom orddoc order ordimage ordinality ordvideo organization orlany orlvary out outer outfile outline output over overflow overriding package pad parallel parallel_enable parameters parent parse partial partition partitions pascal passing password password_grace_time password_lock_time password_reuse_max password_reuse_time password_verify_function patch path patindex pctincrease pctthreshold pctused pctversion percent percent_rank percentile_cont percentile_disc performance period period_add period_diff permanent physical pi pipe pipelined pivot pluggable plugin policy position post_transaction pow power pragma prebuilt precedes preceding precision prediction prediction_cost prediction_details prediction_probability prediction_set prepare present preserve prior priority private private_sga privileges procedural procedure procedure_analyze processlist profiles project prompt protection public publishingservername purge quarter query quick quiesce quota quotename radians raise rand range rank raw read reads readsize rebuild record records recover recovery recursive recycle redo reduced ref reference referenced references referencing refresh regexp_like register regr_avgx regr_avgy regr_count regr_intercept regr_r2 regr_slope regr_sxx regr_sxy reject rekey relational relative relaylog release release_lock relies_on relocate rely rem remainder rename repair repeat replace replicate replication required reset resetlogs resize resource respect restore restricted result result_cache resumable resume retention return returning returns reuse reverse revoke right rlike role roles rollback rolling rollup round row row_count rowdependencies rowid rownum rows rtrim rules safe salt sample save savepoint sb1 sb2 sb4 scan schema schemacheck scn scope scroll sdo_georaster sdo_topo_geometry search sec_to_time second section securefile security seed segment select self sequence sequential serializable server servererror session session_user sessions_per_user set sets settings sha sha1 sha2 share shared shared_pool short show shrink shutdown si_averagecolor si_colorhistogram si_featurelist si_positionalcolor si_stillimage si_texture siblings sid sign sin size size_t sizes skip slave sleep smalldatetimefromparts smallfile snapshot some soname sort soundex source space sparse spfile split sql sql_big_result sql_buffer_result sql_cache sql_calc_found_rows sql_small_result sql_variant_property sqlcode sqldata sqlerror sqlname sqlstate sqrt square standalone standby start starting startup statement static statistics stats_binomial_test stats_crosstab stats_ks_test stats_mode stats_mw_test stats_one_way_anova stats_t_test_ stats_t_test_indep stats_t_test_one stats_t_test_paired stats_wsr_test status std stddev stddev_pop stddev_samp stdev stop storage store stored str str_to_date straight_join strcmp strict string struct stuff style subdate subpartition subpartitions substitutable substr substring subtime subtring_index subtype success sum suspend switch switchoffset switchover sync synchronous synonym sys sys_xmlagg sysasm sysaux sysdate sysdatetimeoffset sysdba sysoper system system_user sysutcdatetime table tables tablespace tan tdo template temporary terminated tertiary_weights test than then thread through tier ties time time_format time_zone timediff timefromparts timeout timestamp timestampadd timestampdiff timezone_abbr timezone_minute timezone_region to to_base64 to_date to_days to_seconds todatetimeoffset trace tracking transaction transactional translate translation treat trigger trigger_nestlevel triggers trim truncate try_cast try_convert try_parse type ub1 ub2 ub4 ucase unarchived unbounded uncompress under undo unhex unicode uniform uninstall union unique unix_timestamp unknown unlimited unlock unpivot unrecoverable unsafe unsigned until untrusted unusable unused update updated upgrade upped upper upsert url urowid usable usage use use_stored_outlines user user_data user_resources users using utc_date utc_timestamp uuid uuid_short validate validate_password_strength validation valist value values var var_samp varcharc vari varia variab variabl variable variables variance varp varraw varrawc varray verify version versions view virtual visible void wait wallet warning warnings week weekday weekofyear wellformed when whene whenev wheneve whenever where while whitespace with within without work wrapped xdb xml xmlagg xmlattributes xmlcast xmlcolattval xmlelement xmlexists xmlforest xmlindex xmlnamespaces xmlpi xmlquery xmlroot xmlschema xmlserialize xmltable xmltype xor year year_to_month years yearweek",literal:"true false null",built_in:"array bigint binary bit blob boolean char character date dec decimal float int int8 integer interval number numeric real record serial serial8 smallint text varchar varying void"},c:[{cN:"string",b:"'",e:"'",c:[e.BE,{b:"''"}]},{cN:"string",b:'"',e:'"',c:[e.BE,{b:'""'}]},{cN:"string",b:"`",e:"`",c:[e.BE]},e.CNM,e.CBCM,t]},e.CBCM,t]}});hljs.registerLanguage("perl",function(e){var t="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0 break given say state when",r={cN:"subst",b:"[$@]\\{",e:"\\}",k:t},s={b:"->{",e:"}"},n={v:[{b:/\$\d/},{b:/[\$%@](\^\w\b|#\w+(::\w+)*|{\w+}|\w+(::\w*)*)/},{b:/[\$%@][^\s\w{]/,r:0}]},i=[e.BE,r,n],o=[n,e.HCM,e.C("^\\=\\w","\\=cut",{eW:!0}),s,{cN:"string",c:i,v:[{b:"q[qwxr]?\\s*\\(",e:"\\)",r:5},{b:"q[qwxr]?\\s*\\[",e:"\\]",r:5},{b:"q[qwxr]?\\s*\\{",e:"\\}",r:5},{b:"q[qwxr]?\\s*\\|",e:"\\|",r:5},{b:"q[qwxr]?\\s*\\<",e:"\\>",r:5},{b:"qw\\s+q",e:"q",r:5},{b:"'",e:"'",c:[e.BE]},{b:'"',e:'"'},{b:"`",e:"`",c:[e.BE]},{b:"{\\w+}",c:[],r:0},{b:"-?\\w+\\s*\\=\\>",c:[],r:0}]},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"(\\/\\/|"+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"function",bK:"sub",e:"(\\s*\\(.*?\\))?[;{]",eE:!0,r:5,c:[e.TM]},{b:"-\\w\\b",r:0},{b:"^__DATA__$",e:"^__END__$",sL:"mojolicious",c:[{b:"^@@.*",e:"$",cN:"comment"}]}];return r.c=o,s.c=o,{aliases:["pl","pm"],l:/[\w\.]+/,k:t,c:o}});hljs.registerLanguage("python",function(e){var r={keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda async await nonlocal|10 None True False",built_in:"Ellipsis NotImplemented"},b={cN:"meta",b:/^(>>>|\.\.\.) /},c={cN:"subst",b:/\{/,e:/\}/,k:r,i:/#/},a={cN:"string",c:[e.BE],v:[{b:/(u|b)?r?'''/,e:/'''/,c:[b],r:10},{b:/(u|b)?r?"""/,e:/"""/,c:[b],r:10},{b:/(fr|rf|f)'''/,e:/'''/,c:[b,c]},{b:/(fr|rf|f)"""/,e:/"""/,c:[b,c]},{b:/(u|r|ur)'/,e:/'/,r:10},{b:/(u|r|ur)"/,e:/"/,r:10},{b:/(b|br)'/,e:/'/},{b:/(b|br)"/,e:/"/},{b:/(fr|rf|f)'/,e:/'/,c:[c]},{b:/(fr|rf|f)"/,e:/"/,c:[c]},e.ASM,e.QSM]},s={cN:"number",r:0,v:[{b:e.BNR+"[lLjJ]?"},{b:"\\b(0o[0-7]+)[lLjJ]?"},{b:e.CNR+"[lLjJ]?"}]},i={cN:"params",b:/\(/,e:/\)/,c:["self",b,s,a]};return c.c=[a,s,b],{aliases:["py","gyp"],k:r,i:/(<\/|->|\?)|=>/,c:[b,s,a,e.HCM,{v:[{cN:"function",bK:"def"},{cN:"class",bK:"class"}],e:/:/,i:/[${=;\n,]/,c:[e.UTM,i,{b:/->/,eW:!0,k:"None"}]},{cN:"meta",b:/^[\t ]*@/,e:/$/},{b:/\b(print|exec)\(/}]}});hljs.registerLanguage("http",function(e){var t="HTTP/[0-9\\.]+";return{aliases:["https"],i:"\\S",c:[{b:"^"+t,e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{b:"^[A-Z]+ (.*?) "+t+"$",rB:!0,e:"$",c:[{cN:"string",b:" ",e:" ",eB:!0,eE:!0},{b:t},{cN:"keyword",b:"[A-Z]+"}]},{cN:"attribute",b:"^\\w",e:": ",eE:!0,i:"\\n|\\s|=",starts:{e:"$",r:0}},{b:"\\n\\n",starts:{sL:[],eW:!0}}]}});hljs.registerLanguage("diff",function(e){return{aliases:["patch"],c:[{cN:"meta",r:10,v:[{b:/^@@ +\-\d+,\d+ +\+\d+,\d+ +@@$/},{b:/^\*\*\* +\d+,\d+ +\*\*\*\*$/},{b:/^\-\-\- +\d+,\d+ +\-\-\-\-$/}]},{cN:"comment",v:[{b:/Index: /,e:/$/},{b:/={3,}/,e:/$/},{b:/^\-{3}/,e:/$/},{b:/^\*{3} /,e:/$/},{b:/^\+{3}/,e:/$/},{b:/\*{5}/,e:/\*{5}$/}]},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"addition",b:"^\\!",e:"$"}]}});hljs.registerLanguage("makefile",function(e){var i={cN:"variable",v:[{b:"\\$\\("+e.UIR+"\\)",c:[e.BE]},{b:/\$[@%)?(\\[\\])?";return{aliases:["csharp"],k:i,i:/::/,c:[e.C("///","$",{rB:!0,c:[{cN:"doctag",v:[{b:"///",r:0},{b:""},{b:""}]}]}),e.CLCM,e.CBCM,{cN:"meta",b:"#",e:"$",k:{"meta-keyword":"if else elif endif define undef warning error line region endregion pragma checksum"}},l,e.CNM,{bK:"class interface",e:/[{;=]/,i:/[^\s:]/,c:[e.TM,e.CLCM,e.CBCM]},{bK:"namespace",e:/[{;=]/,i:/[^\s:]/,c:[e.inherit(e.TM,{b:"[a-zA-Z](\\.?\\w)*"}),e.CLCM,e.CBCM]},{cN:"meta",b:"^\\s*\\[",eB:!0,e:"\\]",eE:!0,c:[{cN:"meta-string",b:/"/,e:/"/}]},{bK:"new return throw await else",r:0},{cN:"function",b:"("+b+"\\s+)+"+e.IR+"\\s*\\(",rB:!0,e:/[{;=]/,eE:!0,k:i,c:[{b:e.IR+"\\s*\\(",rB:!0,c:[e.TM],r:0},{cN:"params",b:/\(/,e:/\)/,eB:!0,eE:!0,k:i,r:0,c:[l,e.CNM,e.CBCM]},e.CLCM,e.CBCM]}]}});hljs.registerLanguage("twig",function(e){var t={cN:"params",b:"\\(",e:"\\)"},a="attribute block constant cycle date dump include max min parent random range source template_from_string",r={bK:a,k:{name:a},r:0,c:[t]},c={b:/\|[A-Za-z_]+:?/,k:"abs batch capitalize convert_encoding date date_modify default escape first format join json_encode keys last length lower merge nl2br number_format raw replace reverse round slice sort split striptags title trim upper url_encode",c:[r]},s="autoescape block do embed extends filter flush for if import include macro sandbox set spaceless use verbatim";return s=s+" "+s.split(" ").map(function(e){return"end"+e}).join(" "),{aliases:["craftcms"],cI:!0,sL:"xml",c:[e.C(/\{#/,/#}/),{cN:"template-tag",b:/\{%/,e:/%}/,c:[{cN:"name",b:/\w+/,k:s,starts:{eW:!0,c:[c,r],r:0}}]},{cN:"template-variable",b:/\{\{/,e:/}}/,c:["self",c,r]}]}});hljs.registerLanguage("shell",function(s){return{aliases:["console"],c:[{cN:"meta",b:"^\\s{0,3}[\\w\\d\\[\\]()@-]*[>%$#]",starts:{e:"$",sL:"bash"}}]}}); \ No newline at end of file diff --git a/plugins/hyer/getads.php b/plugins/hyer/getads.php new file mode 100644 index 0000000..eb60d59 --- /dev/null +++ b/plugins/hyer/getads.php @@ -0,0 +1,204 @@ +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"; + } + +} \ No newline at end of file diff --git a/plugins/hyer/hyer.php b/plugins/hyer/hyer.php new file mode 100644 index 0000000..2dcab8d --- /dev/null +++ b/plugins/hyer/hyer.php @@ -0,0 +1,221 @@ + '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 = '
'; + + # 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 = '
'; + $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); + } +} \ No newline at end of file diff --git a/plugins/hyer/hyer.yaml b/plugins/hyer/hyer.yaml new file mode 100644 index 0000000..a8e010a --- /dev/null +++ b/plugins/hyer/hyer.yaml @@ -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) \ No newline at end of file diff --git a/plugins/hyer/public/hyer.css b/plugins/hyer/public/hyer.css new file mode 100644 index 0000000..06f1696 --- /dev/null +++ b/plugins/hyer/public/hyer.css @@ -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); +} diff --git a/plugins/hyer/public/hyer.js b/plugins/hyer/public/hyer.js new file mode 100644 index 0000000..3fa62d2 --- /dev/null +++ b/plugins/hyer/public/hyer.js @@ -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: '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '' + + '' + + '
' + + '
' + + '' + + '' + + '
' + + '' + + '
' + + '
' + + '
' + + '
' + + '
{{ message }}
' + + '
' + + '
' + + '
' + + '' + + '
Premium
' + + '

{{ getTitle(ad) }}

' + + '{{ getLink(ad) }}' + + '

{{ getTeaser(ad) }}

' + + '
' + + '
' + + '
' + + ' {{ ad.city }} ' + + ' {{ ad.size}}' + + '
' + + '
Web:' + + '' + + '' + + '' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '

Web-Agentur oder Freelancer?

' + + '

Dann ist genau hier Ihre Expertise gefragt!
Mit einem Dienstleister-Eintrag auf cmsstash.de

' + + 'Eintrag erstellen' + + '
' + + '
' + + '
', + 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: '
' + + '
' + + '
' + + '
' + + '' + + '
Premium
' + + '

{{ getTitle(ad) }}

' + + '{{ getLink(ad) }}' + + '

{{ getTeaser(ad) }}

' + + '
' + + '
' + + '
' + + ' {{ ad.city }} ' + + ' {{ ad.size}}' + + '
' + + '
Web:' + + '' + + '' + + '' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '

Web-Agentur oder Freelancer?

' + + '

Dann ist genau hier Ihre Expertise gefragt!
Mit einem Dienstleister-Eintrag auf cmsstash.de

' + + 'Eintrag erstellen' + + '
' + + '
' + + '
' + + '
', + 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: '', + }, +}); \ No newline at end of file diff --git a/plugins/hyer/templates/hyer.twig b/plugins/hyer/templates/hyer.twig new file mode 100644 index 0000000..ee108c0 --- /dev/null +++ b/plugins/hyer/templates/hyer.twig @@ -0,0 +1,51 @@ + \ No newline at end of file diff --git a/plugins/math/math.php b/plugins/math/math.php index a9fc1ba..c075823 100644 --- a/plugins/math/math.php +++ b/plugins/math/math.php @@ -1,47 +1,47 @@ - '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);'); - } - } - } + '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);'); + } + } + } } \ No newline at end of file diff --git a/plugins/math/math.yaml b/plugins/math/math.yaml index dda4eec..70e56be 100644 --- a/plugins/math/math.yaml +++ b/plugins/math/math.yaml @@ -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 \ No newline at end of file diff --git a/plugins/math/public/README.md b/plugins/math/public/README.md index a3396c1..d0a0632 100644 --- a/plugins/math/public/README.md +++ b/plugins/math/public/README.md @@ -1,140 +1,140 @@ -# [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:** KaTeX’s layout is based on Donald Knuth’s 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 - - -``` - -#### 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}"); -// '...' -``` - -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 ` +``` + +#### 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}"); +// '...' +``` + +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 `'; - } - } - - public function addInlineJS($JS) - { - $this->inlineJS[] = ''; - } - - public function renderCSS() - { - return implode('', $this->CSS) . implode('', $this->inlineCSS); - } - - public function renderJS() - { - return implode('', $this->JS) . implode('', $this->inlineJS); - } - - /** - * Checks, if a string is a valid internal or external ressource like js-file or css-file - * @params $path string - * @return string or false - */ - public function getFileUrl($path) - { - $internalFile = __DIR__ . '/../plugins' . $path; - - if(file_exists($internalFile)) - { - return $this->baseUrl . '/plugins' . $path; - } - - return $path; - - if(fopen($path, "r")) - { - return $path; - } - - return false; - } +baseUrl = $baseUrl; + $this->JS = array(); + $this->CSS = array(); + $this->inlineJS = array(); + $this->inlineCSS = array(); + } + + public function addCSS($CSS) + { + $CSSfile = $this->getFileUrl($CSS); + + if($CSSfile) + { + $this->CSS[] = ''; + } + } + + public function addInlineCSS($CSS) + { + $this->inlineCSS[] = ''; + } + + public function addJS($JS) + { + $JSfile = $this->getFileUrl($JS); + + if($JSfile) + { + $this->JS[] = ''; + } + } + + public function addInlineJS($JS) + { + $this->inlineJS[] = ''; + } + + public function activateVue() + { + $vueUrl = ''; + if(!in_array($vueUrl, $this->JS)) + { + $this->JS[] = $vueUrl; + } + } + + public function activateAxios() + { + $axiosUrl = ''; + if(!in_array($axiosUrl, $this->JS)) + { + $this->JS[] = $axiosUrl; + + $axios = ''; + $this->JS[] = $axios; + } + } + + public function activateTachyons() + { + $tachyonsUrl = ''; + if(!in_array($tachyonsUrl, $this->CSS)) + { + $this->CSS[] = $tachyonsUrl; + } + } + + public function renderCSS() + { + return implode('', $this->CSS) . implode('', $this->inlineCSS); + } + + public function renderJS() + { + return implode('', $this->JS) . implode('', $this->inlineJS); + } + + /** + * Checks, if a string is a valid internal or external ressource like js-file or css-file + * @params $path string + * @return string or false + */ + public function getFileUrl($path) + { + $internalFile = __DIR__ . '/../plugins' . $path; + + if(file_exists($internalFile)) + { + return $this->baseUrl . '/plugins' . $path; + } + + return $path; + + if(fopen($path, "r")) + { + return $path; + } + + return false; + } } \ No newline at end of file diff --git a/system/Controllers/AuthController.php b/system/Controllers/AuthController.php index 2bd2aa5..c0b5e1b 100644 --- a/system/Controllers/AuthController.php +++ b/system/Controllers/AuthController.php @@ -1,179 +1,179 @@ -withRedirect($this->c->router->pathFor('content.raw')); - } - else - { - return $response->withRedirect($this->c->router->pathFor('auth.show')); - } - } - - /** - * show login form - * - * @param obj $request the slim request object. - * @param obj $response the slim response object. - * @param array $args with arguments past to the slim router - * @return obj $response and string route. - */ - - public function show(Request $request, Response $response, $args) - { - $data = array(); - - /* check previous login attemps */ - $yaml = new WriteYaml(); - $logins = $yaml->getYaml('settings/users', '.logins'); - $userIP = $this->getUserIP(); - $userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false; - - if($userLogins) - { - /* get the latest */ - $lastLogin = intval($logins[$userIP][$userLogins-1]); - - /* if last login is longer than 60 seconds ago, clear it. */ - if(time() - $lastLogin > 60) - { - unset($logins[$userIP]); - $yaml->updateYaml('settings/users', '.logins', $logins); - } - - /* Did the user made three login attemps that failed? */ - elseif($userLogins >= 3) - { - $timeleft = 60 - (time() - $lastLogin); - $data['messages'] = array('time' => $timeleft, 'error' => array( 'Too many bad logins. Please wait.')); - } - } - - return $this->render($response, '/auth/login.twig', $data); - } - - /** - * signin an existing user - * - * @param obj $request the slim request object with form data in the post params. - * @param obj $response the slim response object. - * @return obj $response with redirect to route. - */ - - public function login(Request $request, Response $response) - { - /* log user attemps to authenticate */ - $yaml = new WriteYaml(); - $logins = $yaml->getYaml('settings/users', '.logins'); - $userIP = $this->getUserIP(); - $userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false; - - /* if there have been user logins before. You have to do this again, because user does not always refresh the login page and old login attemps are stored. */ - if($userLogins) - { - /* get the latest */ - $lastLogin = intval($logins[$userIP][$userLogins-1]); - - /* if last login is longer than 60 seconds ago, clear it and add this attempt */ - if(time() - $lastLogin > 60) - { - unset($logins[$userIP]); - $yaml->updateYaml('settings/users', '.logins', $logins); - } - - /* Did the user made three login attemps that failed? */ - elseif($userLogins >= 2) - { - $logins[$userIP][] = time(); - $yaml->updateYaml('settings/users', '.logins', $logins); - - return $response->withRedirect($this->c->router->pathFor('auth.show')); - } - } - - /* authentication */ - $params = $request->getParams(); - $validation = new Validation(); - - if($validation->signin($params)) - { - $user = new User(); - $userdata = $user->getUser($params['username']); - - if($userdata && password_verify($params['password'], $userdata['password'])) - { - $user->login($userdata['username']); - - /* clear the user login attemps */ - if($userLogins) - { - unset($logins[$userIP]); - $yaml->updateYaml('settings/users', '.logins', $logins); - } - - return $response->withRedirect($this->c->router->pathFor('content.raw')); - } - } - - /* if authentication failed, add attempt to log file */ - $logins[$userIP][] = time(); - $yaml->updateYaml('settings/users', '.logins', $logins); - - $this->c->flash->addMessage('error', 'Ups, wrong password or username, please try again.'); - return $response->withRedirect($this->c->router->pathFor('auth.show')); - } - - /** - * log out a user - * - * @param obj $request the slim request object - * @param obj $response the slim response object - * @return obje $response with redirect to route - */ - - public function logout(Request $request, Response $response) - { - if(isset($_SESSION)) - { - session_destroy(); - } - - return $response->withRedirect($this->c->router->pathFor('auth.show')); - } - - private function getUserIP() - { - $client = @$_SERVER['HTTP_CLIENT_IP']; - $forward = @$_SERVER['HTTP_X_FORWARDED_FOR']; - $remote = $_SERVER['REMOTE_ADDR']; - - if(filter_var($client, FILTER_VALIDATE_IP)) - { - $ip = $client; - } - elseif(filter_var($forward, FILTER_VALIDATE_IP)) - { - $ip = $forward; - } - else - { - $ip = $remote; - } - - return $ip; - } +withRedirect($this->c->router->pathFor('content.raw')); + } + else + { + return $response->withRedirect($this->c->router->pathFor('auth.show')); + } + } + + /** + * show login form + * + * @param obj $request the slim request object. + * @param obj $response the slim response object. + * @param array $args with arguments past to the slim router + * @return obj $response and string route. + */ + + public function show(Request $request, Response $response, $args) + { + $data = array(); + + /* check previous login attemps */ + $yaml = new WriteYaml(); + $logins = $yaml->getYaml('settings/users', '.logins'); + $userIP = $this->getUserIP(); + $userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false; + + if($userLogins) + { + /* get the latest */ + $lastLogin = intval($logins[$userIP][$userLogins-1]); + + /* if last login is longer than 60 seconds ago, clear it. */ + if(time() - $lastLogin > 60) + { + unset($logins[$userIP]); + $yaml->updateYaml('settings/users', '.logins', $logins); + } + + /* Did the user made three login attemps that failed? */ + elseif($userLogins >= 3) + { + $timeleft = 60 - (time() - $lastLogin); + $data['messages'] = array('time' => $timeleft, 'error' => array( 'Too many bad logins. Please wait.')); + } + } + + return $this->render($response, '/auth/login.twig', $data); + } + + /** + * signin an existing user + * + * @param obj $request the slim request object with form data in the post params. + * @param obj $response the slim response object. + * @return obj $response with redirect to route. + */ + + public function login(Request $request, Response $response) + { + /* log user attemps to authenticate */ + $yaml = new WriteYaml(); + $logins = $yaml->getYaml('settings/users', '.logins'); + $userIP = $this->getUserIP(); + $userLogins = isset($logins[$userIP]) ? count($logins[$userIP]) : false; + + /* if there have been user logins before. You have to do this again, because user does not always refresh the login page and old login attemps are stored. */ + if($userLogins) + { + /* get the latest */ + $lastLogin = intval($logins[$userIP][$userLogins-1]); + + /* if last login is longer than 60 seconds ago, clear it and add this attempt */ + if(time() - $lastLogin > 60) + { + unset($logins[$userIP]); + $yaml->updateYaml('settings/users', '.logins', $logins); + } + + /* Did the user made three login attemps that failed? */ + elseif($userLogins >= 2) + { + $logins[$userIP][] = time(); + $yaml->updateYaml('settings/users', '.logins', $logins); + + return $response->withRedirect($this->c->router->pathFor('auth.show')); + } + } + + /* authentication */ + $params = $request->getParams(); + $validation = new Validation(); + + if($validation->signin($params)) + { + $user = new User(); + $userdata = $user->getUser($params['username']); + + if($userdata && password_verify($params['password'], $userdata['password'])) + { + $user->login($userdata['username']); + + /* clear the user login attemps */ + if($userLogins) + { + unset($logins[$userIP]); + $yaml->updateYaml('settings/users', '.logins', $logins); + } + + return $response->withRedirect($this->c->router->pathFor('content.raw')); + } + } + + /* if authentication failed, add attempt to log file */ + $logins[$userIP][] = time(); + $yaml->updateYaml('settings/users', '.logins', $logins); + + $this->c->flash->addMessage('error', 'Ups, wrong password or username, please try again.'); + return $response->withRedirect($this->c->router->pathFor('auth.show')); + } + + /** + * log out a user + * + * @param obj $request the slim request object + * @param obj $response the slim response object + * @return obje $response with redirect to route + */ + + public function logout(Request $request, Response $response) + { + if(isset($_SESSION)) + { + session_destroy(); + } + + return $response->withRedirect($this->c->router->pathFor('auth.show')); + } + + private function getUserIP() + { + $client = @$_SERVER['HTTP_CLIENT_IP']; + $forward = @$_SERVER['HTTP_X_FORWARDED_FOR']; + $remote = $_SERVER['REMOTE_ADDR']; + + if(filter_var($client, FILTER_VALIDATE_IP)) + { + $ip = $client; + } + elseif(filter_var($forward, FILTER_VALIDATE_IP)) + { + $ip = $forward; + } + else + { + $ip = $remote; + } + + return $ip; + } } \ No newline at end of file diff --git a/system/Controllers/ContentApiController.php b/system/Controllers/ContentApiController.php index 9a3128d..490bd8b 100644 --- a/system/Controllers/ContentApiController.php +++ b/system/Controllers/ContentApiController.php @@ -1,1261 +1,1274 @@ -params = $request->getParams(); - $this->uri = $request->getUri(); - - # validate input only if raw mode - if($this->params['raw']) - { - if(!$this->validateEditorInput()){ return $response->withJson($this->errors,422); } - } - - # set structure - if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); } - - # set item - if(!$this->setItem()){ return $response->withJson($this->errors, 404); } - - # set the status for published and drafted - $this->setPublishStatus(); - - # set path - $this->setItemPath($this->item->fileType); - - # if raw mode, use the content from request - if($this->params['raw']) - { - $this->content = '# ' . $this->params['title'] . "\r\n\r\n" . $this->params['content']; - } - else - { - # read content from file - if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - - # If it is a draft, then create clean markdown content - if(is_array($this->content)) - { - # initialize parsedown extension - $parsedown = new ParsedownExtension(); - - # turn markdown into an array of markdown-blocks - $this->content = $parsedown->arrayBlocksToMarkdown($this->content); - } - } - - # set path for the file (or folder) - $this->setItemPath('md'); - - # update the file - if($this->write->writeFile($this->settings['contentFolder'], $this->path, $this->content)) - { - # update the file - $delete = $this->deleteContentFiles(['txt']); - - # update the internal structure - $this->setStructure($draft = true, $cache = false); - - # update the public structure - $this->setStructure($draft = false, $cache = false); - - return $response->withJson(['success'], 200); - } - else - { - return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404); - } - } - - public function unpublishArticle(Request $request, Response $response, $args) - { - # get params from call - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - - # set structure - if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); } - - # set item - if(!$this->setItem()){ return $response->withJson($this->errors, 404); } - - # set the status for published and drafted - $this->setPublishStatus(); - - # check if draft exists, if not, create one. - if(!$this->item->drafted) - { - # set path for the file (or folder) - $this->setItemPath('md'); - - # set content of markdown-file - if(!$this->setContent()){ return $response->withJson($this->errors, 404); } - - # initialize parsedown extension - $parsedown = new ParsedownExtension(); - - # turn markdown into an array of markdown-blocks - $contentArray = $parsedown->markdownToArrayBlocks($this->content); - - # encode the content into json - $contentJson = json_encode($contentArray); - - # set path for the file (or folder) - $this->setItemPath('txt'); - - /* update the file */ - if(!$this->write->writeFile($this->settings['contentFolder'], $this->path, $contentJson)) - { - return $response->withJson(['errors' => ['message' => 'Could not create a draft of the page. Please check if the folder is writable']], 404); - } - } - - # update the file - $delete = $this->deleteContentFiles(['md']); - - if($delete) - { - # update the internal structure - $this->setStructure($draft = true, $cache = false); - - # update the live structure - $this->setStructure($draft = false, $cache = false); - - return $response->withJson(['success'], 200); - } - else - { - return $response->withJson(['errors' => ['message' => "Could not delete some files. Please check if the files exists and are writable"]], 404); - } - } - - public function deleteArticle(Request $request, Response $response, $args) - { - # get params from call - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - - # set url to base path initially - $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor']; - - # set structure - if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); } - - # set item - if(!$this->setItem()){ return $response->withJson($this->errors, 404); } - - if($this->item->elementType == 'file') - { - $delete = $this->deleteContentFiles(['md','txt']); - } - elseif($this->item->elementType == 'folder') - { - $delete = $this->deleteContentFolder(); - } - - if($delete) - { - # check if it is a subfile or subfolder and set the redirect-url to the parent item - if(count($this->item->keyPathArray) > 1) - { - # get the parent item - $parentItem = Folder::getParentItem($this->structure, $this->item->keyPathArray); - - if($parentItem) - { - # an active file has been moved to another folder - $url .= $parentItem->urlRelWoF; - } - } - - # update the live structure - $this->setStructure($draft = false, $cache = false); - - #update the backend structure - $this->setStructure($draft = true, $cache = false); - - return $response->withJson(array('data' => $this->structure, 'errors' => false, 'url' => $url), 200); - } - else - { - return $response->withJson(array('data' => $this->structure, 'errors' => $this->errors), 404); - } - } - - public function updateArticle(Request $request, Response $response, $args) - { - # get params from call - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - - # validate input - if(!$this->validateEditorInput()){ return $response->withJson($this->errors,422); } - - # set structure - if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); } - - # set item - if(!$this->setItem()){ return $response->withJson($this->errors, 404); } - - # set path for the file (or folder) - $this->setItemPath('txt'); - - # merge title with content for complete markdown document - $updatedContent = '# ' . $this->params['title'] . "\r\n\r\n" . $this->params['content']; - - # initialize parsedown extension - $parsedown = new ParsedownExtension(); - - # turn markdown into an array of markdown-blocks - $contentArray = $parsedown->markdownToArrayBlocks($updatedContent); - - # encode the content into json - $contentJson = json_encode($contentArray); - - /* update the file */ - if($this->write->writeFile($this->settings['contentFolder'], $this->path, $contentJson)) - { - # update the internal structure - $this->setStructure($draft = true, $cache = false); - - return $response->withJson(['success'], 200); - } - else - { - return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404); - } - } - - public function sortArticle(Request $request, Response $response, $args) - { - # get params from call - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - - # url is only needed, if an active page is moved to another folder, so user has to be redirected to the new url - $url = false; - - # set structure - if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); } - - # validate input - if(!$this->validateNavigationSort()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Data not valid. Please refresh the page and try again.', 'url' => $url), 422); } - - # get the ids (key path) for item, old folder and new folder - $itemKeyPath = explode('.', $this->params['item_id']); - $parentKeyFrom = explode('.', $this->params['parent_id_from']); - $parentKeyTo = explode('.', $this->params['parent_id_to']); - - # get the item from structure - $item = Folder::getItemWithKeyPath($this->structure, $itemKeyPath); - - if(!$item){ return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not find this page. Please refresh and try again.', 'url' => $url), 404); } - - # if a folder is moved on the first level - if($this->params['parent_id_from'] == 'navi') - { - # create empty and default values so that the logic below still works - $newFolder = new \stdClass(); - $newFolder->path = ''; - $folderContent = $this->structure; - } - else - { - # get the target folder from structure - $newFolder = Folder::getItemWithKeyPath($this->structure, $parentKeyTo); - - # get the content of the target folder - $folderContent = $newFolder->folderContent; - } - - # if the item has been moved within the same folder - if($this->params['parent_id_from'] == $this->params['parent_id_to']) - { - # get key of item - $itemKey = end($itemKeyPath); - reset($itemKeyPath); - - # delete item from folderContent - unset($folderContent[$itemKey]); - } - elseif($this->params['active'] == 'active') - { - # an active file has been moved to another folder, so send new url with response - $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . $newFolder->urlRelWoF . '/' . $item->slug; - } - - # add item to newFolder - array_splice($folderContent, $this->params['index_new'], 0, array($item)); - - # initialize index - $index = 0; - - # initialise write object - $write = new Write(); - - # iterate through the whole content of the new folder to rename the files - $writeError = false; - foreach($folderContent as $folderItem) - { - if(!$write->moveElement($folderItem, $newFolder->path, $index)) - { - $writeError = true; - } - $index++; - } - if($writeError){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); } - - # update the structure for editor - $this->setStructure($draft = true, $cache = false); - - # get item for url and set it active again - if(isset($this->params['url'])) - { - $activeItem = Folder::getItemForUrl($this->structure, $this->params['url']); - } - - # keep the internal structure for response - $internalStructure = $this->structure; - - # update the structure for website - $this->setStructure($draft = false, $cache = false); - - return $response->withJson(array('data' => $internalStructure, 'errors' => false, 'url' => $url)); - } - - public function createArticle(Request $request, Response $response, $args) - { - # get params from call - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - - # url is only needed, if an active page is moved - $url = false; - - # set structure - if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); } - - # validate input - if(!$this->validateNaviItem()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Special Characters not allowed. Length between 1 and 20 chars.', 'url' => $url), 422); } - - # get the ids (key path) for item, old folder and new folder - $folderKeyPath = explode('.', $this->params['folder_id']); - - # get the item from structure - $folder = Folder::getItemWithKeyPath($this->structure, $folderKeyPath); - - if(!$folder){ return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not find this page. Please refresh and try again.', 'url' => $url), 404); } - - # Rename all files within the folder to make sure, that namings and orders are correct - # get the content of the target folder - $folderContent = $folder->folderContent; - - # create the name for the new item - $nameParts = Folder::getStringParts($this->params['item_name']); - $name = implode("-", $nameParts); - $slug = $name; - - # initialize index - $index = 0; - - # initialise write object - $write = new Write(); - - # iterate through the whole content of the new folder - $writeError = false; - - foreach($folderContent as $folderItem) - { - # check, if the same name as new item, then return an error - if($folderItem->slug == $slug) - { - return $response->withJson(array('data' => $this->structure, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404); - } - - if(!$write->moveElement($folderItem, $folder->path, $index)) - { - $writeError = true; - } - $index++; - } - - if($writeError){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); } - - # add prefix number to the name - $namePath = $index > 9 ? $index . '-' . $name : '0' . $index . '-' . $name; - $folderPath = 'content' . $folder->path; - - # create default content - $content = json_encode(['# Add Title', 'Add Content']); - - if($this->params['type'] == 'file') - { - if(!$write->writeFile($folderPath, $namePath . '.txt', $content)) - { - return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the file. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); - } - } - elseif($this->params['type'] == 'folder') - { - if(!$write->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath)) - { - return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); - } - $write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content); - } - - # update the structure for editor - $this->setStructure($draft = true, $cache = false); - - # get item for url and set it active again - if(isset($this->params['url'])) - { - $activeItem = Folder::getItemForUrl($this->structure, $this->params['url']); - } - - # activate this if you want to redirect after creating the page... - # $url = $this->uri->getBaseUrl() . '/tm/content' . $folder->urlRelWoF . '/' . $name; - - return $response->withJson(array('data' => $this->structure, 'errors' => false, 'url' => $url)); - } - - public function createBaseFolder(Request $request, Response $response, $args) - { - # get params from call - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - - # url is only needed, if an active page is moved - $url = false; - - # set structure - if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); } - - # validate input - #if(!$this->validateBaseFolder()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Special Characters not allowed. Length between 1 and 20 chars.', 'url' => $url), 422); } - - # create the name for the new item - $nameParts = Folder::getStringParts($this->params['item_name']); - $name = implode("-", $nameParts); - $slug = $name; - - # initialize index - $index = 0; - - # initialise write object - $write = new Write(); - - # iterate through the whole content of the new folder - $writeError = false; - - foreach($this->structure as $folder) - { - # check, if the same name as new item, then return an error - if($folder->slug == $slug) - { - return $response->withJson(array('data' => $this->structure, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404); - } - - if(!$write->moveElement($folder, '', $index)) - { - $writeError = true; - } - $index++; - } - - if($writeError){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); } - - # add prefix number to the name - $namePath = $index > 9 ? $index . '-' . $name : '0' . $index . '-' . $name; - $folderPath = 'content'; - - if(!$write->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath)) - { - return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); - } - - # create default content - $content = json_encode(['# Add Title', 'Add Content']); - - $write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content); - - # update the structure for editor - $this->setStructure($draft = true, $cache = false); - - # get item for url and set it active again - if(isset($this->params['url'])) - { - $activeItem = Folder::getItemForUrl($this->structure, $this->params['url']); - } - - return $response->withJson(array('data' => $this->structure, 'errors' => false, 'url' => $url)); - } - - public function getNavigation(Request $request, Response $response, $args) - { - # get params from call - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - - # set structure - if(!$this->setStructure($draft = true, $cache = false)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); } - - # set information for homepage - $this->setHomepage(); - - # get item for url and set it active again - if(isset($this->params['url'])) - { - $activeItem = Folder::getItemForUrl($this->structure, $this->params['url']); - } - - return $response->withJson(array('data' => $this->structure, 'homepage' => $this->homepage, 'errors' => false)); - } - - public function getArticleMarkdown(Request $request, Response $response, $args) - { - /* get params from call */ - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - - # set structure - if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - - /* set item */ - if(!$this->setItem()){ return $response->withJson($this->errors, 404); } - - # set the status for published and drafted - $this->setPublishStatus(); - - # set path - $this->setItemPath($this->item->fileType); - - # read content from file - if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - - $content = $this->content; - - if($content == '') - { - $content = []; - } - - # if content is not an array, then transform it - if(!is_array($content)) - { - # initialize parsedown extension - $parsedown = new ParsedownExtension(); - - # turn markdown into an array of markdown-blocks - $content = $parsedown->markdownToArrayBlocks($content); - } - - # delete markdown from title - if(isset($content[0])) - { - $content[0] = trim($content[0], "# "); - } - - return $response->withJson(array('data' => $content, 'errors' => false)); - } - - public function getArticleHtml(Request $request, Response $response, $args) - { - /* get params from call */ - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - - # set structure - if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - - /* set item */ - if(!$this->setItem()){ return $response->withJson($this->errors, 404); } - - # set the status for published and drafted - $this->setPublishStatus(); - - # set path - $this->setItemPath($this->item->fileType); - - # read content from file - if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - - $content = $this->content; - - if($content == '') - { - $content = []; - } - - # initialize parsedown extension - $parsedown = new ParsedownExtension(); - - # if content is not an array, then transform it - if(!is_array($content)) - { - # turn markdown into an array of markdown-blocks - $content = $parsedown->markdownToArrayBlocks($content); - } - - # needed for ToC links - $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel; - - # flag for TOC - $toc = false; - - # loop through mardkown-array and create html-blocks - foreach($content as $key => $block) - { - # parse markdown-file to content-array - $contentArray = $parsedown->text($block); - - if($block == '[TOC]') - { - $toc = $key; - } - - # parse markdown-content-array to content-string - $content[$key] = ['id' => $key, 'html' => $parsedown->markup($contentArray, $relurl)]; - } - - if($toc) - { - $tocMarkup = $parsedown->buildTOC($parsedown->headlines); - $content[$toc] = ['id' => $toc, 'html' => $tocMarkup]; - } - - /* - $footnotes = $parsedown->getFootnotes(); - - print_r($footnotes); - die(); - */ - - return $response->withJson(array('data' => $content, 'errors' => false)); - } - - public function addBlock(Request $request, Response $response, $args) - { - /* get params from call */ - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - - /* validate input */ - if(!$this->validateBlockInput()){ return $response->withJson($this->errors,422); } - - # set structure - if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - - /* set item */ - if(!$this->setItem()){ return $response->withJson($this->errors, 404); } - - # set the status for published and drafted - $this->setPublishStatus(); - - # set path - $this->setItemPath($this->item->fileType); - - # read content from file - if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - - # make it more clear which content we have - $pageMarkdown = $this->content; - - $blockMarkdown = $this->params['markdown']; - - # standardize line breaks - $blockMarkdown = str_replace(array("\r\n", "\r"), "\n", $blockMarkdown); - - # remove surrounding line breaks - $blockMarkdown = trim($blockMarkdown, "\n"); - - if($pageMarkdown == '') - { - $pageMarkdown = []; - } - - # initialize parsedown extension - $parsedown = new ParsedownExtension(); - - # if content is not an array, then transform it - if(!is_array($pageMarkdown)) - { - # turn markdown into an array of markdown-blocks - $pageMarkdown = $parsedown->markdownToArrayBlocks($pageMarkdown); - } - - # if it is a new content-block - if($this->params['block_id'] == 99999) - { - # set the id of the markdown-block (it will be one more than the actual array, so count is perfect) - $id = count($pageMarkdown); - - # add the new markdown block to the page content - $pageMarkdown[] = $blockMarkdown; - } - elseif(($this->params['block_id'] == 0) OR !isset($pageMarkdown[$this->params['block_id']])) - { - # if the block does not exists, return an error - return $response->withJson(array('data' => false, 'errors' => 'The ID of the content-block is wrong.'), 404); - } - else - { - # insert new markdown block - array_splice( $pageMarkdown, $this->params['block_id'], 0, $blockMarkdown ); - $id = $this->params['block_id']; - } - - # encode the content into json - $pageJson = json_encode($pageMarkdown); - - # set path for the file (or folder) - $this->setItemPath('txt'); - - /* update the file */ - if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson)) - { - # update the internal structure - $this->setStructure($draft = true, $cache = false); - $this->content = $pageMarkdown; - } - else - { - return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404); - } - - /* set safe mode to escape javascript and html in markdown */ - $parsedown->setSafeMode(true); - - /* parse markdown-file to content-array */ - $blockArray = $parsedown->text($blockMarkdown); - - # we assume that toc is not relevant - $toc = false; - - # needed for ToC links - $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel; - - if($blockMarkdown == '[TOC]') - { - # if block is table of content itself, then generate the table of content - $tableofcontent = $this->generateToc(); - - # and only use the html-markup - $blockHTML = $tableofcontent['html']; - } - else - { - # parse markdown-content-array to content-string - $blockHTML = $parsedown->markup($blockArray, $relurl); - - # if it is a headline - if($blockMarkdown[0] == '#') - { - # then the TOC holds either false (if no toc used in the page) or it holds an object with the id and toc-markup - $toc = $this->generateToc(); - } - } - - return $response->withJson(array('content' => [ 'id' => $id, 'html' => $blockHTML ] , 'markdown' => $blockMarkdown, 'id' => $id, 'toc' => $toc, 'errors' => false)); - } - - protected function generateToc() - { - # we assume that page has no table of content - $toc = false; - - # make sure $this->content is updated - $content = $this->content; - - if($content == '') - { - $content = []; - } - - # initialize parsedown extension - $parsedown = new ParsedownExtension(); - - # if content is not an array, then transform it - if(!is_array($content)) - { - # turn markdown into an array of markdown-blocks - $content = $parsedown->markdownToArrayBlocks($content); - } - - # needed for ToC links - $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel; - - # loop through mardkown-array and create html-blocks - foreach($content as $key => $block) - { - # parse markdown-file to content-array - $contentArray = $parsedown->text($block); - - if($block == '[TOC]') - { - # toc is true and holds the key of the table of content now - $toc = $key; - } - - # parse markdown-content-array to content-string - $content[$key] = ['id' => $key, 'html' => $parsedown->markup($contentArray, $relurl)]; - } - - # if page has a table of content - if($toc) - { - # generate the toc markup - $tocMarkup = $parsedown->buildTOC($parsedown->headlines); - - # toc holds the id of the table of content and the html-markup now - $toc = ['id' => $toc, 'html' => $tocMarkup]; - } - - return $toc; - } - - public function updateBlock(Request $request, Response $response, $args) - { - /* get params from call */ - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - - /* validate input */ - if(!$this->validateBlockInput()){ return $response->withJson($this->errors,422); } - - # set structure - if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - - /* set item */ - if(!$this->setItem()){ return $response->withJson($this->errors, 404); } - - # set the status for published and drafted - $this->setPublishStatus(); - - # set path - $this->setItemPath($this->item->fileType); - - # read content from file - if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - - # make it more clear which content we have - $pageMarkdown = $this->content; - - $blockMarkdown = $this->params['markdown']; - - # standardize line breaks - $blockMarkdown = str_replace(array("\r\n", "\r"), "\n", $blockMarkdown); - - # remove surrounding line breaks - $blockMarkdown = trim($blockMarkdown, "\n"); - - if($pageMarkdown == '') - { - $pageMarkdown = []; - } - - # initialize parsedown extension - $parsedown = new ParsedownExtension(); - - # if content is not an array, then transform it - if(!is_array($pageMarkdown)) - { - # turn markdown into an array of markdown-blocks - $pageMarkdown = $parsedown->markdownToArrayBlocks($pageMarkdown); - } - - if(!isset($pageMarkdown[$this->params['block_id']])) - { - # if the block does not exists, return an error - return $response->withJson(array('data' => false, 'errors' => 'The ID of the content-block is wrong.'), 404); - } - elseif($this->params['block_id'] == 0) - { - # if it is the title, then delete the "# " if it exists - $blockMarkdown = trim($blockMarkdown, "# "); - - # store the markdown-headline in a separate variable - $blockMarkdownTitle = '# ' . $blockMarkdown; - - # add the markdown-headline to the page-markdown - $pageMarkdown[0] = $blockMarkdownTitle; - $id = 0; - } - else - { - # update the markdown block in the page content - $pageMarkdown[$this->params['block_id']] = $blockMarkdown; - $id = $this->params['block_id']; - } - - # encode the content into json - $pageJson = json_encode($pageMarkdown); - - # set path for the file (or folder) - $this->setItemPath('txt'); - - /* update the file */ - if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson)) - { - # update the internal structure - $this->setStructure($draft = true, $cache = false); - - # updated the content variable - $this->content = $pageMarkdown; - } - else - { - return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404); - } - - /* set safe mode to escape javascript and html in markdown */ - $parsedown->setSafeMode(true); - - /* parse markdown-file to content-array, if title parse title. */ - if($this->params['block_id'] == 0) - { - $blockArray = $parsedown->text($blockMarkdownTitle); - } - else - { - $blockArray = $parsedown->text($blockMarkdown); - } - - # we assume that toc is not relevant - $toc = false; - - # needed for ToC links - $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel; - - if($blockMarkdown == '[TOC]') - { - # if block is table of content itself, then generate the table of content - $tableofcontent = $this->generateToc(); - - # and only use the html-markup - $blockHTML = $tableofcontent['html']; - } - else - { - # parse markdown-content-array to content-string - $blockHTML = $parsedown->markup($blockArray, $relurl); - - # if it is a headline - if($blockMarkdown[0] == '#') - { - # then the TOC holds either false (if no toc used in the page) or it holds an object with the id and toc-markup - $toc = $this->generateToc(); - } - } - - return $response->withJson(array('content' => [ 'id' => $id, 'html' => $blockHTML ] , 'markdown' => $blockMarkdown, 'id' => $id, 'toc' => $toc, 'errors' => false)); - } - - public function moveBlock(Request $request, Response $response, $args) - { - # get params from call - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - - # validate input - # if(!$this->validateBlockInput()){ return $response->withJson($this->errors,422); } - - # set structure - if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - - # set item - if(!$this->setItem()){ return $response->withJson($this->errors, 404); } - - # set the status for published and drafted - $this->setPublishStatus(); - - # set path - $this->setItemPath($this->item->fileType); - - # read content from file - if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - - # make it more clear which content we have - $pageMarkdown = $this->content; - - if($pageMarkdown == '') - { - $pageMarkdown = []; - } - - # initialize parsedown extension - $parsedown = new ParsedownExtension(); - - # if content is not an array, then transform it - if(!is_array($pageMarkdown)) - { - # turn markdown into an array of markdown-blocks - $pageMarkdown = $parsedown->markdownToArrayBlocks($pageMarkdown); - } - - $oldIndex = ($this->params['old_index'] + 1); - $newIndex = ($this->params['new_index'] + 1); - - if(!isset($pageMarkdown[$oldIndex])) - { - # if the block does not exists, return an error - return $response->withJson(array('data' => false, 'errors' => 'The ID of the content-block is wrong.'), 404); - } - - $extract = array_splice($pageMarkdown, $oldIndex, 1); - array_splice($pageMarkdown, $newIndex, 0, $extract); - - # encode the content into json - $pageJson = json_encode($pageMarkdown); - - # set path for the file (or folder) - $this->setItemPath('txt'); - - /* update the file */ - if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson)) - { - # update the internal structure - $this->setStructure($draft = true, $cache = false); - - # update this content - $this->content = $pageMarkdown; - } - else - { - return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404); - } - - # we assume that toc is not relevant - $toc = false; - - # needed for ToC links - $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel; - - # if the moved item is a headline - if($extract[0][0] == '#') - { - $toc = $this->generateToc(); - } - - # if it is the title, then delete the "# " if it exists - $pageMarkdown[0] = trim($pageMarkdown[0], "# "); - - return $response->withJson(array('markdown' => $pageMarkdown, 'toc' => $toc, 'errors' => false)); - } - - public function deleteBlock(Request $request, Response $response, $args) - { - /* get params from call */ - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - $errors = false; - - # set structure - if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - - # set item - if(!$this->setItem()){ return $response->withJson($this->errors, 404); } - - # set the status for published and drafted - $this->setPublishStatus(); - - # set path - $this->setItemPath($this->item->fileType); - - # read content from file - if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } - - # get content - $this->content; - - if($this->content == '') - { - $this->content = []; - } - - # initialize parsedown extension - $parsedown = new ParsedownExtension(); - - # if content is not an array, then transform it - if(!is_array($this->content)) - { - # turn markdown into an array of markdown-blocks - $this->content = $parsedown->markdownToArrayBlocks($this->content); - } - - # check if id exists - if(!isset($this->content[$this->params['block_id']])){ return $response->withJson(array('data' => false, 'errors' => 'The ID of the content-block is wrong.'), 404); } - - # check if block is image - $contentBlock = $this->content[$this->params['block_id']]; - $contentBlockStart = substr($contentBlock, 0, 2); - if($contentBlockStart == '[!' OR $contentBlockStart == '![') - { - # extract image path - preg_match("/\((.*?)\)/",$contentBlock,$matches); - if(isset($matches[1])) - { - $imageBaseName = explode('-', $matches[1]); - $imageBaseName = str_replace('media/live/', '', $imageBaseName[0]); - $processImage = new ProcessImage(); - if(!$processImage->deleteImage($imageBaseName)) - { - $errors = 'Could not delete some of the images, please check manually'; - } - } - } - - # delete the block - unset($this->content[$this->params['block_id']]); - $this->content = array_values($this->content); - - $pageMarkdown = $this->content; - - # delete markdown from title - if(isset($pageMarkdown[0])) - { - $pageMarkdown[0] = trim($pageMarkdown[0], "# "); - } - - # encode the content into json - $pageJson = json_encode($this->content); - - # set path for the file (or folder) - $this->setItemPath('txt'); - - /* update the file */ - if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson)) - { - # update the internal structure - $this->setStructure($draft = true, $cache = false); - } - else - { - return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404); - } - - $toc = false; - - if($contentBlock[0] == '#') - { - $toc = $this->generateToc(); - } - - return $response->withJson(array('markdown' => $pageMarkdown, 'toc' => $toc, 'errors' => $errors)); - } - - public function createImage(Request $request, Response $response, $args) - { - /* get params from call */ - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - - $imageProcessor = new ProcessImage(); - - if($imageProcessor->createImage($this->params['image'], $this->settings['images'])) - { - return $response->withJson(array('errors' => false)); - } - - return $response->withJson(array('errors' => 'could not store image to temporary folder')); - } - - public function publishImage(Request $request, Response $response, $args) - { - $params = $request->getParsedBody(); - - $imageProcessor = new ProcessImage(); - - $imageUrl = $imageProcessor->publishImage($this->settings['images'], $name = false); - if($imageUrl) - { - $params['markdown'] = str_replace('imgplchldr', $imageUrl, $params['markdown']); - - $request = $request->withParsedBody($params); - - return $this->addBlock($request, $response, $args); - } - - return $response->withJson(array('errors' => 'could not store image to media folder')); - } - - public function saveVideoImage(Request $request, Response $response, $args) - { - /* get params from call */ - $this->params = $request->getParams(); - $this->uri = $request->getUri(); - $class = false; - - $imageUrl = $this->params['markdown']; - - if(strpos($imageUrl, 'https://www.youtube.com/watch?v=') !== false) - { - $videoID = str_replace('https://www.youtube.com/watch?v=', '', $imageUrl); - $videoID = strpos($videoID, '&') ? substr($videoID, 0, strpos($videoID, '&')) : $videoID; - $class = 'youtube'; - } - if(strpos($imageUrl, 'https://youtu.be/') !== false) - { - $videoID = str_replace('https://youtu.be/', '', $imageUrl); - $videoID = strpos($videoID, '?') ? substr($videoID, 0, strpos($videoID, '?')) : $videoID; - $class = 'youtube'; - } - - if($class == 'youtube') - { - $videoURLmaxres = 'https://i1.ytimg.com/vi/' . $videoID . '/maxresdefault.jpg'; - $videoURL0 = 'https://i1.ytimg.com/vi/' . $videoID . '/0.jpg'; - } - - $ctx = stream_context_create(array( - 'https' => array( - 'timeout' => 1 - ) - ) - ); - - $imageData = @file_get_contents($videoURLmaxres, 0, $ctx); - if($imageData === false) - { - $imageData = @file_get_contents($videoURL0, 0, $ctx); - if($imageData === false) - { - return $response->withJson(array('errors' => 'could not get the video image')); - } - } - - $imageData64 = 'data:image/jpeg;base64,' . base64_encode($imageData); - $desiredSizes = ['live' => ['width' => 560, 'height' => 315]]; - $imageProcessor = new ProcessImage(); - $tmpImage = $imageProcessor->createImage($imageData64, $desiredSizes); - - if(!$tmpImage) - { - return $response->withJson(array('errors' => 'could not create temporary image')); - } - - $imageUrl = $imageProcessor->publishImage($desiredSizes, $videoID); - if($imageUrl) - { - $this->params['markdown'] = '![' . $class . '-video](' . $imageUrl . ' "click to load video"){#' . $videoID. ' .' . $class . '}'; - - $request = $request->withParsedBody($this->params); - - return $this->addBlock($request, $response, $args); - } - - return $response->withJson(array('errors' => 'could not store the preview image')); - } +params = $request->getParams(); + $this->uri = $request->getUri(); + + # validate input only if raw mode + if($this->params['raw']) + { + if(!$this->validateEditorInput()){ return $response->withJson($this->errors,422); } + } + + # set structure + if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); } + + # set item + if(!$this->setItem()){ return $response->withJson($this->errors, 404); } + + # set the status for published and drafted + $this->setPublishStatus(); + + # set path + $this->setItemPath($this->item->fileType); + + # if raw mode, use the content from request + if($this->params['raw']) + { + $this->content = '# ' . $this->params['title'] . "\r\n\r\n" . $this->params['content']; + } + else + { + # read content from file + if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } + + # If it is a draft, then create clean markdown content + if(is_array($this->content)) + { + # initialize parsedown extension + $parsedown = new ParsedownExtension(); + + # turn markdown into an array of markdown-blocks + $this->content = $parsedown->arrayBlocksToMarkdown($this->content); + } + } + + # set path for the file (or folder) + $this->setItemPath('md'); + + # update the file + if($this->write->writeFile($this->settings['contentFolder'], $this->path, $this->content)) + { + # update the file + $delete = $this->deleteContentFiles(['txt']); + + # update the internal structure + $this->setStructure($draft = true, $cache = false); + + # update the public structure + $this->setStructure($draft = false, $cache = false); + + # dispatch event + $this->c->dispatcher->dispatch('onPagePublished', new OnPagePublished($this->item)); + + return $response->withJson(['success'], 200); + } + else + { + return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404); + } + } + + public function unpublishArticle(Request $request, Response $response, $args) + { + # get params from call + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + + # set structure + if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); } + + # set item + if(!$this->setItem()){ return $response->withJson($this->errors, 404); } + + # set the status for published and drafted + $this->setPublishStatus(); + + # check if draft exists, if not, create one. + if(!$this->item->drafted) + { + # set path for the file (or folder) + $this->setItemPath('md'); + + # set content of markdown-file + if(!$this->setContent()){ return $response->withJson($this->errors, 404); } + + # initialize parsedown extension + $parsedown = new ParsedownExtension(); + + # turn markdown into an array of markdown-blocks + $contentArray = $parsedown->markdownToArrayBlocks($this->content); + + # encode the content into json + $contentJson = json_encode($contentArray); + + # set path for the file (or folder) + $this->setItemPath('txt'); + + /* update the file */ + if(!$this->write->writeFile($this->settings['contentFolder'], $this->path, $contentJson)) + { + return $response->withJson(['errors' => ['message' => 'Could not create a draft of the page. Please check if the folder is writable']], 404); + } + } + + # update the file + $delete = $this->deleteContentFiles(['md']); + + if($delete) + { + # update the internal structure + $this->setStructure($draft = true, $cache = false); + + # update the live structure + $this->setStructure($draft = false, $cache = false); + + # dispatch event + $this->c->dispatcher->dispatch('onPageUnpublished', new OnPageUnpublished($this->item)); + + return $response->withJson(['success'], 200); + } + else + { + return $response->withJson(['errors' => ['message' => "Could not delete some files. Please check if the files exists and are writable"]], 404); + } + } + + public function deleteArticle(Request $request, Response $response, $args) + { + # get params from call + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + + # set url to base path initially + $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor']; + + # set structure + if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); } + + # set item + if(!$this->setItem()){ return $response->withJson($this->errors, 404); } + + if($this->item->elementType == 'file') + { + $delete = $this->deleteContentFiles(['md','txt']); + } + elseif($this->item->elementType == 'folder') + { + $delete = $this->deleteContentFolder(); + } + + if($delete) + { + # check if it is a subfile or subfolder and set the redirect-url to the parent item + if(count($this->item->keyPathArray) > 1) + { + # get the parent item + $parentItem = Folder::getParentItem($this->structure, $this->item->keyPathArray); + + if($parentItem) + { + # an active file has been moved to another folder + $url .= $parentItem->urlRelWoF; + } + } + + # update the live structure + $this->setStructure($draft = false, $cache = false); + + #update the backend structure + $this->setStructure($draft = true, $cache = false); + + # dispatch event + $this->c->dispatcher->dispatch('onPageDeleted', new OnPageDeleted($this->item)); + + return $response->withJson(array('data' => $this->structure, 'errors' => false, 'url' => $url), 200); + } + else + { + return $response->withJson(array('data' => $this->structure, 'errors' => $this->errors), 404); + } + } + + public function updateArticle(Request $request, Response $response, $args) + { + # get params from call + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + + # validate input + if(!$this->validateEditorInput()){ return $response->withJson($this->errors,422); } + + # set structure + if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); } + + # set item + if(!$this->setItem()){ return $response->withJson($this->errors, 404); } + + # set path for the file (or folder) + $this->setItemPath('txt'); + + # merge title with content for complete markdown document + $updatedContent = '# ' . $this->params['title'] . "\r\n\r\n" . $this->params['content']; + + # initialize parsedown extension + $parsedown = new ParsedownExtension(); + + # turn markdown into an array of markdown-blocks + $contentArray = $parsedown->markdownToArrayBlocks($updatedContent); + + # encode the content into json + $contentJson = json_encode($contentArray); + + /* update the file */ + if($this->write->writeFile($this->settings['contentFolder'], $this->path, $contentJson)) + { + # update the internal structure + $this->setStructure($draft = true, $cache = false); + + return $response->withJson(['success'], 200); + } + else + { + return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404); + } + } + + public function sortArticle(Request $request, Response $response, $args) + { + # get params from call + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + + # url is only needed, if an active page is moved to another folder, so user has to be redirected to the new url + $url = false; + + # set structure + if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); } + + # validate input + if(!$this->validateNavigationSort()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Data not valid. Please refresh the page and try again.', 'url' => $url), 422); } + + # get the ids (key path) for item, old folder and new folder + $itemKeyPath = explode('.', $this->params['item_id']); + $parentKeyFrom = explode('.', $this->params['parent_id_from']); + $parentKeyTo = explode('.', $this->params['parent_id_to']); + + # get the item from structure + $item = Folder::getItemWithKeyPath($this->structure, $itemKeyPath); + + if(!$item){ return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not find this page. Please refresh and try again.', 'url' => $url), 404); } + + # if a folder is moved on the first level + if($this->params['parent_id_from'] == 'navi') + { + # create empty and default values so that the logic below still works + $newFolder = new \stdClass(); + $newFolder->path = ''; + $folderContent = $this->structure; + } + else + { + # get the target folder from structure + $newFolder = Folder::getItemWithKeyPath($this->structure, $parentKeyTo); + + # get the content of the target folder + $folderContent = $newFolder->folderContent; + } + + # if the item has been moved within the same folder + if($this->params['parent_id_from'] == $this->params['parent_id_to']) + { + # get key of item + $itemKey = end($itemKeyPath); + reset($itemKeyPath); + + # delete item from folderContent + unset($folderContent[$itemKey]); + } + elseif($this->params['active'] == 'active') + { + # an active file has been moved to another folder, so send new url with response + $url = $this->uri->getBaseUrl() . '/tm/content/' . $this->settings['editor'] . $newFolder->urlRelWoF . '/' . $item->slug; + } + + # add item to newFolder + array_splice($folderContent, $this->params['index_new'], 0, array($item)); + + # initialize index + $index = 0; + + # initialise write object + $write = new Write(); + + # iterate through the whole content of the new folder to rename the files + $writeError = false; + foreach($folderContent as $folderItem) + { + if(!$write->moveElement($folderItem, $newFolder->path, $index)) + { + $writeError = true; + } + $index++; + } + if($writeError){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); } + + # update the structure for editor + $this->setStructure($draft = true, $cache = false); + + # get item for url and set it active again + if(isset($this->params['url'])) + { + $activeItem = Folder::getItemForUrl($this->structure, $this->params['url']); + } + + # keep the internal structure for response + $internalStructure = $this->structure; + + # update the structure for website + $this->setStructure($draft = false, $cache = false); + + # dispatch event + $this->c->dispatcher->dispatch('onPageSorted', new OnPageSorted($this->params)); + + return $response->withJson(array('data' => $internalStructure, 'errors' => false, 'url' => $url)); + } + + public function createArticle(Request $request, Response $response, $args) + { + # get params from call + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + + # url is only needed, if an active page is moved + $url = false; + + # set structure + if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); } + + # validate input + if(!$this->validateNaviItem()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Special Characters not allowed. Length between 1 and 20 chars.', 'url' => $url), 422); } + + # get the ids (key path) for item, old folder and new folder + $folderKeyPath = explode('.', $this->params['folder_id']); + + # get the item from structure + $folder = Folder::getItemWithKeyPath($this->structure, $folderKeyPath); + + if(!$folder){ return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not find this page. Please refresh and try again.', 'url' => $url), 404); } + + # Rename all files within the folder to make sure, that namings and orders are correct + # get the content of the target folder + $folderContent = $folder->folderContent; + + # create the name for the new item + $nameParts = Folder::getStringParts($this->params['item_name']); + $name = implode("-", $nameParts); + $slug = $name; + + # initialize index + $index = 0; + + # initialise write object + $write = new Write(); + + # iterate through the whole content of the new folder + $writeError = false; + + foreach($folderContent as $folderItem) + { + # check, if the same name as new item, then return an error + if($folderItem->slug == $slug) + { + return $response->withJson(array('data' => $this->structure, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404); + } + + if(!$write->moveElement($folderItem, $folder->path, $index)) + { + $writeError = true; + } + $index++; + } + + if($writeError){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); } + + # add prefix number to the name + $namePath = $index > 9 ? $index . '-' . $name : '0' . $index . '-' . $name; + $folderPath = 'content' . $folder->path; + + # create default content + $content = json_encode(['# Add Title', 'Add Content']); + + if($this->params['type'] == 'file') + { + if(!$write->writeFile($folderPath, $namePath . '.txt', $content)) + { + return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the file. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); + } + } + elseif($this->params['type'] == 'folder') + { + if(!$write->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath)) + { + return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); + } + $write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content); + } + + # update the structure for editor + $this->setStructure($draft = true, $cache = false); + + # get item for url and set it active again + if(isset($this->params['url'])) + { + $activeItem = Folder::getItemForUrl($this->structure, $this->params['url']); + } + + # activate this if you want to redirect after creating the page... + # $url = $this->uri->getBaseUrl() . '/tm/content' . $folder->urlRelWoF . '/' . $name; + + return $response->withJson(array('data' => $this->structure, 'errors' => false, 'url' => $url)); + } + + public function createBaseFolder(Request $request, Response $response, $args) + { + # get params from call + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + + # url is only needed, if an active page is moved + $url = false; + + # set structure + if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); } + + # validate input + #if(!$this->validateBaseFolder()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Special Characters not allowed. Length between 1 and 20 chars.', 'url' => $url), 422); } + + # create the name for the new item + $nameParts = Folder::getStringParts($this->params['item_name']); + $name = implode("-", $nameParts); + $slug = $name; + + # initialize index + $index = 0; + + # initialise write object + $write = new Write(); + + # iterate through the whole content of the new folder + $writeError = false; + + foreach($this->structure as $folder) + { + # check, if the same name as new item, then return an error + if($folder->slug == $slug) + { + return $response->withJson(array('data' => $this->structure, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404); + } + + if(!$write->moveElement($folder, '', $index)) + { + $writeError = true; + } + $index++; + } + + if($writeError){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); } + + # add prefix number to the name + $namePath = $index > 9 ? $index . '-' . $name : '0' . $index . '-' . $name; + $folderPath = 'content'; + + if(!$write->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath)) + { + return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); + } + + # create default content + $content = json_encode(['# Add Title', 'Add Content']); + + $write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', $content); + + # update the structure for editor + $this->setStructure($draft = true, $cache = false); + + # get item for url and set it active again + if(isset($this->params['url'])) + { + $activeItem = Folder::getItemForUrl($this->structure, $this->params['url']); + } + + return $response->withJson(array('data' => $this->structure, 'errors' => false, 'url' => $url)); + } + + public function getNavigation(Request $request, Response $response, $args) + { + # get params from call + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + + # set structure + if(!$this->setStructure($draft = true, $cache = false)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); } + + # set information for homepage + $this->setHomepage(); + + # get item for url and set it active again + if(isset($this->params['url'])) + { + $activeItem = Folder::getItemForUrl($this->structure, $this->params['url']); + } + + return $response->withJson(array('data' => $this->structure, 'homepage' => $this->homepage, 'errors' => false)); + } + + public function getArticleMarkdown(Request $request, Response $response, $args) + { + /* get params from call */ + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + + # set structure + if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } + + /* set item */ + if(!$this->setItem()){ return $response->withJson($this->errors, 404); } + + # set the status for published and drafted + $this->setPublishStatus(); + + # set path + $this->setItemPath($this->item->fileType); + + # read content from file + if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } + + $content = $this->content; + + if($content == '') + { + $content = []; + } + + # if content is not an array, then transform it + if(!is_array($content)) + { + # initialize parsedown extension + $parsedown = new ParsedownExtension(); + + # turn markdown into an array of markdown-blocks + $content = $parsedown->markdownToArrayBlocks($content); + } + + # delete markdown from title + if(isset($content[0])) + { + $content[0] = trim($content[0], "# "); + } + + return $response->withJson(array('data' => $content, 'errors' => false)); + } + + public function getArticleHtml(Request $request, Response $response, $args) + { + /* get params from call */ + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + + # set structure + if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } + + /* set item */ + if(!$this->setItem()){ return $response->withJson($this->errors, 404); } + + # set the status for published and drafted + $this->setPublishStatus(); + + # set path + $this->setItemPath($this->item->fileType); + + # read content from file + if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } + + $content = $this->content; + + if($content == '') + { + $content = []; + } + + # initialize parsedown extension + $parsedown = new ParsedownExtension(); + + # fix footnotes in parsedown, might break with complicated footnotes + $parsedown->setVisualMode(); + + # if content is not an array, then transform it + if(!is_array($content)) + { + # turn markdown into an array of markdown-blocks + $content = $parsedown->markdownToArrayBlocks($content); + } + + # needed for ToC links + $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel; + + # flag for TOC + $toc = false; + + # loop through mardkown-array and create html-blocks + foreach($content as $key => $block) + { + # parse markdown-file to content-array + $contentArray = $parsedown->text($block); + + if($block == '[TOC]') + { + $toc = $key; + } + + # parse markdown-content-array to content-string + $content[$key] = ['id' => $key, 'html' => $parsedown->markup($contentArray, $relurl)]; + } + + if($toc) + { + $tocMarkup = $parsedown->buildTOC($parsedown->headlines); + $content[$toc] = ['id' => $toc, 'html' => $tocMarkup]; + } + + return $response->withJson(array('data' => $content, 'errors' => false)); + } + + public function addBlock(Request $request, Response $response, $args) + { + /* get params from call */ + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + + /* validate input */ + if(!$this->validateBlockInput()){ return $response->withJson($this->errors,422); } + + # set structure + if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } + + /* set item */ + if(!$this->setItem()){ return $response->withJson($this->errors, 404); } + + # set the status for published and drafted + $this->setPublishStatus(); + + # set path + $this->setItemPath($this->item->fileType); + + # read content from file + if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } + + # make it more clear which content we have + $pageMarkdown = $this->content; + + $blockMarkdown = $this->params['markdown']; + + # standardize line breaks + $blockMarkdown = str_replace(array("\r\n", "\r"), "\n", $blockMarkdown); + + # remove surrounding line breaks + $blockMarkdown = trim($blockMarkdown, "\n"); + + if($pageMarkdown == '') + { + $pageMarkdown = []; + } + + # initialize parsedown extension + $parsedown = new ParsedownExtension(); + + # if content is not an array, then transform it + if(!is_array($pageMarkdown)) + { + # turn markdown into an array of markdown-blocks + $pageMarkdown = $parsedown->markdownToArrayBlocks($pageMarkdown); + } + + # if it is a new content-block + if($this->params['block_id'] == 99999) + { + # set the id of the markdown-block (it will be one more than the actual array, so count is perfect) + $id = count($pageMarkdown); + + # add the new markdown block to the page content + $pageMarkdown[] = $blockMarkdown; + } + elseif(($this->params['block_id'] == 0) OR !isset($pageMarkdown[$this->params['block_id']])) + { + # if the block does not exists, return an error + return $response->withJson(array('data' => false, 'errors' => 'The ID of the content-block is wrong.'), 404); + } + else + { + # insert new markdown block + array_splice( $pageMarkdown, $this->params['block_id'], 0, $blockMarkdown ); + $id = $this->params['block_id']; + } + + # encode the content into json + $pageJson = json_encode($pageMarkdown); + + # set path for the file (or folder) + $this->setItemPath('txt'); + + /* update the file */ + if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson)) + { + # update the internal structure + $this->setStructure($draft = true, $cache = false); + $this->content = $pageMarkdown; + } + else + { + return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404); + } + + /* set safe mode to escape javascript and html in markdown */ + $parsedown->setSafeMode(true); + + /* parse markdown-file to content-array */ + $blockArray = $parsedown->text($blockMarkdown); + + # we assume that toc is not relevant + $toc = false; + + # needed for ToC links + $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel; + + if($blockMarkdown == '[TOC]') + { + # if block is table of content itself, then generate the table of content + $tableofcontent = $this->generateToc(); + + # and only use the html-markup + $blockHTML = $tableofcontent['html']; + } + else + { + # parse markdown-content-array to content-string + $blockHTML = $parsedown->markup($blockArray, $relurl); + + # if it is a headline + if($blockMarkdown[0] == '#') + { + # then the TOC holds either false (if no toc used in the page) or it holds an object with the id and toc-markup + $toc = $this->generateToc(); + } + } + + return $response->withJson(array('content' => [ 'id' => $id, 'html' => $blockHTML ] , 'markdown' => $blockMarkdown, 'id' => $id, 'toc' => $toc, 'errors' => false)); + } + + protected function generateToc() + { + # we assume that page has no table of content + $toc = false; + + # make sure $this->content is updated + $content = $this->content; + + if($content == '') + { + $content = []; + } + + # initialize parsedown extension + $parsedown = new ParsedownExtension(); + + # if content is not an array, then transform it + if(!is_array($content)) + { + # turn markdown into an array of markdown-blocks + $content = $parsedown->markdownToArrayBlocks($content); + } + + # needed for ToC links + $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel; + + # loop through mardkown-array and create html-blocks + foreach($content as $key => $block) + { + # parse markdown-file to content-array + $contentArray = $parsedown->text($block); + + if($block == '[TOC]') + { + # toc is true and holds the key of the table of content now + $toc = $key; + } + + # parse markdown-content-array to content-string + $content[$key] = ['id' => $key, 'html' => $parsedown->markup($contentArray, $relurl)]; + } + + # if page has a table of content + if($toc) + { + # generate the toc markup + $tocMarkup = $parsedown->buildTOC($parsedown->headlines); + + # toc holds the id of the table of content and the html-markup now + $toc = ['id' => $toc, 'html' => $tocMarkup]; + } + + return $toc; + } + + public function updateBlock(Request $request, Response $response, $args) + { + /* get params from call */ + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + + /* validate input */ + if(!$this->validateBlockInput()){ return $response->withJson($this->errors,422); } + + # set structure + if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } + + /* set item */ + if(!$this->setItem()){ return $response->withJson($this->errors, 404); } + + # set the status for published and drafted + $this->setPublishStatus(); + + # set path + $this->setItemPath($this->item->fileType); + + # read content from file + if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } + + # make it more clear which content we have + $pageMarkdown = $this->content; + + $blockMarkdown = $this->params['markdown']; + + # standardize line breaks + $blockMarkdown = str_replace(array("\r\n", "\r"), "\n", $blockMarkdown); + + # remove surrounding line breaks + $blockMarkdown = trim($blockMarkdown, "\n"); + + if($pageMarkdown == '') + { + $pageMarkdown = []; + } + + # initialize parsedown extension + $parsedown = new ParsedownExtension(); + $parsedown->setVisualMode(); + + # if content is not an array, then transform it + if(!is_array($pageMarkdown)) + { + # turn markdown into an array of markdown-blocks + $pageMarkdown = $parsedown->markdownToArrayBlocks($pageMarkdown); + } + + if(!isset($pageMarkdown[$this->params['block_id']])) + { + # if the block does not exists, return an error + return $response->withJson(array('data' => false, 'errors' => 'The ID of the content-block is wrong.'), 404); + } + elseif($this->params['block_id'] == 0) + { + # if it is the title, then delete the "# " if it exists + $blockMarkdown = trim($blockMarkdown, "# "); + + # store the markdown-headline in a separate variable + $blockMarkdownTitle = '# ' . $blockMarkdown; + + # add the markdown-headline to the page-markdown + $pageMarkdown[0] = $blockMarkdownTitle; + $id = 0; + } + else + { + # update the markdown block in the page content + $pageMarkdown[$this->params['block_id']] = $blockMarkdown; + $id = $this->params['block_id']; + } + + # encode the content into json + $pageJson = json_encode($pageMarkdown); + + # set path for the file (or folder) + $this->setItemPath('txt'); + + /* update the file */ + if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson)) + { + # update the internal structure + $this->setStructure($draft = true, $cache = false); + + # updated the content variable + $this->content = $pageMarkdown; + } + else + { + return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404); + } + + /* set safe mode to escape javascript and html in markdown */ + $parsedown->setSafeMode(true); + + /* parse markdown-file to content-array, if title parse title. */ + if($this->params['block_id'] == 0) + { + $blockArray = $parsedown->text($blockMarkdownTitle); + } + else + { + $blockArray = $parsedown->text($blockMarkdown); + } + + # we assume that toc is not relevant + $toc = false; + + # needed for ToC links + $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel; + + if($blockMarkdown == '[TOC]') + { + # if block is table of content itself, then generate the table of content + $tableofcontent = $this->generateToc(); + + # and only use the html-markup + $blockHTML = $tableofcontent['html']; + } + else + { + # parse markdown-content-array to content-string + $blockHTML = $parsedown->markup($blockArray, $relurl); + + # if it is a headline + if($blockMarkdown[0] == '#') + { + # then the TOC holds either false (if no toc used in the page) or it holds an object with the id and toc-markup + $toc = $this->generateToc(); + } + } + + return $response->withJson(array('content' => [ 'id' => $id, 'html' => $blockHTML ] , 'markdown' => $blockMarkdown, 'id' => $id, 'toc' => $toc, 'errors' => false)); + } + + public function moveBlock(Request $request, Response $response, $args) + { + # get params from call + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + + # validate input + # if(!$this->validateBlockInput()){ return $response->withJson($this->errors,422); } + + # set structure + if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } + + # set item + if(!$this->setItem()){ return $response->withJson($this->errors, 404); } + + # set the status for published and drafted + $this->setPublishStatus(); + + # set path + $this->setItemPath($this->item->fileType); + + # read content from file + if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } + + # make it more clear which content we have + $pageMarkdown = $this->content; + + if($pageMarkdown == '') + { + $pageMarkdown = []; + } + + # initialize parsedown extension + $parsedown = new ParsedownExtension(); + + # if content is not an array, then transform it + if(!is_array($pageMarkdown)) + { + # turn markdown into an array of markdown-blocks + $pageMarkdown = $parsedown->markdownToArrayBlocks($pageMarkdown); + } + + $oldIndex = ($this->params['old_index'] + 1); + $newIndex = ($this->params['new_index'] + 1); + + if(!isset($pageMarkdown[$oldIndex])) + { + # if the block does not exists, return an error + return $response->withJson(array('data' => false, 'errors' => 'The ID of the content-block is wrong.'), 404); + } + + $extract = array_splice($pageMarkdown, $oldIndex, 1); + array_splice($pageMarkdown, $newIndex, 0, $extract); + + # encode the content into json + $pageJson = json_encode($pageMarkdown); + + # set path for the file (or folder) + $this->setItemPath('txt'); + + /* update the file */ + if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson)) + { + # update the internal structure + $this->setStructure($draft = true, $cache = false); + + # update this content + $this->content = $pageMarkdown; + } + else + { + return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404); + } + + # we assume that toc is not relevant + $toc = false; + + # needed for ToC links + $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel; + + # if the moved item is a headline + if($extract[0][0] == '#') + { + $toc = $this->generateToc(); + } + + # if it is the title, then delete the "# " if it exists + $pageMarkdown[0] = trim($pageMarkdown[0], "# "); + + return $response->withJson(array('markdown' => $pageMarkdown, 'toc' => $toc, 'errors' => false)); + } + + public function deleteBlock(Request $request, Response $response, $args) + { + /* get params from call */ + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + $errors = false; + + # set structure + if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } + + # set item + if(!$this->setItem()){ return $response->withJson($this->errors, 404); } + + # set the status for published and drafted + $this->setPublishStatus(); + + # set path + $this->setItemPath($this->item->fileType); + + # read content from file + if(!$this->setContent()){ return $response->withJson(array('data' => false, 'errors' => $this->errors), 404); } + + # get content + $this->content; + + if($this->content == '') + { + $this->content = []; + } + + # initialize parsedown extension + $parsedown = new ParsedownExtension(); + + # if content is not an array, then transform it + if(!is_array($this->content)) + { + # turn markdown into an array of markdown-blocks + $this->content = $parsedown->markdownToArrayBlocks($this->content); + } + + # check if id exists + if(!isset($this->content[$this->params['block_id']])){ return $response->withJson(array('data' => false, 'errors' => 'The ID of the content-block is wrong.'), 404); } + + # check if block is image + $contentBlock = $this->content[$this->params['block_id']]; + $contentBlockStart = substr($contentBlock, 0, 2); + if($contentBlockStart == '[!' OR $contentBlockStart == '![') + { + # extract image path + preg_match("/\((.*?)\)/",$contentBlock,$matches); + if(isset($matches[1])) + { + $imageBaseName = explode('-', $matches[1]); + $imageBaseName = str_replace('media/live/', '', $imageBaseName[0]); + $processImage = new ProcessImage(); + if(!$processImage->deleteImage($imageBaseName)) + { + $errors = 'Could not delete some of the images, please check manually'; + } + } + } + + # delete the block + unset($this->content[$this->params['block_id']]); + $this->content = array_values($this->content); + + $pageMarkdown = $this->content; + + # delete markdown from title + if(isset($pageMarkdown[0])) + { + $pageMarkdown[0] = trim($pageMarkdown[0], "# "); + } + + # encode the content into json + $pageJson = json_encode($this->content); + + # set path for the file (or folder) + $this->setItemPath('txt'); + + /* update the file */ + if($this->write->writeFile($this->settings['contentFolder'], $this->path, $pageJson)) + { + # update the internal structure + $this->setStructure($draft = true, $cache = false); + } + else + { + return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404); + } + + $toc = false; + + if($contentBlock[0] == '#') + { + $toc = $this->generateToc(); + } + + return $response->withJson(array('markdown' => $pageMarkdown, 'toc' => $toc, 'errors' => $errors)); + } + + public function createImage(Request $request, Response $response, $args) + { + /* get params from call */ + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + + $imageProcessor = new ProcessImage(); + + if($imageProcessor->createImage($this->params['image'], $this->settings['images'])) + { + return $response->withJson(array('errors' => false)); + } + + return $response->withJson(array('errors' => 'could not store image to temporary folder')); + } + + public function publishImage(Request $request, Response $response, $args) + { + $params = $request->getParsedBody(); + + $imageProcessor = new ProcessImage(); + + $imageUrl = $imageProcessor->publishImage($this->settings['images'], $name = false); + if($imageUrl) + { + $params['markdown'] = str_replace('imgplchldr', $imageUrl, $params['markdown']); + + $request = $request->withParsedBody($params); + + return $this->addBlock($request, $response, $args); + } + + return $response->withJson(array('errors' => 'could not store image to media folder')); + } + + public function saveVideoImage(Request $request, Response $response, $args) + { + /* get params from call */ + $this->params = $request->getParams(); + $this->uri = $request->getUri(); + $class = false; + + $imageUrl = $this->params['markdown']; + + if(strpos($imageUrl, 'https://www.youtube.com/watch?v=') !== false) + { + $videoID = str_replace('https://www.youtube.com/watch?v=', '', $imageUrl); + $videoID = strpos($videoID, '&') ? substr($videoID, 0, strpos($videoID, '&')) : $videoID; + $class = 'youtube'; + } + if(strpos($imageUrl, 'https://youtu.be/') !== false) + { + $videoID = str_replace('https://youtu.be/', '', $imageUrl); + $videoID = strpos($videoID, '?') ? substr($videoID, 0, strpos($videoID, '?')) : $videoID; + $class = 'youtube'; + } + + if($class == 'youtube') + { + $videoURLmaxres = 'https://i1.ytimg.com/vi/' . $videoID . '/maxresdefault.jpg'; + $videoURL0 = 'https://i1.ytimg.com/vi/' . $videoID . '/0.jpg'; + } + + $ctx = stream_context_create(array( + 'https' => array( + 'timeout' => 1 + ) + ) + ); + + $imageData = @file_get_contents($videoURLmaxres, 0, $ctx); + if($imageData === false) + { + $imageData = @file_get_contents($videoURL0, 0, $ctx); + if($imageData === false) + { + return $response->withJson(array('errors' => 'could not get the video image')); + } + } + + $imageData64 = 'data:image/jpeg;base64,' . base64_encode($imageData); + $desiredSizes = ['live' => ['width' => 560, 'height' => 315]]; + $imageProcessor = new ProcessImage(); + $tmpImage = $imageProcessor->createImage($imageData64, $desiredSizes); + + if(!$tmpImage) + { + return $response->withJson(array('errors' => 'could not create temporary image')); + } + + $imageUrl = $imageProcessor->publishImage($desiredSizes, $videoID); + if($imageUrl) + { + $this->params['markdown'] = '![' . $class . '-video](' . $imageUrl . ' "click to load video"){#' . $videoID. ' .' . $class . '}'; + + $request = $request->withParsedBody($this->params); + + return $this->addBlock($request, $response, $args); + } + + return $response->withJson(array('errors' => 'could not store the preview image')); + } } \ No newline at end of file diff --git a/system/Controllers/ContentBackendController.php b/system/Controllers/ContentBackendController.php index de0e5be..e8278f2 100644 --- a/system/Controllers/ContentBackendController.php +++ b/system/Controllers/ContentBackendController.php @@ -1,160 +1,163 @@ -uri = $request->getUri(); - $this->params = isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()]; - - # set structure - if(!$this->setStructure($draft = true)){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); } - - # set information for homepage - $this->setHomepage(); - - # set item - if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); } - - # get the breadcrumb (here we need it only to mark the actual item active in navigation) - $breadcrumb = isset($this->item->keyPathArray) ? Folder::getBreadcrumb($this->structure, $this->item->keyPathArray) : false; - - # set the status for published and drafted - $this->setPublishStatus(); - - # set path - $this->setItemPath($this->item->fileType); - - # add the modified date for the file - $this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false; - - # read content from file - if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); } - - $content = $this->content; - $title = false; - - # if content is an array, then it is a draft - if(is_array($content)) - { - # transform array to markdown - $parsedown = new ParsedownExtension(); - $content = $parsedown->arrayBlocksToMarkdown($content); - } - - # if there is content - if($content != '') - { - # normalize linebreaks - $content = str_replace(array("\r\n", "\r"), "\n", $content); - $content = trim($content, "\n"); - - # and strip out title - if($content[0] == '#') - { - $contentParts = explode("\n", $content, 2); - $title = trim($contentParts[0], "# \t\n\r\0\x0B"); - $content = trim($contentParts[1]); - } - } - - return $this->render($response, 'editor/editor-raw.twig', array('navigation' => $this->structure, 'homepage' => $this->homepage, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings )); - } - - /** - * Show Content for blox editor - * - * @param obj $request the slim request object - * @param obj $response the slim response object - * @return obje $response with redirect to route - */ - - public function showBlox(Request $request, Response $response, $args) - { - # get params from call - $this->uri = $request->getUri(); - $this->params = isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()]; - - # set structure - if(!$this->setStructure($draft = true)){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); } - - # set information for homepage - $this->setHomepage(); - - # set item - if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); } - - # set the status for published and drafted - $this->setPublishStatus(); - - # set path - $this->setItemPath($this->item->fileType); - - # add the modified date for the file - $this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false; - - # read content from file - if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); } - - $content = $this->content; - - if($content == '') - { - $content = []; - } - - # initialize parsedown extension - $parsedown = new ParsedownExtension(); - - # if content is not an array, then transform it - if(!is_array($content)) - { - # turn markdown into an array of markdown-blocks - $content = $parsedown->markdownToArrayBlocks($content); - } - - # needed for ToC links - $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel; - - foreach($content as $key => $block) - { - /* parse markdown-file to content-array */ - $contentArray = $parsedown->text($block); - - /* parse markdown-content-array to content-string */ - $content[$key] = $parsedown->markup($contentArray, $relurl); - } - - # extract title and delete from content array, array will start at 1 after that. - $title = '# add title'; - if(isset($content[0])) - { - $title = $content[0]; - unset($content[0]); - } - - return $this->render($response, 'editor/editor-blox.twig', array('navigation' => $this->structure, 'homepage' => $this->homepage, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings )); - } - - public function showEmpty(Request $request, Response $response, $args) - { - return $this->renderIntern404($response, array( 'settings' => $this->settings )); - } +uri = $request->getUri(); + $this->params = isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()]; + + # set structure + if(!$this->setStructure($draft = true)){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); } + + # set information for homepage + $this->setHomepage(); + + # set item + if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); } + + # get the breadcrumb (here we need it only to mark the actual item active in navigation) + $breadcrumb = isset($this->item->keyPathArray) ? Folder::getBreadcrumb($this->structure, $this->item->keyPathArray) : false; + + # set the status for published and drafted + $this->setPublishStatus(); + + # set path + $this->setItemPath($this->item->fileType); + + # add the modified date for the file + $this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false; + + # read content from file + if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); } + + $content = $this->content; + $title = false; + + # if content is an array, then it is a draft + if(is_array($content)) + { + # transform array to markdown + $parsedown = new ParsedownExtension(); + $content = $parsedown->arrayBlocksToMarkdown($content); + } + + # if there is content + if($content != '') + { + # normalize linebreaks + $content = str_replace(array("\r\n", "\r"), "\n", $content); + $content = trim($content, "\n"); + + # and strip out title + if($content[0] == '#') + { + $contentParts = explode("\n", $content, 2); + $title = trim($contentParts[0], "# \t\n\r\0\x0B"); + $content = trim($contentParts[1]); + } + } + + return $this->render($response, 'editor/editor-raw.twig', array('navigation' => $this->structure, 'homepage' => $this->homepage, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings )); + } + + /** + * Show Content for blox editor + * + * @param obj $request the slim request object + * @param obj $response the slim response object + * @return obje $response with redirect to route + */ + + public function showBlox(Request $request, Response $response, $args) + { + # get params from call + $this->uri = $request->getUri(); + $this->params = isset($args['params']) ? ['url' => $this->uri->getBasePath() . '/' . $args['params']] : ['url' => $this->uri->getBasePath()]; + + # set structure + if(!$this->setStructure($draft = true)){ return $this->renderIntern404($response, array( 'navigation' => true, 'content' => $this->errors )); } + + # set information for homepage + $this->setHomepage(); + + # set item + if(!$this->setItem()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); } + + # set the status for published and drafted + $this->setPublishStatus(); + + # set path + $this->setItemPath($this->item->fileType); + + # add the modified date for the file + $this->item->modified = ($this->item->published OR $this->item->drafted) ? filemtime($this->settings['contentFolder'] . $this->path) : false; + + # read content from file + if(!$this->setContent()){ return $this->renderIntern404($response, array( 'navigation' => $this->structure, 'settings' => $this->settings, 'content' => $this->errors )); } + + $content = $this->content; + + if($content == '') + { + $content = []; + } + + # initialize parsedown extension + $parsedown = new ParsedownExtension(); + + # to fix footnote-logic in parsedown, set visual mode to true + $parsedown->setVisualMode(); + + # if content is not an array, then transform it + if(!is_array($content)) + { + # turn markdown into an array of markdown-blocks + $content = $parsedown->markdownToArrayBlocks($content); + } + + # needed for ToC links + $relurl = '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel; + + foreach($content as $key => $block) + { + /* parse markdown-file to content-array */ + $contentArray = $parsedown->text($block); + + /* parse markdown-content-array to content-string */ + $content[$key] = $parsedown->markup($contentArray, $relurl); + } + + # extract title and delete from content array, array will start at 1 after that. + $title = '# add title'; + if(isset($content[0])) + { + $title = $content[0]; + unset($content[0]); + } + + return $this->render($response, 'editor/editor-blox.twig', array('navigation' => $this->structure, 'homepage' => $this->homepage, 'title' => $title, 'content' => $content, 'item' => $this->item, 'settings' => $this->settings )); + } + + public function showEmpty(Request $request, Response $response, $args) + { + return $this->renderIntern404($response, array( 'settings' => $this->settings )); + } } \ No newline at end of file diff --git a/system/Controllers/ContentController.php b/system/Controllers/ContentController.php index 34c7167..e9f350a 100644 --- a/system/Controllers/ContentController.php +++ b/system/Controllers/ContentController.php @@ -1,370 +1,370 @@ -c = $c; - $this->settings = $this->c->get('settings'); - $this->structureLiveName = 'structure.txt'; - $this->structureDraftName = 'structure-draft.txt'; - } - - protected function render($response, $route, $data) - { - if(isset($_SESSION['old'])) - { - unset($_SESSION['old']); - } - - $response = $response->withoutHeader('Server'); - $response = $response->withoutHeader('X-Powered-By'); - - if($this->c->request->getUri()->getScheme() == 'https') - { - $response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000'); - } - - $response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff'); - $response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN'); - $response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block'); - $response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade'); - - return $this->c->view->render($response, $route, $data); - } - - protected function render404($response, $data = NULL) - { - return $this->c->view->render($response->withStatus(404), '/404.twig', $data); - } - - protected function renderIntern404($response, $data = NULL) - { - return $this->c->view->render($response->withStatus(404), '/intern404.twig', $data); - } - - protected function validateEditorInput() - { - $validate = new Validation(); - $vResult = $validate->editorInput($this->params); - - if(is_array($vResult)) - { - $message = reset($vResult); - $this->errors = ['errors' => $vResult]; - if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; } - return false; - } - return true; - } - - protected function validateBlockInput() - { - $validate = new Validation(); - $vResult = $validate->blockInput($this->params); - - if(is_array($vResult)) - { - $message = reset($vResult); - $this->errors = ['errors' => $vResult]; - if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; } - return false; - } - return true; - } - - protected function validateNavigationSort() - { - $validate = new Validation(); - $vResult = $validate->navigationSort($this->params); - - if(is_array($vResult)) - { - $message = reset($vResult); - $this->errors = ['errors' => $vResult]; - if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; } - return false; - } - return true; - } - - protected function validateNaviItem() - { - $validate = new Validation(); - $vResult = $validate->navigationItem($this->params); - - if(is_array($vResult)) - { - $message = reset($vResult); - $this->errors = ['errors' => $vResult]; - if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; } - return false; - } - return true; - } - - protected function setStructure($draft = false, $cache = true) - { - # set initial structure to false - $structure = false; - - # name of structure-file for draft or live - $filename = $draft ? $this->structureDraftName : $this->structureLiveName; - - # set variables and objects - $this->write = new writeCache(); - - # check, if cached structure is still valid - if($cache && $this->write->validate('cache', 'lastCache.txt', 600)) - { - # get the cached structure - $structure = $this->write->getCache('cache', $filename); - } - - # if no structure was found or cache is deactivated - if(!$structure) - { - # scan the content of the folder - $structure = Folder::scanFolder($this->settings['rootPath'] . $this->settings['contentFolder'], $draft); - - # if there is content, then get the content details - if(count($structure) > 0) - { - # create an array of object with the whole content of the folder - $structure = Folder::getFolderContentDetails($structure, $this->uri->getBaseUrl(), $this->uri->getBasePath()); - } - - # cache navigation - $this->write->updateCache('cache', $filename, 'lastCache.txt', $structure); - } - - $this->structure = $structure; - return true; - } - - protected function setHomepage() - { - $contentFolder = Folder::scanFolderFlat($this->settings['rootPath'] . $this->settings['contentFolder']); - - if(in_array('index.md', $contentFolder)) - { - $md = true; - $status = 'published'; - } - if(in_array('index.txt', $contentFolder)) - { - $txt = true; - $status = 'unpublished'; - } - if(isset($txt) && isset($md)) - { - $status = 'modified'; - } - - $active = false; - if($this->params['url'] == '/' || $this->params['url'] == $this->uri->getBasePath() ) - { - $active = 'active'; - } - - $this->homepage = ['status' => $status, 'active' => $active]; - } - - protected function setItem() - { - # if it is the homepage - if($this->params['url'] == $this->uri->getBasePath() OR $this->params['url'] == '/') - { - $item = new \stdClass; - $item->elementType = 'folder'; - $item->path = ''; - $item->urlRel = '/'; - } - else - { - # search for the url in the structure - $item = Folder::getItemForUrl($this->structure, $this->params['url']); - } - - if($item) - { - if($item->elementType == 'file') - { - $pathParts = explode('.', $item->path); - $fileType = array_pop($pathParts); - $pathWithoutType = implode('.', $pathParts); - $item->pathWithoutType = $pathWithoutType; - } - elseif($item->elementType == 'folder') - { - $item->pathWithoutItem = $item->path; - $item->path = $item->path . DIRECTORY_SEPARATOR . 'index'; - $item->pathWithoutType = $item->path; - } - $this->item = $item; - return true; - } - - $this->errors = ['errors' => ['message' => 'requested page-url not found']]; - return false; - } - - # determine if you want to write to published file (md) or to draft (txt) - protected function setItemPath($fileType) - { - $this->path = $this->item->pathWithoutType . '.' . $fileType; - } - - protected function setPublishStatus() - { - $this->item->published = false; - $this->item->drafted = false; - - if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.md')) - { - $this->item->published = true; - - # add file-type in case it is a folder - $this->item->fileType = "md"; - } - - if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.txt')) - { - $this->item->drafted = true; - - # add file-type in case it is a folder - $this->item->fileType = "txt"; - } - - if(!$this->item->drafted && !$this->item->published && $this->item->elementType == "folder") - { - # set txt as default for a folder, so that we can create an index.txt for a folder. - $this->item->fileType = "txt"; - } - } - - protected function deleteContentFiles($fileTypes, $folder = false) - { - $basePath = $this->settings['rootPath'] . $this->settings['contentFolder']; - - foreach($fileTypes as $fileType) - { - if(file_exists($basePath . $this->item->pathWithoutType . '.' . $fileType) && !unlink($basePath . $this->item->pathWithoutType . '.' . $fileType) ) - { - $this->errors = ['message' => 'We could not delete the file, please check, if the file is writable.']; - } - } - - if($this->errors) - { - return false; - } - - return true; - } - - protected function deleteContentFolder() - { - $basePath = $this->settings['rootPath'] . $this->settings['contentFolder']; - $path = $basePath . $this->item->pathWithoutItem; - - if(file_exists($path)) - { - $files = array_diff(scandir($path), array('.', '..')); - - # check if there are folders first, then stop the operation - foreach ($files as $file) - { - if(is_dir(realpath($path) . DIRECTORY_SEPARATOR . $file)) - { - $this->errors = ['message' => 'Please delete the sub-folder first.']; - } - } - - if(!$this->errors) - { - foreach ($files as $file) - { - unlink(realpath($path) . DIRECTORY_SEPARATOR . $file); - } - return rmdir($path); - } - } - return false; - } - - protected function setContent() - { - # if the file exists - if($this->item->published OR $this->item->drafted) - { - $content = $this->write->getFile($this->settings['contentFolder'], $this->path); - if($this->item->fileType == 'txt') - { - # decode the json-draft to an array - $content = json_decode($content); - } - } - elseif($this->item->elementType == "folder") - { - $content = ''; - } - else - { - $this->errors = ['errors' => ['message' => 'requested file not found']]; - return false; - } - - $this->content = $content; - return true; - } +c = $c; + $this->settings = $this->c->get('settings'); + $this->structureLiveName = 'structure.txt'; + $this->structureDraftName = 'structure-draft.txt'; + } + + protected function render($response, $route, $data) + { + if(isset($_SESSION['old'])) + { + unset($_SESSION['old']); + } + + $response = $response->withoutHeader('Server'); + $response = $response->withoutHeader('X-Powered-By'); + + if($this->c->request->getUri()->getScheme() == 'https') + { + $response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000'); + } + + $response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff'); + $response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN'); + $response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block'); + $response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade'); + + return $this->c->view->render($response, $route, $data); + } + + protected function render404($response, $data = NULL) + { + return $this->c->view->render($response->withStatus(404), '/404.twig', $data); + } + + protected function renderIntern404($response, $data = NULL) + { + return $this->c->view->render($response->withStatus(404), '/intern404.twig', $data); + } + + protected function validateEditorInput() + { + $validate = new Validation(); + $vResult = $validate->editorInput($this->params); + + if(is_array($vResult)) + { + $message = reset($vResult); + $this->errors = ['errors' => $vResult]; + if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; } + return false; + } + return true; + } + + protected function validateBlockInput() + { + $validate = new Validation(); + $vResult = $validate->blockInput($this->params); + + if(is_array($vResult)) + { + $message = reset($vResult); + $this->errors = ['errors' => $vResult]; + if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; } + return false; + } + return true; + } + + protected function validateNavigationSort() + { + $validate = new Validation(); + $vResult = $validate->navigationSort($this->params); + + if(is_array($vResult)) + { + $message = reset($vResult); + $this->errors = ['errors' => $vResult]; + if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; } + return false; + } + return true; + } + + protected function validateNaviItem() + { + $validate = new Validation(); + $vResult = $validate->navigationItem($this->params); + + if(is_array($vResult)) + { + $message = reset($vResult); + $this->errors = ['errors' => $vResult]; + if(isset($message[0])){ $this->errors['errors']['message'] = $message[0]; } + return false; + } + return true; + } + + protected function setStructure($draft = false, $cache = true) + { + # set initial structure to false + $structure = false; + + # name of structure-file for draft or live + $filename = $draft ? $this->structureDraftName : $this->structureLiveName; + + # set variables and objects + $this->write = new writeCache(); + + # check, if cached structure is still valid + if($cache && $this->write->validate('cache', 'lastCache.txt', 600)) + { + # get the cached structure + $structure = $this->write->getCache('cache', $filename); + } + + # if no structure was found or cache is deactivated + if(!$structure) + { + # scan the content of the folder + $structure = Folder::scanFolder($this->settings['rootPath'] . $this->settings['contentFolder'], $draft); + + # if there is content, then get the content details + if(count($structure) > 0) + { + # create an array of object with the whole content of the folder + $structure = Folder::getFolderContentDetails($structure, $this->uri->getBaseUrl(), $this->uri->getBasePath()); + } + + # cache navigation + $this->write->updateCache('cache', $filename, 'lastCache.txt', $structure); + } + + $this->structure = $structure; + return true; + } + + protected function setHomepage() + { + $contentFolder = Folder::scanFolderFlat($this->settings['rootPath'] . $this->settings['contentFolder']); + + if(in_array('index.md', $contentFolder)) + { + $md = true; + $status = 'published'; + } + if(in_array('index.txt', $contentFolder)) + { + $txt = true; + $status = 'unpublished'; + } + if(isset($txt) && isset($md)) + { + $status = 'modified'; + } + + $active = false; + if($this->params['url'] == '/' || $this->params['url'] == $this->uri->getBasePath() ) + { + $active = 'active'; + } + + $this->homepage = ['status' => $status, 'active' => $active]; + } + + protected function setItem() + { + # if it is the homepage + if($this->params['url'] == $this->uri->getBasePath() OR $this->params['url'] == '/') + { + $item = new \stdClass; + $item->elementType = 'folder'; + $item->path = ''; + $item->urlRel = '/'; + } + else + { + # search for the url in the structure + $item = Folder::getItemForUrl($this->structure, $this->params['url']); + } + + if($item) + { + if($item->elementType == 'file') + { + $pathParts = explode('.', $item->path); + $fileType = array_pop($pathParts); + $pathWithoutType = implode('.', $pathParts); + $item->pathWithoutType = $pathWithoutType; + } + elseif($item->elementType == 'folder') + { + $item->pathWithoutItem = $item->path; + $item->path = $item->path . DIRECTORY_SEPARATOR . 'index'; + $item->pathWithoutType = $item->path; + } + $this->item = $item; + return true; + } + + $this->errors = ['errors' => ['message' => 'requested page-url not found']]; + return false; + } + + # determine if you want to write to published file (md) or to draft (txt) + protected function setItemPath($fileType) + { + $this->path = $this->item->pathWithoutType . '.' . $fileType; + } + + protected function setPublishStatus() + { + $this->item->published = false; + $this->item->drafted = false; + + if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.md')) + { + $this->item->published = true; + + # add file-type in case it is a folder + $this->item->fileType = "md"; + } + + if(file_exists($this->settings['rootPath'] . $this->settings['contentFolder'] . $this->item->pathWithoutType . '.txt')) + { + $this->item->drafted = true; + + # add file-type in case it is a folder + $this->item->fileType = "txt"; + } + + if(!$this->item->drafted && !$this->item->published && $this->item->elementType == "folder") + { + # set txt as default for a folder, so that we can create an index.txt for a folder. + $this->item->fileType = "txt"; + } + } + + protected function deleteContentFiles($fileTypes, $folder = false) + { + $basePath = $this->settings['rootPath'] . $this->settings['contentFolder']; + + foreach($fileTypes as $fileType) + { + if(file_exists($basePath . $this->item->pathWithoutType . '.' . $fileType) && !unlink($basePath . $this->item->pathWithoutType . '.' . $fileType) ) + { + $this->errors = ['message' => 'We could not delete the file, please check, if the file is writable.']; + } + } + + if($this->errors) + { + return false; + } + + return true; + } + + protected function deleteContentFolder() + { + $basePath = $this->settings['rootPath'] . $this->settings['contentFolder']; + $path = $basePath . $this->item->pathWithoutItem; + + if(file_exists($path)) + { + $files = array_diff(scandir($path), array('.', '..')); + + # check if there are folders first, then stop the operation + foreach ($files as $file) + { + if(is_dir(realpath($path) . DIRECTORY_SEPARATOR . $file)) + { + $this->errors = ['message' => 'Please delete the sub-folder first.']; + } + } + + if(!$this->errors) + { + foreach ($files as $file) + { + unlink(realpath($path) . DIRECTORY_SEPARATOR . $file); + } + return rmdir($path); + } + } + return false; + } + + protected function setContent() + { + # if the file exists + if($this->item->published OR $this->item->drafted) + { + $content = $this->write->getFile($this->settings['contentFolder'], $this->path); + if($this->item->fileType == 'txt') + { + # decode the json-draft to an array + $content = json_decode($content); + } + } + elseif($this->item->elementType == "folder") + { + $content = ''; + } + else + { + $this->errors = ['errors' => ['message' => 'requested file not found']]; + return false; + } + + $this->content = $content; + return true; + } } \ No newline at end of file diff --git a/system/Controllers/Controller.php b/system/Controllers/Controller.php index f9bd274..452553d 100644 --- a/system/Controllers/Controller.php +++ b/system/Controllers/Controller.php @@ -1,50 +1,51 @@ -c = $c; - } - - protected function render($response, $route, $data) - { - // $data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData(); - - if(isset($_SESSION['old'])) - { - unset($_SESSION['old']); - } - - $response = $response->withoutHeader('Server'); - $response = $response->withoutHeader('X-Powered-By'); - - if($this->c->request->getUri()->getScheme() == 'https') - { - $response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000'); - } - - $response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff'); - $response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN'); - $response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block'); - $response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade'); - - return $this->c->view->render($response, $route, $data); - } - - protected function render404($response, $data = NULL) - { - return $this->c->view->render($response->withStatus(404), '/404.twig', $data); - } +c = $c; + } + + protected function render($response, $route, $data) + { + # why commented this out?? + $data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData(); + + if(isset($_SESSION['old'])) + { + unset($_SESSION['old']); + } + + $response = $response->withoutHeader('Server'); + $response = $response->withoutHeader('X-Powered-By'); + + if($this->c->request->getUri()->getScheme() == 'https') + { + $response = $response->withAddedHeader('Strict-Transport-Security', 'max-age=63072000'); + } + + $response = $response->withAddedHeader('X-Content-Type-Options', 'nosniff'); + $response = $response->withAddedHeader('X-Frame-Options', 'SAMEORIGIN'); + $response = $response->withAddedHeader('X-XSS-Protection', '1;mode=block'); + $response = $response->withAddedHeader('Referrer-Policy', 'no-referrer-when-downgrade'); + + return $this->c->view->render($response, $route, $data); + } + + protected function render404($response, $data = NULL) + { + return $this->c->view->render($response->withStatus(404), '/404.twig', $data); + } } \ No newline at end of file diff --git a/system/Controllers/FormController.php b/system/Controllers/FormController.php index 58f570d..0e87c3b 100644 --- a/system/Controllers/FormController.php +++ b/system/Controllers/FormController.php @@ -1,135 +1,135 @@ -isPost()) - { - $params = $request->getParams(); - reset($params); - $pluginName = key($params); - $referer = $request->getHeader('HTTP_REFERER'); - - # simple bot check with honeypot - if(isset($params[$pluginName]['personal-mail'])) - { - if($params[$pluginName]['personal-mail'] != '') - { - $this->c->flash->addMessage('publicform', 'bot'); - return $response->withRedirect($referer[0]); - } - unset($params[$pluginName]['personal-mail']); - } - - #recaptcha check - if(isset($params['g-recaptcha-response'])) - { - $recaptchaApi = 'https://www.google.com/recaptcha/api/siteverify'; - $settings = $this->c->get('settings'); - $secret = isset($settings['plugins'][$pluginName]['recaptcha_secretkey']) ? $settings['plugins'][$pluginName]['recaptcha_secretkey'] : false; - $recaptchaRequest = ['secret' => $secret, 'response' => $params['g-recaptcha-response']]; - - # 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", - 'method' => 'POST', - 'content' => http_build_query($recaptchaRequest), - 'timeout' => 5 - ) - ); - - $context = stream_context_create($options); - $result = file_get_contents($recaptchaApi, false, $context); - $result = json_decode($result); - - if ($result === FALSE || $result->success === FALSE) - { - $this->c->flash->addMessage('publicform', 'bot'); - return $response->withRedirect($referer[0]); - } - } - - if(isset($params[$pluginName])) - { - # validate the user-input - $this->validateInput('plugins', $pluginName, $params[$pluginName]); - } - - # check for errors and redirect to path, if errors found - if(isset($_SESSION['errors'])) - { - $this->c->flash->addMessage('error', 'Please correct the errors'); - return $response->withRedirect($referer[0]); - } - - # clean up and make sure that only validated data are stored - $data = [ $pluginName => $params[$pluginName]]; - - # create write object - $writeYaml = new WriteYaml(); - - # write the form data into yaml file - $writeYaml->updateYaml('settings', 'formdata.yaml', $data); - - # add message and return to original site - $this->c->flash->addMessage('formdata', $pluginName); - return $response->withRedirect($referer[0]); - } - } - - private function validateInput($objectType, $objectName, $userInput) - { - # get settings and start validation - $originalSettings = \Typemill\Settings::getObjectSettings($objectType, $objectName); - $userSettings = \Typemill\Settings::getUserSettings(); - $validate = new Validation(); - - if(isset($originalSettings['public']['fields'])) - { - /* flaten the multi-dimensional array with fieldsets to a one-dimensional array */ - $originalFields = array(); - foreach($originalSettings['public']['fields'] as $fieldName => $fieldValue) - { - if(isset($fieldValue['fields'])) - { - foreach($fieldValue['fields'] as $subFieldName => $subFieldValue) - { - $originalFields[$subFieldName] = $subFieldValue; - } - } - else - { - $originalFields[$fieldName] = $fieldValue; - } - } - - /* take the user input data and iterate over all fields and values */ - foreach($userInput as $fieldName => $fieldValue) - { - /* get the corresponding field definition from original plugin settings */ - $fieldDefinition = isset($originalFields[$fieldName]) ? $originalFields[$fieldName] : false; - - if($fieldDefinition) - { - /* validate user input for this field */ - $validate->objectField($fieldName, $fieldValue, $objectName, $fieldDefinition); - } - if(!$fieldDefinition && $fieldName != 'active') - { - $_SESSION['errors'][$objectName][$fieldName] = array('This field is not defined!'); - } - } - } - } +isPost()) + { + $params = $request->getParams(); + reset($params); + $pluginName = key($params); + $referer = $request->getHeader('HTTP_REFERER'); + + # simple bot check with honeypot + if(isset($params[$pluginName]['personal-mail'])) + { + if($params[$pluginName]['personal-mail'] != '') + { + $this->c->flash->addMessage('publicform', 'bot'); + return $response->withRedirect($referer[0]); + } + unset($params[$pluginName]['personal-mail']); + } + + #recaptcha check + if(isset($params['g-recaptcha-response'])) + { + $recaptchaApi = 'https://www.google.com/recaptcha/api/siteverify'; + $settings = $this->c->get('settings'); + $secret = isset($settings['plugins'][$pluginName]['recaptcha_secretkey']) ? $settings['plugins'][$pluginName]['recaptcha_secretkey'] : false; + $recaptchaRequest = ['secret' => $secret, 'response' => $params['g-recaptcha-response']]; + + # 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", + 'method' => 'POST', + 'content' => http_build_query($recaptchaRequest), + 'timeout' => 5 + ) + ); + + $context = stream_context_create($options); + $result = file_get_contents($recaptchaApi, false, $context); + $result = json_decode($result); + + if ($result === FALSE || $result->success === FALSE) + { + $this->c->flash->addMessage('publicform', 'bot'); + return $response->withRedirect($referer[0]); + } + } + + if(isset($params[$pluginName])) + { + # validate the user-input + $this->validateInput('plugins', $pluginName, $params[$pluginName]); + } + + # check for errors and redirect to path, if errors found + if(isset($_SESSION['errors'])) + { + $this->c->flash->addMessage('error', 'Please correct the errors'); + return $response->withRedirect($referer[0]); + } + + # clean up and make sure that only validated data are stored + $data = [ $pluginName => $params[$pluginName]]; + + # create write object + $writeYaml = new WriteYaml(); + + # write the form data into yaml file + $writeYaml->updateYaml('settings', 'formdata.yaml', $data); + + # add message and return to original site + $this->c->flash->addMessage('formdata', $pluginName); + return $response->withRedirect($referer[0]); + } + } + + private function validateInput($objectType, $objectName, $userInput) + { + # get settings and start validation + $originalSettings = \Typemill\Settings::getObjectSettings($objectType, $objectName); + $userSettings = \Typemill\Settings::getUserSettings(); + $validate = new Validation(); + + if(isset($originalSettings['public']['fields'])) + { + /* flaten the multi-dimensional array with fieldsets to a one-dimensional array */ + $originalFields = array(); + foreach($originalSettings['public']['fields'] as $fieldName => $fieldValue) + { + if(isset($fieldValue['fields'])) + { + foreach($fieldValue['fields'] as $subFieldName => $subFieldValue) + { + $originalFields[$subFieldName] = $subFieldValue; + } + } + else + { + $originalFields[$fieldName] = $fieldValue; + } + } + + /* take the user input data and iterate over all fields and values */ + foreach($userInput as $fieldName => $fieldValue) + { + /* get the corresponding field definition from original plugin settings */ + $fieldDefinition = isset($originalFields[$fieldName]) ? $originalFields[$fieldName] : false; + + if($fieldDefinition) + { + /* validate user input for this field */ + $validate->objectField($fieldName, $fieldValue, $objectName, $fieldDefinition); + } + if(!$fieldDefinition && $fieldName != 'active') + { + $_SESSION['errors'][$objectName][$fieldName] = array('This field is not defined!'); + } + } + } + } } \ No newline at end of file diff --git a/system/Controllers/PageController.php b/system/Controllers/PageController.php index f6ab21c..e7504a3 100644 --- a/system/Controllers/PageController.php +++ b/system/Controllers/PageController.php @@ -1,236 +1,236 @@ -c->get('settings'); - $pathToContent = $settings['rootPath'] . $settings['contentFolder']; - $cache = new WriteCache(); - $uri = $request->getUri(); - $base_url = $uri->getBaseUrl(); - - try - { - /* if the cached structure is still valid, use it */ - if($cache->validate('cache', 'lastCache.txt',600)) - { - $structure = $this->getCachedStructure($cache); - } - if(!isset($structure) OR !$structure) - { - /* if not, get a fresh structure of the content folder */ - $structure = $this->getFreshStructure($pathToContent, $cache, $uri); - - /* if there is no structure at all, the content folder is probably empty */ - if(!$structure) - { - $content = '

No Content

Your content folder is empty.

'; - - return $this->render($response, '/index.twig', array( 'content' => $content )); - } - elseif(!$cache->validate('cache', 'lastSitemap.txt', 86400)) - { - /* update sitemap */ - $sitemap = new WriteSitemap(); - $sitemap->updateSitemap('cache', 'sitemap.xml', 'lastSitemap.txt', $structure, $uri->getBaseUrl()); - - /* check and update the typemill-version in the user settings */ - $this->updateVersion($uri->getBaseUrl()); - } - } - - /* dispatch event and let others manipulate the structure */ - $structure = $this->c->dispatcher->dispatch('onPagetreeLoaded', new OnPagetreeLoaded($structure))->getData(); - } - catch (Exception $e) - { - echo $e->getMessage(); - exit(1); - } - - /* if the user is on startpage */ - if(empty($args)) - { - /* check, if there is an index-file in the root of the content folder */ - $contentMD = file_exists($pathToContent . DIRECTORY_SEPARATOR . 'index.md') ? file_get_contents($pathToContent . DIRECTORY_SEPARATOR . 'index.md') : NULL; - } - else - { - /* get the request url */ - $urlRel = $uri->getBasePath() . '/' . $args['params']; - - /* find the url in the content-item-tree and return the item-object for the file */ - $item = Folder::getItemForUrl($structure, $urlRel); - - /* if there is still no item, return a 404-page */ - if(!$item) - { - return $this->render404($response, array( 'navigation' => $structure, 'settings' => $settings, 'base_url' => $base_url )); - } - - /* get breadcrumb for page */ - $breadcrumb = Folder::getBreadcrumb($structure, $item->keyPathArray); - $breadcrumb = $this->c->dispatcher->dispatch('onBreadcrumbLoaded', new OnBreadcrumbLoaded($breadcrumb))->getData(); - - /* add the paging to the item */ - $item = Folder::getPagingForItem($structure, $item); - $item = $this->c->dispatcher->dispatch('onItemLoaded', new OnItemLoaded($item))->getData(); - - /* check if url is a folder. If so, check if there is an index-file in that folder */ - if($item->elementType == 'folder') - { - $filePath = $pathToContent . $item->path . DIRECTORY_SEPARATOR . 'index.md'; - } - elseif($item->elementType == 'file') - { - $filePath = $pathToContent . $item->path; - } - - /* add the modified date for the file */ - $item->modified = isset($filePath) ? filemtime($filePath) : false; - - /* read the content of the file */ - $contentMD = isset($filePath) ? file_get_contents($filePath) : false; - } - - # dispatch the original content without plugin-manipulations for case anyone wants to use it - $this->c->dispatcher->dispatch('onOriginalLoaded', new OnOriginalLoaded($contentMD)); - - $contentMD = $this->c->dispatcher->dispatch('onMarkdownLoaded', new OnMarkdownLoaded($contentMD))->getData(); - - /* initialize parsedown */ - $parsedown = new ParsedownExtension(); - - /* set safe mode to escape javascript and html in markdown */ - $parsedown->setSafeMode(true); - - /* parse markdown-file to content-array */ - $contentArray = $parsedown->text($contentMD); - $contentArray = $this->c->dispatcher->dispatch('onContentArrayLoaded', new OnContentArrayLoaded($contentArray))->getData(); - - /* get the first image from content array */ - $firstImage = $this->getFirstImage($contentArray); - - $itemUrl = isset($item->urlRel) ? $item->urlRel : false; - - /* parse markdown-content-array to content-string */ - $contentHTML = $parsedown->markup($contentArray, $itemUrl); - $contentHTML = $this->c->dispatcher->dispatch('onHtmlLoaded', new OnHtmlLoaded($contentHTML))->getData(); - - /* extract the h1 headline*/ - $contentParts = explode("", $contentHTML); - $title = isset($contentParts[0]) ? strip_tags($contentParts[0]) : $settings['title']; - - $contentHTML = isset($contentParts[1]) ? $contentParts[1] : $contentHTML; - - /* create excerpt from content */ - $excerpt = substr($contentHTML,0,500); - - /* create description from excerpt */ - $description = isset($excerpt) ? strip_tags($excerpt) : false; - if($description) - { - $description = trim(preg_replace('/\s+/', ' ', $description)); - $description = substr($description, 0, 300); - $lastSpace = strrpos($description, ' '); - $description = substr($description, 0, $lastSpace); - } - - /* get url and alt-tag for first image, if exists */ - if($firstImage) - { - preg_match('#\((.*?)\)#', $firstImage, $img_url); - if($img_url[1]) - { - preg_match('#\[(.*?)\]#', $firstImage, $img_alt); - - $firstImage = array('img_url' => $base_url . '/' . $img_url[1], 'img_alt' => $img_alt[1]); - } - } - - $route = empty($args) && $settings['startpage'] ? '/cover.twig' : '/index.twig'; - - return $this->render($response, $route, array('navigation' => $structure, 'content' => $contentHTML, 'item' => $item, 'breadcrumb' => $breadcrumb, 'settings' => $settings, 'title' => $title, 'description' => $description, 'base_url' => $base_url, 'image' => $firstImage )); - } - - protected function getCachedStructure($cache) - { - return $cache->getCache('cache', 'structure.txt'); - } - - protected function getFreshStructure($pathToContent, $cache, $uri) - { - /* scan the content of the folder */ - $structure = Folder::scanFolder($pathToContent); - - /* if there is no content, render an empty page */ - if(count($structure) == 0) - { - return false; - } - - /* create an array of object with the whole content of the folder */ - $structure = Folder::getFolderContentDetails($structure, $uri->getBaseUrl(), $uri->getBasePath()); - - /* cache navigation */ - $cache->updateCache('cache', 'structure.txt', 'lastCache.txt', $structure); - - return $structure; - } - - protected function updateVersion($baseUrl) - { - /* check the latest public typemill version */ - $version = new VersionCheck(); - $latestVersion = $version->checkVersion($baseUrl); - - if($latestVersion) - { - /* store latest version */ - \Typemill\Settings::updateSettings(array('latestVersion' => $latestVersion)); - } - } - - protected function getFirstImage(array $contentBlocks) - { - foreach($contentBlocks as $block) - { - /* is it a paragraph? */ - if(isset($block['name']) && $block['name'] == 'p') - { - if(isset($block['handler']['argument']) && substr($block['handler']['argument'], 0, 2) == '![' ) - { - return $block['handler']['argument']; - } - } - } - - return false; - } +c->get('settings'); + $pathToContent = $settings['rootPath'] . $settings['contentFolder']; + $cache = new WriteCache(); + $uri = $request->getUri(); + $base_url = $uri->getBaseUrl(); + + try + { + /* if the cached structure is still valid, use it */ + if($cache->validate('cache', 'lastCache.txt',600)) + { + $structure = $this->getCachedStructure($cache); + } + if(!isset($structure) OR !$structure) + { + /* if not, get a fresh structure of the content folder */ + $structure = $this->getFreshStructure($pathToContent, $cache, $uri); + + /* if there is no structure at all, the content folder is probably empty */ + if(!$structure) + { + $content = '

No Content

Your content folder is empty.

'; + + return $this->render($response, '/index.twig', array( 'content' => $content )); + } + elseif(!$cache->validate('cache', 'lastSitemap.txt', 86400)) + { + /* update sitemap */ + $sitemap = new WriteSitemap(); + $sitemap->updateSitemap('cache', 'sitemap.xml', 'lastSitemap.txt', $structure, $uri->getBaseUrl()); + + /* check and update the typemill-version in the user settings */ + $this->updateVersion($uri->getBaseUrl()); + } + } + + /* dispatch event and let others manipulate the structure */ + $structure = $this->c->dispatcher->dispatch('onPagetreeLoaded', new OnPagetreeLoaded($structure))->getData(); + } + catch (Exception $e) + { + echo $e->getMessage(); + exit(1); + } + + /* if the user is on startpage */ + if(empty($args)) + { + /* check, if there is an index-file in the root of the content folder */ + $contentMD = file_exists($pathToContent . DIRECTORY_SEPARATOR . 'index.md') ? file_get_contents($pathToContent . DIRECTORY_SEPARATOR . 'index.md') : NULL; + } + else + { + /* get the request url */ + $urlRel = $uri->getBasePath() . '/' . $args['params']; + + /* find the url in the content-item-tree and return the item-object for the file */ + $item = Folder::getItemForUrl($structure, $urlRel); + + /* if there is still no item, return a 404-page */ + if(!$item) + { + return $this->render404($response, array( 'navigation' => $structure, 'settings' => $settings, 'base_url' => $base_url )); + } + + /* get breadcrumb for page */ + $breadcrumb = Folder::getBreadcrumb($structure, $item->keyPathArray); + $breadcrumb = $this->c->dispatcher->dispatch('onBreadcrumbLoaded', new OnBreadcrumbLoaded($breadcrumb))->getData(); + + /* add the paging to the item */ + $item = Folder::getPagingForItem($structure, $item); + $item = $this->c->dispatcher->dispatch('onItemLoaded', new OnItemLoaded($item))->getData(); + + /* check if url is a folder. If so, check if there is an index-file in that folder */ + if($item->elementType == 'folder') + { + $filePath = $pathToContent . $item->path . DIRECTORY_SEPARATOR . 'index.md'; + } + elseif($item->elementType == 'file') + { + $filePath = $pathToContent . $item->path; + } + + /* add the modified date for the file */ + $item->modified = isset($filePath) ? filemtime($filePath) : false; + + /* read the content of the file */ + $contentMD = isset($filePath) ? file_get_contents($filePath) : false; + } + + # dispatch the original content without plugin-manipulations for case anyone wants to use it + $this->c->dispatcher->dispatch('onOriginalLoaded', new OnOriginalLoaded($contentMD)); + + $contentMD = $this->c->dispatcher->dispatch('onMarkdownLoaded', new OnMarkdownLoaded($contentMD))->getData(); + + /* initialize parsedown */ + $parsedown = new ParsedownExtension(); + + /* set safe mode to escape javascript and html in markdown */ + $parsedown->setSafeMode(true); + + /* parse markdown-file to content-array */ + $contentArray = $parsedown->text($contentMD); + $contentArray = $this->c->dispatcher->dispatch('onContentArrayLoaded', new OnContentArrayLoaded($contentArray))->getData(); + + /* get the first image from content array */ + $firstImage = $this->getFirstImage($contentArray); + + $itemUrl = isset($item->urlRel) ? $item->urlRel : false; + + /* parse markdown-content-array to content-string */ + $contentHTML = $parsedown->markup($contentArray, $itemUrl); + $contentHTML = $this->c->dispatcher->dispatch('onHtmlLoaded', new OnHtmlLoaded($contentHTML))->getData(); + + /* extract the h1 headline*/ + $contentParts = explode("", $contentHTML); + $title = isset($contentParts[0]) ? strip_tags($contentParts[0]) : $settings['title']; + + $contentHTML = isset($contentParts[1]) ? $contentParts[1] : $contentHTML; + + /* create excerpt from content */ + $excerpt = substr($contentHTML,0,500); + + /* create description from excerpt */ + $description = isset($excerpt) ? strip_tags($excerpt) : false; + if($description) + { + $description = trim(preg_replace('/\s+/', ' ', $description)); + $description = substr($description, 0, 300); + $lastSpace = strrpos($description, ' '); + $description = substr($description, 0, $lastSpace); + } + + /* get url and alt-tag for first image, if exists */ + if($firstImage) + { + preg_match('#\((.*?)\)#', $firstImage, $img_url); + if($img_url[1]) + { + preg_match('#\[(.*?)\]#', $firstImage, $img_alt); + + $firstImage = array('img_url' => $base_url . '/' . $img_url[1], 'img_alt' => $img_alt[1]); + } + } + + $route = empty($args) && $settings['startpage'] ? '/cover.twig' : '/index.twig'; + + return $this->render($response, $route, array('navigation' => $structure, 'content' => $contentHTML, 'item' => $item, 'breadcrumb' => $breadcrumb, 'settings' => $settings, 'title' => $title, 'description' => $description, 'base_url' => $base_url, 'image' => $firstImage )); + } + + protected function getCachedStructure($cache) + { + return $cache->getCache('cache', 'structure.txt'); + } + + protected function getFreshStructure($pathToContent, $cache, $uri) + { + /* scan the content of the folder */ + $structure = Folder::scanFolder($pathToContent); + + /* if there is no content, render an empty page */ + if(count($structure) == 0) + { + return false; + } + + /* create an array of object with the whole content of the folder */ + $structure = Folder::getFolderContentDetails($structure, $uri->getBaseUrl(), $uri->getBasePath()); + + /* cache navigation */ + $cache->updateCache('cache', 'structure.txt', 'lastCache.txt', $structure); + + return $structure; + } + + protected function updateVersion($baseUrl) + { + /* check the latest public typemill version */ + $version = new VersionCheck(); + $latestVersion = $version->checkVersion($baseUrl); + + if($latestVersion) + { + /* store latest version */ + \Typemill\Settings::updateSettings(array('latestVersion' => $latestVersion)); + } + } + + protected function getFirstImage(array $contentBlocks) + { + foreach($contentBlocks as $block) + { + /* is it a paragraph? */ + if(isset($block['name']) && $block['name'] == 'p') + { + if(isset($block['handler']['argument']) && substr($block['handler']['argument'], 0, 2) == '![' ) + { + return $block['handler']['argument']; + } + } + } + + return false; + } } \ No newline at end of file diff --git a/system/Controllers/SettingsController.php b/system/Controllers/SettingsController.php index ed5cf0b..4509fa3 100644 --- a/system/Controllers/SettingsController.php +++ b/system/Controllers/SettingsController.php @@ -1,796 +1,796 @@ -c->get('settings'); - $copyright = $this->getCopyright(); - $languages = $this->getLanguages(); - $locale = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? substr($_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2) : 'en'; - $users = $user->getUsers(); - $route = $request->getAttribute('route'); - - return $this->render($response, 'settings/system.twig', array('settings' => $settings, 'copyright' => $copyright, 'languages' => $languages, 'locale' => $locale, 'users' => $users, 'route' => $route->getName() )); - } - - public function saveSettings($request, $response, $args) - { - if($request->isPost()) - { - $referer = $request->getHeader('HTTP_REFERER'); - $uri = $request->getUri(); - $base_url = $uri->getBaseUrl(); - - # security, users should not be able to fake post with settings from other typemill pages. - if(!isset($referer[0]) OR $referer[0] !== $base_url . '/tm/settings' ) - { - $this->c->flash->addMessage('error', 'illegal referer'); - return $response->withRedirect($this->c->router->pathFor('settings.show')); - } - - $settings = \Typemill\Settings::getUserSettings(); - $params = $request->getParams(); - $newSettings = isset($params['settings']) ? $params['settings'] : false; - $validate = new Validation(); - - if($newSettings) - { - /* make sure only allowed fields are stored */ - $newSettings = array( - 'title' => $newSettings['title'], - 'author' => $newSettings['author'], - 'copyright' => $newSettings['copyright'], - 'year' => $newSettings['year'], - 'startpage' => isset($newSettings['startpage']) ? true : false, - 'editor' => $newSettings['editor'], - ); - - $copyright = $this->getCopyright(); - - $validate->settings($newSettings, $copyright, 'settings'); - } - else - { - $this->c->flash->addMessage('error', 'Wrong Input'); - return $response->withRedirect($this->c->router->pathFor('settings.show')); - } - - if(isset($_SESSION['errors'])) - { - $this->c->flash->addMessage('error', 'Please correct the errors'); - return $response->withRedirect($this->c->router->pathFor('settings.show')); - } - - /* store updated settings */ - \Typemill\Settings::updateSettings(array_merge($settings, $newSettings)); - - $this->c->flash->addMessage('info', 'Settings are stored'); - return $response->withRedirect($this->c->router->pathFor('settings.show')); - } - } - - /********************* - ** THEME SETTINGS ** - *********************/ - - public function showThemes($request, $response, $args) - { - $userSettings = $this->c->get('settings'); - $themes = $this->getThemes(); - $themedata = array(); - $fieldsModel = new Fields(); - - foreach($themes as $themeName) - { - /* if theme is active, list it first */ - if($userSettings['theme'] == $themeName) - { - $themedata = array_merge(array($themeName => null), $themedata); - } - else - { - $themedata[$themeName] = null; - } - - $themeSettings = \Typemill\Settings::getObjectSettings('themes', $themeName); - if($themeSettings) - { - /* store them as default theme data with author, year, default settings and field-definitions */ - $themedata[$themeName] = $themeSettings; - } - - if(isset($themeSettings['forms']['fields'])) - { - $fields = $fieldsModel->getFields($userSettings, 'themes', $themeName, $themeSettings); - - /* overwrite original theme form definitions with enhanced form objects */ - $themedata[$themeName]['forms']['fields'] = $fields; - } - - /* add the preview image */ - $img = getcwd() . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $themeName . DIRECTORY_SEPARATOR . $themeName . '.jpg'; - $img = file_exists($img) ? $img : false; - - $themedata[$themeName]['img'] = $img; - } - - /* add the users for navigation */ - $user = new User(); - $users = $user->getUsers(); - $route = $request->getAttribute('route'); - - return $this->render($response, 'settings/themes.twig', array('settings' => $userSettings, 'themes' => $themedata, 'users' => $users, 'route' => $route->getName() )); - } - - public function showPlugins($request, $response, $args) - { - $userSettings = $this->c->get('settings'); - $plugins = array(); - $fieldsModel = new Fields(); - $fields = array(); - - /* iterate through the plugins in the stored user settings */ - foreach($userSettings['plugins'] as $pluginName => $pluginUserSettings) - { - /* add plugin to plugin Data, if active, set it first */ - /* if plugin is active, list it first */ - if($userSettings['plugins'][$pluginName]['active'] == true) - { - $plugins = array_merge(array($pluginName => null), $plugins); - } - else - { - $plugins[$pluginName] = Null; - } - - /* Check if the user has deleted a plugin. Then delete it in the settings and store the updated settings. */ - if(!is_dir($userSettings['rootPath'] . 'plugins' . DIRECTORY_SEPARATOR . $pluginName)) - { - /* remove the plugin settings and store updated settings */ - \Typemill\Settings::removePluginSettings($pluginName); - continue; - } - - /* load the original plugin definitions from the plugin folder (author, version and stuff) */ - $pluginOriginalSettings = \Typemill\Settings::getObjectSettings('plugins', $pluginName); - if($pluginOriginalSettings) - { - /* store them as default plugin data with plugin author, plugin year, default settings and field-definitions */ - $plugins[$pluginName] = $pluginOriginalSettings; - } - - /* check, if the plugin has been disabled in the form-session-data */ - if(isset($_SESSION['old']) && !isset($_SESSION['old'][$pluginName]['active'])) - { - $plugins[$pluginName]['settings']['active'] = false; - } - - /* if the plugin defines forms and fields, so that the user can edit the plugin settings in the frontend */ - if(isset($pluginOriginalSettings['forms']['fields'])) - { - # if the plugin defines frontend fields - if(isset($pluginOriginalSettings['public'])) - { - $pluginOriginalSettings['forms']['fields']['recaptcha'] = ['type' => 'checkbox', 'label' => 'Google Recaptcha', 'checkboxlabel' => 'Activate Recaptcha' ]; - $pluginOriginalSettings['forms']['fields']['recaptcha_webkey'] = ['type' => 'text', 'label' => 'Recaptcha Website Key', 'help' => 'Add the recaptcha website key here. You can get the key from the recaptcha website.', 'description' => 'The website key is mandatory if you activate the recaptcha field']; - $pluginOriginalSettings['forms']['fields']['recaptcha_secretkey'] = ['type' => 'text', 'label' => 'Recaptcha Secret Key', 'help' => 'Add the recaptcha secret key here. You can get the key from the recaptcha website.', 'description' => 'The secret key is mandatory if you activate the recaptcha field']; - } - - /* get all the fields and prefill them with the dafault-data, the user-data or old input data */ - $fields = $fieldsModel->getFields($userSettings, 'plugins', $pluginName, $pluginOriginalSettings); - - /* overwrite original plugin form definitions with enhanced form objects */ - $plugins[$pluginName]['forms']['fields'] = $fields; - } - } - - $user = new User(); - $users = $user->getUsers(); - $route = $request->getAttribute('route'); - - return $this->render($response, 'settings/plugins.twig', array('settings' => $userSettings, 'plugins' => $plugins, 'users' => $users, 'route' => $route->getName() )); - } - - /************************************* - ** SAVE THEME- AND PLUGIN-SETTINGS ** - *************************************/ - - public function saveThemes($request, $response, $args) - { - if($request->isPost()) - { - $referer = $request->getHeader('HTTP_REFERER'); - $uri = $request->getUri(); - $base_url = $uri->getBaseUrl(); - - # users should not be able to fake post with settings from other typemill pages. - if(!isset($referer[0]) OR $referer[0] !== $base_url . '/tm/themes' ) - { - $this->c->flash->addMessage('error', 'illegal referer'); - return $response->withRedirect($this->c->router->pathFor('themes.show')); - } - - $userSettings = \Typemill\Settings::getUserSettings(); - $params = $request->getParams(); - $themeName = isset($params['theme']) ? $params['theme'] : false; - $userInput = isset($params[$themeName]) ? $params[$themeName] : false; - $validate = new Validation(); - $themeSettings = \Typemill\Settings::getObjectSettings('themes', $themeName); - - if(isset($themeSettings['settings']['images'])) - { - # get the default settings - $defaultSettings = \Typemill\Settings::getDefaultSettings(); - - # merge the default image settings with the theme image settings, delete all others (image settings from old theme) - $userSettings['images'] = array_merge($defaultSettings['images'], $themeSettings['settings']['images']); - } - - /* set theme name and delete theme settings from user settings for the case, that the new theme has no settings */ - $userSettings['theme'] = $themeName; - - if($userInput) - { - /* validate the user-input */ - $this->validateInput('themes', $themeName, $userInput, $validate); - - /* set user input as theme settings */ - $userSettings['themes'][$themeName] = $userInput; - } - - /* check for errors and redirect to path, if errors found */ - if(isset($_SESSION['errors'])) - { - $this->c->flash->addMessage('error', 'Please correct the errors'); - return $response->withRedirect($this->c->router->pathFor('themes.show')); - } - - /* store updated settings */ - \Typemill\Settings::updateSettings($userSettings); - - $this->c->flash->addMessage('info', 'Settings are stored'); - return $response->withRedirect($this->c->router->pathFor('themes.show')); - } - } - - public function savePlugins($request, $response, $args) - { - if($request->isPost()) - { - $referer = $request->getHeader('HTTP_REFERER'); - $uri = $request->getUri(); - $base_url = $uri->getBaseUrl(); - - # security, users should not be able to fake post with settings from other typemill pages. - if(!isset($referer[0]) OR $referer[0] !== $base_url . '/tm/plugins' ) - { - $this->c->flash->addMessage('error', 'illegal referer'); - return $response->withRedirect($this->c->router->pathFor('plugins.show')); - } - - $userSettings = \Typemill\Settings::getUserSettings(); - $pluginSettings = array(); - $userInput = $request->getParams(); - $validate = new Validation(); - - /* use the stored user settings and iterate over all original plugin settings, so we do not forget any... */ - foreach($userSettings['plugins'] as $pluginName => $pluginUserSettings) - { - /* if there are no input-data for this plugin, then use the stored plugin settings */ - if(!isset($userInput[$pluginName])) - { - $pluginSettings[$pluginName] = $pluginUserSettings; - } - else - { - /* validate the user-input */ - $this->validateInput('plugins', $pluginName, $userInput[$pluginName], $validate); - - /* use the input data */ - $pluginSettings[$pluginName] = $userInput[$pluginName]; - } - - /* deactivate the plugin, if there is no active flag */ - if(!isset($userInput[$pluginName]['active'])) - { - $pluginSettings[$pluginName]['active'] = false; - } - } - - if(isset($_SESSION['errors'])) - { - $this->c->flash->addMessage('error', 'Please correct the errors below'); - } - else - { - /* if everything is valid, add plugin settings to base settings again */ - $userSettings['plugins'] = $pluginSettings; - - /* store updated settings */ - \Typemill\Settings::updateSettings($userSettings); - - $this->c->flash->addMessage('info', 'Settings are stored'); - } - - return $response->withRedirect($this->c->router->pathFor('plugins.show')); - } - } - - private function validateInput($objectType, $objectName, $userInput, $validate) - { - /* fetch the original settings from the folder (plugin or theme) to get the field definitions */ - $originalSettings = \Typemill\Settings::getObjectSettings($objectType, $objectName); - - if(isset($originalSettings['forms']['fields'])) - { - /* flaten the multi-dimensional array with fieldsets to a one-dimensional array */ - $originalFields = array(); - foreach($originalSettings['forms']['fields'] as $fieldName => $fieldValue) - { - if(isset($fieldValue['fields'])) - { - foreach($fieldValue['fields'] as $subFieldName => $subFieldValue) - { - $originalFields[$subFieldName] = $subFieldValue; - } - } - else - { - $originalFields[$fieldName] = $fieldValue; - } - } - - # if the plugin defines frontend fields - if(isset($originalSettings['public'])) - { - $originalFields['recaptcha'] = ['type' => 'checkbox', 'label' => 'Google Recaptcha', 'checkboxlabel' => 'Activate Recaptcha' ]; - $originalFields['recaptcha_webkey'] = ['type' => 'text', 'label' => 'Recaptcha Website Key', 'help' => 'Add the recaptcha website key here. You can get the key from the recaptcha website.', 'description' => 'The website key is mandatory if you activate the recaptcha field']; - $originalFields['recaptcha_secretkey'] = ['type' => 'text', 'label' => 'Recaptcha Secret Key', 'help' => 'Add the recaptcha secret key here. You can get the key from the recaptcha website.', 'description' => 'The secret key is mandatory if you activate the recaptcha field']; - } - - /* take the user input data and iterate over all fields and values */ - foreach($userInput as $fieldName => $fieldValue) - { - /* get the corresponding field definition from original plugin settings */ - $fieldDefinition = isset($originalFields[$fieldName]) ? $originalFields[$fieldName] : false; - - if($fieldDefinition) - { - /* validate user input for this field */ - $validate->objectField($fieldName, $fieldValue, $objectName, $fieldDefinition); - } - if(!$fieldDefinition && $fieldName != 'active') - { - $_SESSION['errors'][$objectName][$fieldName] = array('This field is not defined!'); - } - } - } - } - - /*********************** - ** USER MANAGEMENT ** - ***********************/ - - public function showUser($request, $response, $args) - { - if($_SESSION['role'] == 'editor' && $_SESSION['user'] !== $args['username']) - { - return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $_SESSION['user']] )); - } - - $validate = new Validation(); - - if($validate->username($args['username'])) - { - $user = new User(); - $users = $user->getUsers(); - $userrole = $user->getUserroles(); - $userdata = $user->getUser($args['username']); - $settings = $this->c->get('settings'); - - if($userdata) - { - return $this->render($response, 'settings/user.twig', array('settings' => $settings, 'users' => $users, 'userdata' => $userdata, 'userrole' => $userrole, 'username' => $args['username'] )); - } - } - - $this->c->flash->addMessage('error', 'User does not exists'); - return $response->withRedirect($this->c->router->pathFor('user.list')); - } - - public function listUser($request, $response) - { - $user = new User(); - $users = $user->getUsers(); - $userdata = array(); - $route = $request->getAttribute('route'); - $settings = $this->c->get('settings'); - - foreach($users as $username) - { - $userdata[] = $user->getUser($username); - } - - return $this->render($response, 'settings/userlist.twig', array('settings' => $settings, 'users' => $users, 'userdata' => $userdata, 'route' => $route->getName() )); - } - - public function newUser($request, $response, $args) - { - $user = new User(); - $users = $user->getUsers(); - $userrole = $user->getUserroles(); - $route = $request->getAttribute('route'); - $settings = $this->c->get('settings'); - - return $this->render($response, 'settings/usernew.twig', array('settings' => $settings, 'users' => $users, 'userrole' => $userrole, 'route' => $route->getName() )); - } - - public function createUser($request, $response, $args) - { - if($request->isPost()) - { - $referer = $request->getHeader('HTTP_REFERER'); - $uri = $request->getUri(); - $base_url = $uri->getBaseUrl(); - - # security, users should not be able to fake post with settings from other typemill pages. - if(!isset($referer[0]) OR $referer[0] !== $base_url . '/tm/user/new' ) - { - $this->c->flash->addMessage('error', 'illegal referer'); - return $response->withRedirect($this->c->router->pathFor('user.new')); - } - - $params = $request->getParams(); - $user = new User(); - $userroles = $user->getUserroles(); - $validate = new Validation(); - - if($validate->newUser($params, $userroles)) - { - $userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']); - - $user->createUser($userdata); - - $this->c->flash->addMessage('info', 'Welcome, there is a new user!'); - return $response->withRedirect($this->c->router->pathFor('user.list')); - } - - $this->c->flash->addMessage('error', 'Please correct your input'); - return $response->withRedirect($this->c->router->pathFor('user.new')); - } - } - - public function updateUser($request, $response, $args) - { - if($request->isPost()) - { - $referer = $request->getHeader('HTTP_REFERER'); - $uri = $request->getUri(); - $base_url = $uri->getBaseUrl(); - - # security, users should not be able to fake post with settings from other typemill pages. - if(!isset($referer[0]) OR strpos($referer[0], $base_url . '/tm/user/') === false ) - { - $this->c->flash->addMessage('error', 'illegal referer'); - return $response->withRedirect($this->c->router->pathFor('user.list')); - } - - $params = $request->getParams(); - $user = new User(); - $userroles = $user->getUserroles(); - $validate = new Validation(); - - /* non admins have different update rights */ - if($_SESSION['role'] !== 'administrator') - { - /* if an editor tries to update other userdata than its own */ - if($_SESSION['user'] !== $params['username']) - { - return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $_SESSION['user']] )); - } - - /* non admins cannot change his userrole */ - $params['userrole'] = $_SESSION['role']; - } - - if($validate->existingUser($params, $userroles)) - { - $userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole']); - - if(empty($params['password']) AND empty($params['newpassword'])) - { - $user->updateUser($userdata); - $this->c->flash->addMessage('info', 'Saved all changes'); - return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']])); - } - elseif($validate->newPassword($params)) - { - $userdata['password'] = $params['newpassword']; - $user->updateUser($userdata); - $this->c->flash->addMessage('info', 'Saved all changes'); - return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']])); - } - } - - $this->c->flash->addMessage('error', 'Please correct your input'); - return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']])); - } - } - - public function deleteUser($request, $response, $args) - { - if($request->isPost()) - { - $referer = $request->getHeader('HTTP_REFERER'); - $uri = $request->getUri(); - $base_url = $uri->getBaseUrl(); - - # security, users should not be able to fake post with settings from other typemill pages. - if(!isset($referer[0]) OR strpos($referer[0], $base_url . '/tm/user/') === false ) - { - $this->c->flash->addMessage('error', 'illegal referer'); - return $response->withRedirect($this->c->router->pathFor('user.list')); - } - - $params = $request->getParams(); - $validate = new Validation(); - $user = new User(); - - /* non admins have different update rights */ - if($_SESSION['role'] !== 'administrator') - { - /* if an editor tries to delete other user than its own */ - if($_SESSION['user'] !== $params['username']) - { - return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $_SESSION['user']] )); - } - } - - if($validate->username($params['username'])) - { - $user->deleteUser($params['username']); - - $this->c->flash->addMessage('info', 'Say goodbye, the user is gone!'); - return $response->withRedirect($this->c->router->pathFor('user.list')); - } - - $this->c->flash->addMessage('error', 'Ups, we did not find that user'); - return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']])); - } - } - - private function getThemes() - { - $themeFolder = $this->c->get('settings')['rootPath'] . $this->c->get('settings')['themeFolder']; - $themeFolderC = scandir($themeFolder); - $themes = array(); - foreach ($themeFolderC as $key => $theme) - { - if (!in_array($theme, array(".",".."))) - { - if (is_dir($themeFolder . DIRECTORY_SEPARATOR . $theme)) - { - $themes[] = $theme; - } - } - } - return $themes; - } - - private function getCopyright() - { - return array( - "©", - "CC-BY", - "CC-BY-NC", - "CC-BY-NC-ND", - "CC-BY-NC-SA", - "CC-BY-ND", - "CC-BY-SA", - "None" - ); - } - - private function getLanguages() - { - return array( - 'ab' => 'Abkhazian', - 'aa' => 'Afar', - 'af' => 'Afrikaans', - 'ak' => 'Akan', - 'sq' => 'Albanian', - 'am' => 'Amharic', - 'ar' => 'Arabic', - 'an' => 'Aragonese', - 'hy' => 'Armenian', - 'as' => 'Assamese', - 'av' => 'Avaric', - 'ae' => 'Avestan', - 'ay' => 'Aymara', - 'az' => 'Azerbaijani', - 'bm' => 'Bambara', - 'ba' => 'Bashkir', - 'eu' => 'Basque', - 'be' => 'Belarusian', - 'bn' => 'Bengali', - 'bh' => 'Bihari languages', - 'bi' => 'Bislama', - 'bs' => 'Bosnian', - 'br' => 'Breton', - 'bg' => 'Bulgarian', - 'my' => 'Burmese', - 'ca' => 'Catalan, Valencian', - 'km' => 'Central Khmer', - 'ch' => 'Chamorro', - 'ce' => 'Chechen', - 'ny' => 'Chichewa, Chewa, Nyanja', - 'zh' => 'Chinese', - 'cu' => 'Church Slavonic, Old Bulgarian, Old Church Slavonic', - 'cv' => 'Chuvash', - 'kw' => 'Cornish', - 'co' => 'Corsican', - 'cr' => 'Cree', - 'hr' => 'Croatian', - 'cs' => 'Czech', - 'da' => 'Danish', - 'dv' => 'Divehi, Dhivehi, Maldivian', - 'nl' => 'Dutch, Flemish', - 'dz' => 'Dzongkha', - 'en' => 'English', - 'eo' => 'Esperanto', - 'et' => 'Estonian', - 'ee' => 'Ewe', - 'fo' => 'Faroese', - 'fj' => 'Fijian', - 'fi' => 'Finnish', - 'fr' => 'French', - 'ff' => 'Fulah', - 'gd' => 'Gaelic, Scottish Gaelic', - 'gl' => 'Galician', - 'lg' => 'Ganda', - 'ka' => 'Georgian', - 'de' => 'German', - 'ki' => 'Gikuyu, Kikuyu', - 'el' => 'Greek (Modern)', - 'kl' => 'Greenlandic, Kalaallisut', - 'gn' => 'Guarani', - 'gu' => 'Gujarati', - 'ht' => 'Haitian, Haitian Creole', - 'ha' => 'Hausa', - 'he' => 'Hebrew', - 'hz' => 'Herero', - 'hi' => 'Hindi', - 'ho' => 'Hiri Motu', - 'hu' => 'Hungarian', - 'is' => 'Icelandic', - 'io' => 'Ido', - 'ig' => 'Igbo', - 'id' => 'Indonesian', - 'ia' => 'Interlingua (International Auxiliary Language Association)', - 'ie' => 'Interlingue', - 'iu' => 'Inuktitut', - 'ik' => 'Inupiaq', - 'ga' => 'Irish', - 'it' => 'Italian', - 'ja' => 'Japanese', - 'jv' => 'Javanese', - 'kn' => 'Kannada', - 'kr' => 'Kanuri', - 'ks' => 'Kashmiri', - 'kk' => 'Kazakh', - 'rw' => 'Kinyarwanda', - 'kv' => 'Komi', - 'kg' => 'Kongo', - 'ko' => 'Korean', - 'kj' => 'Kwanyama, Kuanyama', - 'ku' => 'Kurdish', - 'ky' => 'Kyrgyz', - 'lo' => 'Lao', - 'la' => 'Latin', - 'lv' => 'Latvian', - 'lb' => 'Letzeburgesch, Luxembourgish', - 'li' => 'Limburgish, Limburgan, Limburger', - 'ln' => 'Lingala', - 'lt' => 'Lithuanian', - 'lu' => 'Luba-Katanga', - 'mk' => 'Macedonian', - 'mg' => 'Malagasy', - 'ms' => 'Malay', - 'ml' => 'Malayalam', - 'mt' => 'Maltese', - 'gv' => 'Manx', - 'mi' => 'Maori', - 'mr' => 'Marathi', - 'mh' => 'Marshallese', - 'ro' => 'Moldovan, Moldavian, Romanian', - 'mn' => 'Mongolian', - 'na' => 'Nauru', - 'nv' => 'Navajo, Navaho', - 'nd' => 'Northern Ndebele', - 'ng' => 'Ndonga', - 'ne' => 'Nepali', - 'se' => 'Northern Sami', - 'no' => 'Norwegian', - 'nb' => 'Norwegian Bokmål', - 'nn' => 'Norwegian Nynorsk', - 'ii' => 'Nuosu, Sichuan Yi', - 'oc' => 'Occitan (post 1500)', - 'oj' => 'Ojibwa', - 'or' => 'Oriya', - 'om' => 'Oromo', - 'os' => 'Ossetian, Ossetic', - 'pi' => 'Pali', - 'pa' => 'Panjabi, Punjabi', - 'ps' => 'Pashto, Pushto', - 'fa' => 'Persian', - 'pl' => 'Polish', - 'pt' => 'Portuguese', - 'qu' => 'Quechua', - 'rm' => 'Romansh', - 'rn' => 'Rundi', - 'ru' => 'Russian', - 'sm' => 'Samoan', - 'sg' => 'Sango', - 'sa' => 'Sanskrit', - 'sc' => 'Sardinian', - 'sr' => 'Serbian', - 'sn' => 'Shona', - 'sd' => 'Sindhi', - 'si' => 'Sinhala, Sinhalese', - 'sk' => 'Slovak', - 'sl' => 'Slovenian', - 'so' => 'Somali', - 'st' => 'Sotho, Southern', - 'nr' => 'South Ndebele', - 'es' => 'Spanish, Castilian', - 'su' => 'Sundanese', - 'sw' => 'Swahili', - 'ss' => 'Swati', - 'sv' => 'Swedish', - 'tl' => 'Tagalog', - 'ty' => 'Tahitian', - 'tg' => 'Tajik', - 'ta' => 'Tamil', - 'tt' => 'Tatar', - 'te' => 'Telugu', - 'th' => 'Thai', - 'bo' => 'Tibetan', - 'ti' => 'Tigrinya', - 'to' => 'Tonga (Tonga Islands)', - 'ts' => 'Tsonga', - 'tn' => 'Tswana', - 'tr' => 'Turkish', - 'tk' => 'Turkmen', - 'tw' => 'Twi', - 'ug' => 'Uighur, Uyghur', - 'uk' => 'Ukrainian', - 'ur' => 'Urdu', - 'uz' => 'Uzbek', - 've' => 'Venda', - 'vi' => 'Vietnamese', - 'vo' => 'Volap_k', - 'wa' => 'Walloon', - 'cy' => 'Welsh', - 'fy' => 'Western Frisian', - 'wo' => 'Wolof', - 'xh' => 'Xhosa', - 'yi' => 'Yiddish', - 'yo' => 'Yoruba', - 'za' => 'Zhuang, Chuang', - 'zu' => 'Zulu' - ); - } +c->get('settings'); + $copyright = $this->getCopyright(); + $languages = $this->getLanguages(); + $locale = isset($_SERVER['HTTP_ACCEPT_LANGUAGE']) ? substr($_SERVER["HTTP_ACCEPT_LANGUAGE"],0,2) : 'en'; + $users = $user->getUsers(); + $route = $request->getAttribute('route'); + + return $this->render($response, 'settings/system.twig', array('settings' => $settings, 'copyright' => $copyright, 'languages' => $languages, 'locale' => $locale, 'users' => $users, 'route' => $route->getName() )); + } + + public function saveSettings($request, $response, $args) + { + if($request->isPost()) + { + $referer = $request->getHeader('HTTP_REFERER'); + $uri = $request->getUri(); + $base_url = $uri->getBaseUrl(); + + # security, users should not be able to fake post with settings from other typemill pages. + if(!isset($referer[0]) OR $referer[0] !== $base_url . '/tm/settings' ) + { + $this->c->flash->addMessage('error', 'illegal referer'); + return $response->withRedirect($this->c->router->pathFor('settings.show')); + } + + $settings = \Typemill\Settings::getUserSettings(); + $params = $request->getParams(); + $newSettings = isset($params['settings']) ? $params['settings'] : false; + $validate = new Validation(); + + if($newSettings) + { + /* make sure only allowed fields are stored */ + $newSettings = array( + 'title' => $newSettings['title'], + 'author' => $newSettings['author'], + 'copyright' => $newSettings['copyright'], + 'year' => $newSettings['year'], + 'startpage' => isset($newSettings['startpage']) ? true : false, + 'editor' => $newSettings['editor'], + ); + + $copyright = $this->getCopyright(); + + $validate->settings($newSettings, $copyright, 'settings'); + } + else + { + $this->c->flash->addMessage('error', 'Wrong Input'); + return $response->withRedirect($this->c->router->pathFor('settings.show')); + } + + if(isset($_SESSION['errors'])) + { + $this->c->flash->addMessage('error', 'Please correct the errors'); + return $response->withRedirect($this->c->router->pathFor('settings.show')); + } + + /* store updated settings */ + \Typemill\Settings::updateSettings(array_merge($settings, $newSettings)); + + $this->c->flash->addMessage('info', 'Settings are stored'); + return $response->withRedirect($this->c->router->pathFor('settings.show')); + } + } + + /********************* + ** THEME SETTINGS ** + *********************/ + + public function showThemes($request, $response, $args) + { + $userSettings = $this->c->get('settings'); + $themes = $this->getThemes(); + $themedata = array(); + $fieldsModel = new Fields(); + + foreach($themes as $themeName) + { + /* if theme is active, list it first */ + if($userSettings['theme'] == $themeName) + { + $themedata = array_merge(array($themeName => null), $themedata); + } + else + { + $themedata[$themeName] = null; + } + + $themeSettings = \Typemill\Settings::getObjectSettings('themes', $themeName); + if($themeSettings) + { + /* store them as default theme data with author, year, default settings and field-definitions */ + $themedata[$themeName] = $themeSettings; + } + + if(isset($themeSettings['forms']['fields'])) + { + $fields = $fieldsModel->getFields($userSettings, 'themes', $themeName, $themeSettings); + + /* overwrite original theme form definitions with enhanced form objects */ + $themedata[$themeName]['forms']['fields'] = $fields; + } + + /* add the preview image */ + $img = getcwd() . DIRECTORY_SEPARATOR . 'themes' . DIRECTORY_SEPARATOR . $themeName . DIRECTORY_SEPARATOR . $themeName . '.jpg'; + $img = file_exists($img) ? $img : false; + + $themedata[$themeName]['img'] = $img; + } + + /* add the users for navigation */ + $user = new User(); + $users = $user->getUsers(); + $route = $request->getAttribute('route'); + + return $this->render($response, 'settings/themes.twig', array('settings' => $userSettings, 'themes' => $themedata, 'users' => $users, 'route' => $route->getName() )); + } + + public function showPlugins($request, $response, $args) + { + $userSettings = $this->c->get('settings'); + $plugins = array(); + $fieldsModel = new Fields(); + $fields = array(); + + /* iterate through the plugins in the stored user settings */ + foreach($userSettings['plugins'] as $pluginName => $pluginUserSettings) + { + /* add plugin to plugin Data, if active, set it first */ + /* if plugin is active, list it first */ + if($userSettings['plugins'][$pluginName]['active'] == true) + { + $plugins = array_merge(array($pluginName => null), $plugins); + } + else + { + $plugins[$pluginName] = Null; + } + + /* Check if the user has deleted a plugin. Then delete it in the settings and store the updated settings. */ + if(!is_dir($userSettings['rootPath'] . 'plugins' . DIRECTORY_SEPARATOR . $pluginName)) + { + /* remove the plugin settings and store updated settings */ + \Typemill\Settings::removePluginSettings($pluginName); + continue; + } + + /* load the original plugin definitions from the plugin folder (author, version and stuff) */ + $pluginOriginalSettings = \Typemill\Settings::getObjectSettings('plugins', $pluginName); + if($pluginOriginalSettings) + { + /* store them as default plugin data with plugin author, plugin year, default settings and field-definitions */ + $plugins[$pluginName] = $pluginOriginalSettings; + } + + /* check, if the plugin has been disabled in the form-session-data */ + if(isset($_SESSION['old']) && !isset($_SESSION['old'][$pluginName]['active'])) + { + $plugins[$pluginName]['settings']['active'] = false; + } + + /* if the plugin defines forms and fields, so that the user can edit the plugin settings in the frontend */ + if(isset($pluginOriginalSettings['forms']['fields'])) + { + # if the plugin defines frontend fields + if(isset($pluginOriginalSettings['public'])) + { + $pluginOriginalSettings['forms']['fields']['recaptcha'] = ['type' => 'checkbox', 'label' => 'Google Recaptcha', 'checkboxlabel' => 'Activate Recaptcha' ]; + $pluginOriginalSettings['forms']['fields']['recaptcha_webkey'] = ['type' => 'text', 'label' => 'Recaptcha Website Key', 'help' => 'Add the recaptcha website key here. You can get the key from the recaptcha website.', 'description' => 'The website key is mandatory if you activate the recaptcha field']; + $pluginOriginalSettings['forms']['fields']['recaptcha_secretkey'] = ['type' => 'text', 'label' => 'Recaptcha Secret Key', 'help' => 'Add the recaptcha secret key here. You can get the key from the recaptcha website.', 'description' => 'The secret key is mandatory if you activate the recaptcha field']; + } + + /* get all the fields and prefill them with the dafault-data, the user-data or old input data */ + $fields = $fieldsModel->getFields($userSettings, 'plugins', $pluginName, $pluginOriginalSettings); + + /* overwrite original plugin form definitions with enhanced form objects */ + $plugins[$pluginName]['forms']['fields'] = $fields; + } + } + + $user = new User(); + $users = $user->getUsers(); + $route = $request->getAttribute('route'); + + return $this->render($response, 'settings/plugins.twig', array('settings' => $userSettings, 'plugins' => $plugins, 'users' => $users, 'route' => $route->getName() )); + } + + /************************************* + ** SAVE THEME- AND PLUGIN-SETTINGS ** + *************************************/ + + public function saveThemes($request, $response, $args) + { + if($request->isPost()) + { + $referer = $request->getHeader('HTTP_REFERER'); + $uri = $request->getUri(); + $base_url = $uri->getBaseUrl(); + + # users should not be able to fake post with settings from other typemill pages. + if(!isset($referer[0]) OR $referer[0] !== $base_url . '/tm/themes' ) + { + $this->c->flash->addMessage('error', 'illegal referer'); + return $response->withRedirect($this->c->router->pathFor('themes.show')); + } + + $userSettings = \Typemill\Settings::getUserSettings(); + $params = $request->getParams(); + $themeName = isset($params['theme']) ? $params['theme'] : false; + $userInput = isset($params[$themeName]) ? $params[$themeName] : false; + $validate = new Validation(); + $themeSettings = \Typemill\Settings::getObjectSettings('themes', $themeName); + + if(isset($themeSettings['settings']['images'])) + { + # get the default settings + $defaultSettings = \Typemill\Settings::getDefaultSettings(); + + # merge the default image settings with the theme image settings, delete all others (image settings from old theme) + $userSettings['images'] = array_merge($defaultSettings['images'], $themeSettings['settings']['images']); + } + + /* set theme name and delete theme settings from user settings for the case, that the new theme has no settings */ + $userSettings['theme'] = $themeName; + + if($userInput) + { + /* validate the user-input */ + $this->validateInput('themes', $themeName, $userInput, $validate); + + /* set user input as theme settings */ + $userSettings['themes'][$themeName] = $userInput; + } + + /* check for errors and redirect to path, if errors found */ + if(isset($_SESSION['errors'])) + { + $this->c->flash->addMessage('error', 'Please correct the errors'); + return $response->withRedirect($this->c->router->pathFor('themes.show')); + } + + /* store updated settings */ + \Typemill\Settings::updateSettings($userSettings); + + $this->c->flash->addMessage('info', 'Settings are stored'); + return $response->withRedirect($this->c->router->pathFor('themes.show')); + } + } + + public function savePlugins($request, $response, $args) + { + if($request->isPost()) + { + $referer = $request->getHeader('HTTP_REFERER'); + $uri = $request->getUri(); + $base_url = $uri->getBaseUrl(); + + # security, users should not be able to fake post with settings from other typemill pages. + if(!isset($referer[0]) OR $referer[0] !== $base_url . '/tm/plugins' ) + { + $this->c->flash->addMessage('error', 'illegal referer'); + return $response->withRedirect($this->c->router->pathFor('plugins.show')); + } + + $userSettings = \Typemill\Settings::getUserSettings(); + $pluginSettings = array(); + $userInput = $request->getParams(); + $validate = new Validation(); + + /* use the stored user settings and iterate over all original plugin settings, so we do not forget any... */ + foreach($userSettings['plugins'] as $pluginName => $pluginUserSettings) + { + /* if there are no input-data for this plugin, then use the stored plugin settings */ + if(!isset($userInput[$pluginName])) + { + $pluginSettings[$pluginName] = $pluginUserSettings; + } + else + { + /* validate the user-input */ + $this->validateInput('plugins', $pluginName, $userInput[$pluginName], $validate); + + /* use the input data */ + $pluginSettings[$pluginName] = $userInput[$pluginName]; + } + + /* deactivate the plugin, if there is no active flag */ + if(!isset($userInput[$pluginName]['active'])) + { + $pluginSettings[$pluginName]['active'] = false; + } + } + + if(isset($_SESSION['errors'])) + { + $this->c->flash->addMessage('error', 'Please correct the errors below'); + } + else + { + /* if everything is valid, add plugin settings to base settings again */ + $userSettings['plugins'] = $pluginSettings; + + /* store updated settings */ + \Typemill\Settings::updateSettings($userSettings); + + $this->c->flash->addMessage('info', 'Settings are stored'); + } + + return $response->withRedirect($this->c->router->pathFor('plugins.show')); + } + } + + private function validateInput($objectType, $objectName, $userInput, $validate) + { + /* fetch the original settings from the folder (plugin or theme) to get the field definitions */ + $originalSettings = \Typemill\Settings::getObjectSettings($objectType, $objectName); + + if(isset($originalSettings['forms']['fields'])) + { + /* flaten the multi-dimensional array with fieldsets to a one-dimensional array */ + $originalFields = array(); + foreach($originalSettings['forms']['fields'] as $fieldName => $fieldValue) + { + if(isset($fieldValue['fields'])) + { + foreach($fieldValue['fields'] as $subFieldName => $subFieldValue) + { + $originalFields[$subFieldName] = $subFieldValue; + } + } + else + { + $originalFields[$fieldName] = $fieldValue; + } + } + + # if the plugin defines frontend fields + if(isset($originalSettings['public'])) + { + $originalFields['recaptcha'] = ['type' => 'checkbox', 'label' => 'Google Recaptcha', 'checkboxlabel' => 'Activate Recaptcha' ]; + $originalFields['recaptcha_webkey'] = ['type' => 'text', 'label' => 'Recaptcha Website Key', 'help' => 'Add the recaptcha website key here. You can get the key from the recaptcha website.', 'description' => 'The website key is mandatory if you activate the recaptcha field']; + $originalFields['recaptcha_secretkey'] = ['type' => 'text', 'label' => 'Recaptcha Secret Key', 'help' => 'Add the recaptcha secret key here. You can get the key from the recaptcha website.', 'description' => 'The secret key is mandatory if you activate the recaptcha field']; + } + + /* take the user input data and iterate over all fields and values */ + foreach($userInput as $fieldName => $fieldValue) + { + /* get the corresponding field definition from original plugin settings */ + $fieldDefinition = isset($originalFields[$fieldName]) ? $originalFields[$fieldName] : false; + + if($fieldDefinition) + { + /* validate user input for this field */ + $validate->objectField($fieldName, $fieldValue, $objectName, $fieldDefinition); + } + if(!$fieldDefinition && $fieldName != 'active') + { + $_SESSION['errors'][$objectName][$fieldName] = array('This field is not defined!'); + } + } + } + } + + /*********************** + ** USER MANAGEMENT ** + ***********************/ + + public function showUser($request, $response, $args) + { + if($_SESSION['role'] == 'editor' && $_SESSION['user'] !== $args['username']) + { + return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $_SESSION['user']] )); + } + + $validate = new Validation(); + + if($validate->username($args['username'])) + { + $user = new User(); + $users = $user->getUsers(); + $userrole = $user->getUserroles(); + $userdata = $user->getUser($args['username']); + $settings = $this->c->get('settings'); + + if($userdata) + { + return $this->render($response, 'settings/user.twig', array('settings' => $settings, 'users' => $users, 'userdata' => $userdata, 'userrole' => $userrole, 'username' => $args['username'] )); + } + } + + $this->c->flash->addMessage('error', 'User does not exists'); + return $response->withRedirect($this->c->router->pathFor('user.list')); + } + + public function listUser($request, $response) + { + $user = new User(); + $users = $user->getUsers(); + $userdata = array(); + $route = $request->getAttribute('route'); + $settings = $this->c->get('settings'); + + foreach($users as $username) + { + $userdata[] = $user->getUser($username); + } + + return $this->render($response, 'settings/userlist.twig', array('settings' => $settings, 'users' => $users, 'userdata' => $userdata, 'route' => $route->getName() )); + } + + public function newUser($request, $response, $args) + { + $user = new User(); + $users = $user->getUsers(); + $userrole = $user->getUserroles(); + $route = $request->getAttribute('route'); + $settings = $this->c->get('settings'); + + return $this->render($response, 'settings/usernew.twig', array('settings' => $settings, 'users' => $users, 'userrole' => $userrole, 'route' => $route->getName() )); + } + + public function createUser($request, $response, $args) + { + if($request->isPost()) + { + $referer = $request->getHeader('HTTP_REFERER'); + $uri = $request->getUri(); + $base_url = $uri->getBaseUrl(); + + # security, users should not be able to fake post with settings from other typemill pages. + if(!isset($referer[0]) OR $referer[0] !== $base_url . '/tm/user/new' ) + { + $this->c->flash->addMessage('error', 'illegal referer'); + return $response->withRedirect($this->c->router->pathFor('user.new')); + } + + $params = $request->getParams(); + $user = new User(); + $userroles = $user->getUserroles(); + $validate = new Validation(); + + if($validate->newUser($params, $userroles)) + { + $userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']); + + $user->createUser($userdata); + + $this->c->flash->addMessage('info', 'Welcome, there is a new user!'); + return $response->withRedirect($this->c->router->pathFor('user.list')); + } + + $this->c->flash->addMessage('error', 'Please correct your input'); + return $response->withRedirect($this->c->router->pathFor('user.new')); + } + } + + public function updateUser($request, $response, $args) + { + if($request->isPost()) + { + $referer = $request->getHeader('HTTP_REFERER'); + $uri = $request->getUri(); + $base_url = $uri->getBaseUrl(); + + # security, users should not be able to fake post with settings from other typemill pages. + if(!isset($referer[0]) OR strpos($referer[0], $base_url . '/tm/user/') === false ) + { + $this->c->flash->addMessage('error', 'illegal referer'); + return $response->withRedirect($this->c->router->pathFor('user.list')); + } + + $params = $request->getParams(); + $user = new User(); + $userroles = $user->getUserroles(); + $validate = new Validation(); + + /* non admins have different update rights */ + if($_SESSION['role'] !== 'administrator') + { + /* if an editor tries to update other userdata than its own */ + if($_SESSION['user'] !== $params['username']) + { + return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $_SESSION['user']] )); + } + + /* non admins cannot change his userrole */ + $params['userrole'] = $_SESSION['role']; + } + + if($validate->existingUser($params, $userroles)) + { + $userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole']); + + if(empty($params['password']) AND empty($params['newpassword'])) + { + $user->updateUser($userdata); + $this->c->flash->addMessage('info', 'Saved all changes'); + return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']])); + } + elseif($validate->newPassword($params)) + { + $userdata['password'] = $params['newpassword']; + $user->updateUser($userdata); + $this->c->flash->addMessage('info', 'Saved all changes'); + return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']])); + } + } + + $this->c->flash->addMessage('error', 'Please correct your input'); + return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']])); + } + } + + public function deleteUser($request, $response, $args) + { + if($request->isPost()) + { + $referer = $request->getHeader('HTTP_REFERER'); + $uri = $request->getUri(); + $base_url = $uri->getBaseUrl(); + + # security, users should not be able to fake post with settings from other typemill pages. + if(!isset($referer[0]) OR strpos($referer[0], $base_url . '/tm/user/') === false ) + { + $this->c->flash->addMessage('error', 'illegal referer'); + return $response->withRedirect($this->c->router->pathFor('user.list')); + } + + $params = $request->getParams(); + $validate = new Validation(); + $user = new User(); + + /* non admins have different update rights */ + if($_SESSION['role'] !== 'administrator') + { + /* if an editor tries to delete other user than its own */ + if($_SESSION['user'] !== $params['username']) + { + return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $_SESSION['user']] )); + } + } + + if($validate->username($params['username'])) + { + $user->deleteUser($params['username']); + + $this->c->flash->addMessage('info', 'Say goodbye, the user is gone!'); + return $response->withRedirect($this->c->router->pathFor('user.list')); + } + + $this->c->flash->addMessage('error', 'Ups, we did not find that user'); + return $response->withRedirect($this->c->router->pathFor('user.show', ['username' => $params['username']])); + } + } + + private function getThemes() + { + $themeFolder = $this->c->get('settings')['rootPath'] . $this->c->get('settings')['themeFolder']; + $themeFolderC = scandir($themeFolder); + $themes = array(); + foreach ($themeFolderC as $key => $theme) + { + if (!in_array($theme, array(".",".."))) + { + if (is_dir($themeFolder . DIRECTORY_SEPARATOR . $theme)) + { + $themes[] = $theme; + } + } + } + return $themes; + } + + private function getCopyright() + { + return array( + "©", + "CC-BY", + "CC-BY-NC", + "CC-BY-NC-ND", + "CC-BY-NC-SA", + "CC-BY-ND", + "CC-BY-SA", + "None" + ); + } + + private function getLanguages() + { + return array( + 'ab' => 'Abkhazian', + 'aa' => 'Afar', + 'af' => 'Afrikaans', + 'ak' => 'Akan', + 'sq' => 'Albanian', + 'am' => 'Amharic', + 'ar' => 'Arabic', + 'an' => 'Aragonese', + 'hy' => 'Armenian', + 'as' => 'Assamese', + 'av' => 'Avaric', + 'ae' => 'Avestan', + 'ay' => 'Aymara', + 'az' => 'Azerbaijani', + 'bm' => 'Bambara', + 'ba' => 'Bashkir', + 'eu' => 'Basque', + 'be' => 'Belarusian', + 'bn' => 'Bengali', + 'bh' => 'Bihari languages', + 'bi' => 'Bislama', + 'bs' => 'Bosnian', + 'br' => 'Breton', + 'bg' => 'Bulgarian', + 'my' => 'Burmese', + 'ca' => 'Catalan, Valencian', + 'km' => 'Central Khmer', + 'ch' => 'Chamorro', + 'ce' => 'Chechen', + 'ny' => 'Chichewa, Chewa, Nyanja', + 'zh' => 'Chinese', + 'cu' => 'Church Slavonic, Old Bulgarian, Old Church Slavonic', + 'cv' => 'Chuvash', + 'kw' => 'Cornish', + 'co' => 'Corsican', + 'cr' => 'Cree', + 'hr' => 'Croatian', + 'cs' => 'Czech', + 'da' => 'Danish', + 'dv' => 'Divehi, Dhivehi, Maldivian', + 'nl' => 'Dutch, Flemish', + 'dz' => 'Dzongkha', + 'en' => 'English', + 'eo' => 'Esperanto', + 'et' => 'Estonian', + 'ee' => 'Ewe', + 'fo' => 'Faroese', + 'fj' => 'Fijian', + 'fi' => 'Finnish', + 'fr' => 'French', + 'ff' => 'Fulah', + 'gd' => 'Gaelic, Scottish Gaelic', + 'gl' => 'Galician', + 'lg' => 'Ganda', + 'ka' => 'Georgian', + 'de' => 'German', + 'ki' => 'Gikuyu, Kikuyu', + 'el' => 'Greek (Modern)', + 'kl' => 'Greenlandic, Kalaallisut', + 'gn' => 'Guarani', + 'gu' => 'Gujarati', + 'ht' => 'Haitian, Haitian Creole', + 'ha' => 'Hausa', + 'he' => 'Hebrew', + 'hz' => 'Herero', + 'hi' => 'Hindi', + 'ho' => 'Hiri Motu', + 'hu' => 'Hungarian', + 'is' => 'Icelandic', + 'io' => 'Ido', + 'ig' => 'Igbo', + 'id' => 'Indonesian', + 'ia' => 'Interlingua (International Auxiliary Language Association)', + 'ie' => 'Interlingue', + 'iu' => 'Inuktitut', + 'ik' => 'Inupiaq', + 'ga' => 'Irish', + 'it' => 'Italian', + 'ja' => 'Japanese', + 'jv' => 'Javanese', + 'kn' => 'Kannada', + 'kr' => 'Kanuri', + 'ks' => 'Kashmiri', + 'kk' => 'Kazakh', + 'rw' => 'Kinyarwanda', + 'kv' => 'Komi', + 'kg' => 'Kongo', + 'ko' => 'Korean', + 'kj' => 'Kwanyama, Kuanyama', + 'ku' => 'Kurdish', + 'ky' => 'Kyrgyz', + 'lo' => 'Lao', + 'la' => 'Latin', + 'lv' => 'Latvian', + 'lb' => 'Letzeburgesch, Luxembourgish', + 'li' => 'Limburgish, Limburgan, Limburger', + 'ln' => 'Lingala', + 'lt' => 'Lithuanian', + 'lu' => 'Luba-Katanga', + 'mk' => 'Macedonian', + 'mg' => 'Malagasy', + 'ms' => 'Malay', + 'ml' => 'Malayalam', + 'mt' => 'Maltese', + 'gv' => 'Manx', + 'mi' => 'Maori', + 'mr' => 'Marathi', + 'mh' => 'Marshallese', + 'ro' => 'Moldovan, Moldavian, Romanian', + 'mn' => 'Mongolian', + 'na' => 'Nauru', + 'nv' => 'Navajo, Navaho', + 'nd' => 'Northern Ndebele', + 'ng' => 'Ndonga', + 'ne' => 'Nepali', + 'se' => 'Northern Sami', + 'no' => 'Norwegian', + 'nb' => 'Norwegian Bokmål', + 'nn' => 'Norwegian Nynorsk', + 'ii' => 'Nuosu, Sichuan Yi', + 'oc' => 'Occitan (post 1500)', + 'oj' => 'Ojibwa', + 'or' => 'Oriya', + 'om' => 'Oromo', + 'os' => 'Ossetian, Ossetic', + 'pi' => 'Pali', + 'pa' => 'Panjabi, Punjabi', + 'ps' => 'Pashto, Pushto', + 'fa' => 'Persian', + 'pl' => 'Polish', + 'pt' => 'Portuguese', + 'qu' => 'Quechua', + 'rm' => 'Romansh', + 'rn' => 'Rundi', + 'ru' => 'Russian', + 'sm' => 'Samoan', + 'sg' => 'Sango', + 'sa' => 'Sanskrit', + 'sc' => 'Sardinian', + 'sr' => 'Serbian', + 'sn' => 'Shona', + 'sd' => 'Sindhi', + 'si' => 'Sinhala, Sinhalese', + 'sk' => 'Slovak', + 'sl' => 'Slovenian', + 'so' => 'Somali', + 'st' => 'Sotho, Southern', + 'nr' => 'South Ndebele', + 'es' => 'Spanish, Castilian', + 'su' => 'Sundanese', + 'sw' => 'Swahili', + 'ss' => 'Swati', + 'sv' => 'Swedish', + 'tl' => 'Tagalog', + 'ty' => 'Tahitian', + 'tg' => 'Tajik', + 'ta' => 'Tamil', + 'tt' => 'Tatar', + 'te' => 'Telugu', + 'th' => 'Thai', + 'bo' => 'Tibetan', + 'ti' => 'Tigrinya', + 'to' => 'Tonga (Tonga Islands)', + 'ts' => 'Tsonga', + 'tn' => 'Tswana', + 'tr' => 'Turkish', + 'tk' => 'Turkmen', + 'tw' => 'Twi', + 'ug' => 'Uighur, Uyghur', + 'uk' => 'Ukrainian', + 'ur' => 'Urdu', + 'uz' => 'Uzbek', + 've' => 'Venda', + 'vi' => 'Vietnamese', + 'vo' => 'Volap_k', + 'wa' => 'Walloon', + 'cy' => 'Welsh', + 'fy' => 'Western Frisian', + 'wo' => 'Wolof', + 'xh' => 'Xhosa', + 'yi' => 'Yiddish', + 'yo' => 'Yoruba', + 'za' => 'Zhuang, Chuang', + 'zu' => 'Zulu' + ); + } } \ No newline at end of file diff --git a/system/Controllers/SetupController.php b/system/Controllers/SetupController.php index 18a59ec..dfe7ef5 100644 --- a/system/Controllers/SetupController.php +++ b/system/Controllers/SetupController.php @@ -1,76 +1,76 @@ -checkPath('settings'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); } - try{ $checkFolder->checkPath('settings/users'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); } - try{ $checkFolder->checkPath('content'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); } - try{ $checkFolder->checkPath('cache'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); } - try{ $checkFolder->checkPath('media'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); } - - $systemcheck = empty($systemcheck) ? false : $systemcheck; - - return $this->render($response, 'auth/setup.twig', array( 'messages' => $systemcheck )); - } - - public function create($request, $response, $args) - { - if($request->isPost()) - { - $params = $request->getParams(); - $validate = new Validation(); - $user = new User(); - - /* set user as admin */ - $params['userrole'] = 'administrator'; - - /* get userroles for validation */ - $userroles = $user->getUserroles(); - - /* validate user */ - if($validate->newUser($params, $userroles)) - { - $userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']); - - /* create initial user */ - $username = $user->createUser($userdata); - - if($username) - { - /* login user */ - $user->login($username); - - # create initial settings file - \Typemill\Settings::createSettings(); - - return $response->withRedirect($this->c->router->pathFor('setup.welcome')); - } - } - - $this->c->flash->addMessage('error', 'Please check your input and try again'); - return $response->withRedirect($this->c->router->pathFor('setup.show')); - } - } - - public function welcome($request, $response, $args) - { - /* store updated settings */ - \Typemill\Settings::updateSettings(array('welcome' => false)); - - return $this->render($response, 'auth/welcome.twig', array()); - } +checkPath('settings'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); } + try{ $checkFolder->checkPath('settings/users'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); } + try{ $checkFolder->checkPath('content'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); } + try{ $checkFolder->checkPath('cache'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); } + try{ $checkFolder->checkPath('media'); }catch(\Exception $e){ $systemcheck['error'][] = $e->getMessage(); } + + $systemcheck = empty($systemcheck) ? false : $systemcheck; + + return $this->render($response, 'auth/setup.twig', array( 'messages' => $systemcheck )); + } + + public function create($request, $response, $args) + { + if($request->isPost()) + { + $params = $request->getParams(); + $validate = new Validation(); + $user = new User(); + + /* set user as admin */ + $params['userrole'] = 'administrator'; + + /* get userroles for validation */ + $userroles = $user->getUserroles(); + + /* validate user */ + if($validate->newUser($params, $userroles)) + { + $userdata = array('username' => $params['username'], 'email' => $params['email'], 'userrole' => $params['userrole'], 'password' => $params['password']); + + /* create initial user */ + $username = $user->createUser($userdata); + + if($username) + { + /* login user */ + $user->login($username); + + # create initial settings file + \Typemill\Settings::createSettings(); + + return $response->withRedirect($this->c->router->pathFor('setup.welcome')); + } + } + + $this->c->flash->addMessage('error', 'Please check your input and try again'); + return $response->withRedirect($this->c->router->pathFor('setup.show')); + } + } + + public function welcome($request, $response, $args) + { + /* store updated settings */ + \Typemill\Settings::updateSettings(array('welcome' => false)); + + return $this->render($response, 'auth/welcome.twig', array()); + } } \ No newline at end of file diff --git a/system/Events/BaseEvent.php b/system/Events/BaseEvent.php index c05ab6d..f902f4e 100644 --- a/system/Events/BaseEvent.php +++ b/system/Events/BaseEvent.php @@ -1,25 +1,25 @@ -data = $data; - } - - public function getData() - { - return $this->data; - } - - public function setData($data) - { - $this->data = $data; - } +data = $data; + } + + public function getData() + { + return $this->data; + } + + public function setData($data) + { + $this->data = $data; + } } \ No newline at end of file diff --git a/system/Events/OnBreadcrumbLoaded.php b/system/Events/OnBreadcrumbLoaded.php index 0bbec2d..56c1fa9 100644 --- a/system/Events/OnBreadcrumbLoaded.php +++ b/system/Events/OnBreadcrumbLoaded.php @@ -1,14 +1,14 @@ -data = $data; - } - - public function getMarkdown() - { - return $this->data; - } - - public function getHTML($urlrel) - { - $parsedown = new ParsedownExtension(); - $contentArray = $parsedown->text($this->data); - $contentHTML = $parsedown->markup($contentArray, $urlrel); - - return $contentHTML; - } +data = $data; + } + + public function getMarkdown() + { + return $this->data; + } + + public function getHTML($urlrel) + { + $parsedown = new ParsedownExtension(); + $contentArray = $parsedown->text($this->data); + $contentHTML = $parsedown->markup($contentArray, $urlrel); + + return $contentHTML; + } } \ No newline at end of file diff --git a/system/Events/OnPageDeleted.php b/system/Events/OnPageDeleted.php new file mode 100644 index 0000000..0240baa --- /dev/null +++ b/system/Events/OnPageDeleted.php @@ -0,0 +1,14 @@ +BlockTypes['\\'][] = 'Math'; - $this->BlockTypes['$'][] = 'Math'; - - $this->InlineTypes['\\'][] = 'Math'; - $this->InlineTypes['$'][] = 'Math'; - $this->inlineMarkerList .= '\\'; - $this->inlineMarkerList .= '$'; - - # table of content support - array_unshift($this->BlockTypes['['], 'TableOfContents'); - } - - public function text($text) - { - $Elements = $this->textElements($text); - - return $Elements; - } - - public function markup($Elements, $relurl) - { - - # make relurl available for other functions - $this->relurl = $relurl; - - # convert to markup - $markup = $this->elements($Elements); - - # trim line breaks - $markup = trim($markup, "\n"); - - # merge consecutive dl elements - $markup = preg_replace('/<\/dl>\s+
\s+/', '', $markup); - - # create table of contents - if(isset($this->DefinitionData['TableOfContents'])) - { - $TOC = $this->buildTOC($this->headlines); - - $markup = preg_replace('%(]*>\[TOC\]

)%i', $TOC, $markup); - } - - # add footnotes - if (isset($this->DefinitionData['Footnote'])) - { - $Element = $this->buildFootnoteElement(); - - $markup .= "\n" . $this->element($Element); - } - - return $markup; - } - - - public function getFootnotes() - { - # add footnotes - if (isset($this->DefinitionData['Footnote'])) - { - $Element = $this->buildFootnoteElement(); - - $footnotes = "\n" . $this->element($Element); - } - - return $footnotes; - } - - # TableOfContents - - protected function blockTableOfContents($line, $block) - { - if ($line['text'] == '[TOC]') - { - $this->DefinitionData['TableOfContents'] = true; - } - } - - # Header - - public $headlines = array(); - - protected function blockHeader($Line) - { - if (isset($Line['text'][1])) - { - $level = strspn($Line['text'], '#'); - - if ($level > 6) - { - return; - } - - $text = trim($Line['text'], '#'); - $headline = URLify::filter($Line['text']); - - if ($this->strictMode and isset($text[0]) and $text[0] !== ' ') - { - return; - } - - $text = trim($text, ' '); - - $Block = array( - 'element' => array( - 'name' => 'h' . min(6, $level), - 'text' => $text, - 'handler' => 'line', - 'attributes' => array( - 'id' => "$headline" - ) - ) - ); - - $this->headlines[] = array('level' => $level, 'name' => $Block['element']['name'], 'attribute' => $Block['element']['attributes']['id'], 'text' => $text); - - return $Block; - } - } - - # build the markup for table of contents - - public function buildTOC($headlines) - { - $markup = '
    '; - - foreach($headlines as $key => $headline) - { - $thisLevel = $headline['level']; - $prevLevel = $key > 0 ? $headlines[$key-1]['level'] : 1; - $nextLevel = isset($headlines[$key+1]) ? $headlines[$key+1]['level'] : 0; - - if($thisLevel > $prevLevel) - { - $markup .= '
      '; - } - - $markup .= '
    • ' . $headline['text'] . ''; - - if($thisLevel == $nextLevel) - { - $markup .= '
    • '; - } - elseif($thisLevel > $nextLevel) - { - while($thisLevel > $nextLevel) - { - $markup .= '
    '; - $thisLevel--; - } - } - } - - $markup .= '
'; - - return $markup; - } - - # - # Footnote Marker - # add absolute url - - protected function inlineFootnoteMarker($Excerpt) - { - - $element = parent::inlineFootnoteMarker($Excerpt); - - if ( ! isset($element)) - { - return null; - } - - $href = $element['element']['element']['attributes']['href']; - - $element['element']['element']['attributes']['href'] = $this->relurl . $href; - - return $element; - } - - public $footnoteCount = 0; - - public function buildFootnoteElement() - { - $Element = array( - 'name' => 'div', - 'attributes' => array('class' => 'footnotes'), - 'elements' => array( - array('name' => 'hr'), - array( - 'name' => 'ol', - 'elements' => array(), - ), - ), - ); - - uasort($this->DefinitionData['Footnote'], 'self::sortFootnotes'); - - foreach ($this->DefinitionData['Footnote'] as $definitionId => $DefinitionData) - { - if ( ! isset($DefinitionData['number'])) - { - continue; - } - - $text = $DefinitionData['text']; - - $textElements = parent::textElements($text); - - $numbers = range(1, $DefinitionData['count']); - - $backLinkElements = array(); - - foreach ($numbers as $number) - { - $backLinkElements[] = array('text' => ' '); - $backLinkElements[] = array( - 'name' => 'a', - 'attributes' => array( - 'href' => $this->relurl . "#fnref$number:$definitionId", - 'rev' => 'footnote', - 'class' => 'footnote-backref', - ), - 'rawHtml' => '↩', - 'allowRawHtmlInSafeMode' => true, - 'autobreak' => false, - ); - } - - unset($backLinkElements[0]); - - $n = count($textElements) -1; - - if ($textElements[$n]['name'] === 'p') - { - $backLinkElements = array_merge( - array( - array( - 'rawHtml' => ' ', - 'allowRawHtmlInSafeMode' => true, - ), - ), - $backLinkElements - ); - - unset($textElements[$n]['name']); - - $textElements[$n] = array( - 'name' => 'p', - 'elements' => array_merge( - array($textElements[$n]), - $backLinkElements - ), - ); - } - else - { - $textElements[] = array( - 'name' => 'p', - 'elements' => $backLinkElements - ); - } - - $Element['elements'][1]['elements'] []= array( - 'name' => 'li', - 'attributes' => array('id' => 'fn:'.$definitionId), - 'elements' => array_merge( - $textElements - ), - ); - } - - return $Element; - } - - # Inline Math - # check https://github.com/BenjaminHoegh/ParsedownMath - # check https://github.com/cben/mathdown/wiki/math-in-markdown - - protected function inlineMath($Excerpt) - { - if(preg_match('/^(? strlen($matches[0]), - 'element' => array( - 'text' => '\(' . $matches[1] . '\)', - ), - ); - } - } - - protected $specialCharacters = array( - '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '<', '>', '#', '+', '-', '.', '!', '|', '~', '^', '=' - ); - - // - // Inline Escape - // ------------------------------------------------------------------------- - protected function inlineEscapeSequence($Excerpt) - { - if (isset($Excerpt['text'][1]) - && in_array($Excerpt['text'][1], $this->specialCharacters) - && !preg_match('/(? array( - 'rawHtml' => $Excerpt['text'][1], - ), - 'extent' => 2, - ); - } - } - - # Block Math - protected function blockMath($Line) - { - $Block = array( - 'element' => array( - 'text' => '', - ), - ); - if (preg_match('/^(? 'math'); - - return $Block; - } - elseif ($Block['end'] === '$$' && preg_match('/^(? 'math'); - - return $Block; - } - - $Block['element']['text'] .= "\n" . $Line['body']; - - // ~ - return $Block; - } - - // ~ - protected function blockMathComplete($Block) - { - return $Block; - } - - # advanced attribute data, check parsedown extra plugin: https://github.com/tovic/parsedown-extra-plugin - protected function parseAttributeData($text) { - // Allow compact attributes ... - $text = str_replace(array('#', '.'), array(' #', ' .'), $text); - if (strpos($text, '="') !== false || strpos($text, '=\'') !== false) { - $text = preg_replace_callback('#([-\w]+=)(["\'])([^\n]*?)\2#', function($m) { - $s = str_replace(array( - ' #', - ' .', - ' ' - ), array( - '#', - '.', - "\x1A" - ), $m[3]); - return $m[1] . $m[2] . $s . $m[2]; - }, $text); - } - $attrs = array(); - foreach (explode(' ', $text) as $v) { - if (!$v) continue; - // `{#foo}` - if ($v[0] === '#' && isset($v[1])) { - $attrs['id'] = substr($v, 1); - // `{.foo}` - } else if ($v[0] === '.' && isset($v[1])) { - $attrs['class'][] = substr($v, 1); - // ~ - } else if (strpos($v, '=') !== false) { - $vv = explode('=', $v, 2); - // `{foo=}` - if ($vv[1] === "") { - $attrs[$vv[0]] = ""; - // `{foo="bar baz"}` - // `{foo='bar baz'}` - } else if ($vv[1][0] === '"' && substr($vv[1], -1) === '"' || $vv[1][0] === "'" && substr($vv[1], -1) === "'") { - $attrs[$vv[0]] = str_replace("\x1A", ' ', substr(substr($vv[1], 1), 0, -1)); - // `{foo=bar}` - } else { - $attrs[$vv[0]] = $vv[1]; - } - // `{foo}` - } else { - $attrs[$v] = $v; - } - } - if (isset($attrs['class'])) { - $attrs['class'] = implode(' ', $attrs['class']); - } - return $attrs; - } - - protected $regexAttribute = '(?:[#.][-\w:\\\]+[ ]*|[-\w:\\\]+(?:=(?:["\'][^\n]*?["\']|[^\s]+)?)?[ ]*)'; - - # turn markdown into an array of markdown blocks for typemill edit mode - function markdownToArrayBlocks($markdown) - { - # standardize line breaks - $markdown = str_replace(array("\r\n", "\r"), "\n", $markdown); - - # remove surrounding line breaks - $markdown = trim($markdown, "\n"); - - # trim to maximum two linebreaks - - # split text into blocks - $blocks = explode("\n\n", $markdown); - - # clean up code blocks - $cleanBlocks = array(); - - # holds the content of codeblocks - $codeBlock = ''; - - # flag if codeblock is on or off. - $codeBlockOn = false; - - # holds the content of a definition list - $definitionList = ""; - - # flag if definition-list is on or off. - $definitionListOn = false; - - foreach($blocks as $block) - { - # remove empty lines - if (chop($block) === '') continue; - - # if the block starts with a fenced code - if(substr($block,0,2) == '``') - { - # and if we are in an open code-block - if($codeBlockOn) - { - # it must be the end of the codeblock, so add it to the codeblock - $block = $codeBlock . "\n" . $block; - - # reset codeblock-value and close the codeblock. - $codeBlock = ''; - $codeBlockOn = false; - } - else - { - # it must be the start of the codeblock. - $codeBlockOn = true; - } - } - if($codeBlockOn) - { - # if the codeblock is complete - if($this->isComplete($block)) - { - $block = $codeBlock . "\n" . $block; - - # reset codeblock-value and close the codeblock. - $codeBlock = ''; - $codeBlockOn = false; - } - else - { - $codeBlock .= "\n" . $block; - continue; - } - } - - # handle definition lists - $checkDL = preg_split('/\r\n|\r|\n/',$block); - if(isset($checkDL[1]) && substr($checkDL[1],0,2) == ': ') - { - $definitionList .= $block . "\n\n"; - $definitionListOn = true; - continue; - } - elseif($definitionListOn) - { - $cleanBlocks[] = $definitionList; - $definitionList = ""; - $definitionListOn = false; - } - - $block = trim($block, "\n"); - - $cleanBlocks[] = $block; - } - return $cleanBlocks; - } - - public function arrayBlocksToMarkdown(array $arrayBlocks) - { - $markdown = ''; - - foreach($arrayBlocks as $block) - { - $markdown .= $block . "\n\n"; - } - - return $markdown; - } - - protected function isComplete($codeblock) - { - $lines = explode("\n", $codeblock); - if(count($lines) > 1) - { - $lastLine = array_pop($lines); - if(substr($lastLine,0,2) == '``') - { - return true; - } - return false; - } - return false; - } +BlockTypes['\\'][] = 'Math'; + $this->BlockTypes['$'][] = 'Math'; + + $this->InlineTypes['\\'][] = 'Math'; + $this->InlineTypes['$'][] = 'Math'; + $this->inlineMarkerList .= '\\'; + $this->inlineMarkerList .= '$'; + + $this->visualMode = false; + + # table of content support + array_unshift($this->BlockTypes['['], 'TableOfContents'); + } + + public function setVisualMode() + { + $this->visualMode = true; + } + + public function text($text) + { + $Elements = $this->textElements($text); + + return $Elements; + } + + public function markup($Elements, $relurl) + { + + # make relurl available for other functions + $this->relurl = $relurl; + + # convert to markup + $markup = $this->elements($Elements); + + # trim line breaks + $markup = trim($markup, "\n"); + + # merge consecutive dl elements + $markup = preg_replace('/<\/dl>\s+
\s+/', '', $markup); + + # create table of contents + if(isset($this->DefinitionData['TableOfContents'])) + { + $TOC = $this->buildTOC($this->headlines); + + $markup = preg_replace('%(]*>\[TOC\]

)%i', $TOC, $markup); + } + + # add footnotes + if (isset($this->DefinitionData['Footnote'])) + { + $Element = $this->buildFootnoteElement(); + + $markup .= "\n" . $this->element($Element); + } + + return $markup; + } + + + public function getFootnotes() + { + # add footnotes + if (isset($this->DefinitionData['Footnote'])) + { + $Element = $this->buildFootnoteElement(); + + $footnotes = "\n" . $this->element($Element); + } + + return $footnotes; + } + + # TableOfContents + + protected function blockTableOfContents($line, $block) + { + if ($line['text'] == '[TOC]') + { + $this->DefinitionData['TableOfContents'] = true; + } + } + + # Header + + public $headlines = array(); + + protected function blockHeader($Line) + { + if (isset($Line['text'][1])) + { + $level = strspn($Line['text'], '#'); + + if ($level > 6) + { + return; + } + + $text = trim($Line['text'], '#'); + $headline = URLify::filter($Line['text']); + + if ($this->strictMode and isset($text[0]) and $text[0] !== ' ') + { + return; + } + + $text = trim($text, ' '); + + $Block = array( + 'element' => array( + 'name' => 'h' . min(6, $level), + 'text' => $text, + 'handler' => 'line', + 'attributes' => array( + 'id' => "$headline" + ) + ) + ); + + $this->headlines[] = array('level' => $level, 'name' => $Block['element']['name'], 'attribute' => $Block['element']['attributes']['id'], 'text' => $text); + + return $Block; + } + } + + # build the markup for table of contents + + public function buildTOC($headlines) + { + $markup = '
    '; + + foreach($headlines as $key => $headline) + { + $thisLevel = $headline['level']; + $prevLevel = $key > 0 ? $headlines[$key-1]['level'] : 1; + $nextLevel = isset($headlines[$key+1]) ? $headlines[$key+1]['level'] : 0; + + if($thisLevel > $prevLevel) + { + $markup .= '
      '; + } + + $markup .= '
    • ' . $headline['text'] . ''; + + if($thisLevel == $nextLevel) + { + $markup .= '
    • '; + } + elseif($thisLevel > $nextLevel) + { + while($thisLevel > $nextLevel) + { + $markup .= '
    '; + $thisLevel--; + } + } + } + + $markup .= '
'; + + return $markup; + } + + # + # Footnote Marker + # add absolute url + + protected function inlineFootnoteMarker($Excerpt) + { + + $element = parent::inlineFootnoteMarker($Excerpt); + + if ( ! isset($element)) + { + return null; + } + + $href = $element['element']['element']['attributes']['href']; + + $element['element']['element']['attributes']['href'] = $this->relurl . $href; + + return $element; + } + + public $footnoteCount = 0; + + public function buildFootnoteElement() + { + $Element = array( + 'name' => 'div', + 'attributes' => array('class' => 'footnotes'), + 'elements' => array( + array('name' => 'hr'), + array( + 'name' => 'ol', + 'elements' => array(), + ), + ), + ); + + uasort($this->DefinitionData['Footnote'], 'self::sortFootnotes'); + + foreach ($this->DefinitionData['Footnote'] as $definitionId => $DefinitionData) + { + if ( ! isset($DefinitionData['number'])) + { + # fix the footnote logic in visual mode, this might break for more complex footnotes. + if($this->visualMode) + { + $DefinitionData['number'] = $definitionId; + $DefinitionData['count'] = 1; + } + else + { + continue; + } + } + + $text = $DefinitionData['text']; + + $textElements = parent::textElements($text); + + $numbers = range(1, $DefinitionData['count']); + + $backLinkElements = array(); + + foreach ($numbers as $number) + { + $backLinkElements[] = array('text' => ' '); + $backLinkElements[] = array( + 'name' => 'a', + 'attributes' => array( + 'href' => $this->relurl . "#fnref$number:$definitionId", + 'rev' => 'footnote', + 'class' => 'footnote-backref', + ), + 'rawHtml' => '↩', + 'allowRawHtmlInSafeMode' => true, + 'autobreak' => false, + ); + } + + unset($backLinkElements[0]); + + $n = count($textElements) -1; + + if ($textElements[$n]['name'] === 'p') + { + $backLinkElements = array_merge( + array( + array( + 'rawHtml' => ' ', + 'allowRawHtmlInSafeMode' => true, + ), + ), + $backLinkElements + ); + + unset($textElements[$n]['name']); + + $textElements[$n] = array( + 'name' => 'p', + 'elements' => array_merge( + array($textElements[$n]), + $backLinkElements + ), + ); + } + else + { + $textElements[] = array( + 'name' => 'p', + 'elements' => $backLinkElements + ); + } + + $Element['elements'][1]['elements'] []= array( + 'name' => 'li', + 'attributes' => array('id' => 'fn:'.$definitionId), + 'elements' => array_merge( + $textElements + ), + ); + } + + return $Element; + } + + # Inline Math + # check https://github.com/BenjaminHoegh/ParsedownMath + # check https://github.com/cben/mathdown/wiki/math-in-markdown + + protected function inlineMath($Excerpt) + { + if(preg_match('/^(? strlen($matches[0]), + 'element' => array( + 'text' => '\(' . $matches[1] . '\)', + ), + ); + } + } + + protected $specialCharacters = array( + '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '<', '>', '#', '+', '-', '.', '!', '|', '~', '^', '=' + ); + + // + // Inline Escape + // ------------------------------------------------------------------------- + protected function inlineEscapeSequence($Excerpt) + { + if (isset($Excerpt['text'][1]) + && in_array($Excerpt['text'][1], $this->specialCharacters) + && !preg_match('/(? array( + 'rawHtml' => $Excerpt['text'][1], + ), + 'extent' => 2, + ); + } + } + + # Block Math + protected function blockMath($Line) + { + $Block = array( + 'element' => array( + 'text' => '', + ), + ); + if (preg_match('/^(? 'math'); + + return $Block; + } + elseif ($Block['end'] === '$$' && preg_match('/^(? 'math'); + + return $Block; + } + + $Block['element']['text'] .= "\n" . $Line['body']; + + // ~ + return $Block; + } + + // ~ + protected function blockMathComplete($Block) + { + return $Block; + } + + # advanced attribute data, check parsedown extra plugin: https://github.com/tovic/parsedown-extra-plugin + protected function parseAttributeData($text) { + // Allow compact attributes ... + $text = str_replace(array('#', '.'), array(' #', ' .'), $text); + if (strpos($text, '="') !== false || strpos($text, '=\'') !== false) { + $text = preg_replace_callback('#([-\w]+=)(["\'])([^\n]*?)\2#', function($m) { + $s = str_replace(array( + ' #', + ' .', + ' ' + ), array( + '#', + '.', + "\x1A" + ), $m[3]); + return $m[1] . $m[2] . $s . $m[2]; + }, $text); + } + $attrs = array(); + foreach (explode(' ', $text) as $v) { + if (!$v) continue; + // `{#foo}` + if ($v[0] === '#' && isset($v[1])) { + $attrs['id'] = substr($v, 1); + // `{.foo}` + } else if ($v[0] === '.' && isset($v[1])) { + $attrs['class'][] = substr($v, 1); + // ~ + } else if (strpos($v, '=') !== false) { + $vv = explode('=', $v, 2); + // `{foo=}` + if ($vv[1] === "") { + $attrs[$vv[0]] = ""; + // `{foo="bar baz"}` + // `{foo='bar baz'}` + } else if ($vv[1][0] === '"' && substr($vv[1], -1) === '"' || $vv[1][0] === "'" && substr($vv[1], -1) === "'") { + $attrs[$vv[0]] = str_replace("\x1A", ' ', substr(substr($vv[1], 1), 0, -1)); + // `{foo=bar}` + } else { + $attrs[$vv[0]] = $vv[1]; + } + // `{foo}` + } else { + $attrs[$v] = $v; + } + } + if (isset($attrs['class'])) { + $attrs['class'] = implode(' ', $attrs['class']); + } + return $attrs; + } + + protected $regexAttribute = '(?:[#.][-\w:\\\]+[ ]*|[-\w:\\\]+(?:=(?:["\'][^\n]*?["\']|[^\s]+)?)?[ ]*)'; + + # turn markdown into an array of markdown blocks for typemill edit mode + function markdownToArrayBlocks($markdown) + { + # standardize line breaks + $markdown = str_replace(array("\r\n", "\r"), "\n", $markdown); + + # remove surrounding line breaks + $markdown = trim($markdown, "\n"); + + # trim to maximum two linebreaks + + # split text into blocks + $blocks = explode("\n\n", $markdown); + + # clean up code blocks + $cleanBlocks = array(); + + # holds the content of codeblocks + $codeBlock = ''; + + # flag if codeblock is on or off. + $codeBlockOn = false; + + # holds the content of a definition list + $definitionList = ""; + + # flag if definition-list is on or off. + $definitionListOn = false; + + foreach($blocks as $block) + { + # remove empty lines + if (chop($block) === '') continue; + + # if the block starts with a fenced code + if(substr($block,0,2) == '``') + { + # and if we are in an open code-block + if($codeBlockOn) + { + # it must be the end of the codeblock, so add it to the codeblock + $block = $codeBlock . "\n" . $block; + + # reset codeblock-value and close the codeblock. + $codeBlock = ''; + $codeBlockOn = false; + } + else + { + # it must be the start of the codeblock. + $codeBlockOn = true; + } + } + if($codeBlockOn) + { + # if the codeblock is complete + if($this->isComplete($block)) + { + $block = $codeBlock . "\n" . $block; + + # reset codeblock-value and close the codeblock. + $codeBlock = ''; + $codeBlockOn = false; + } + else + { + $codeBlock .= "\n" . $block; + continue; + } + } + + # handle definition lists + $checkDL = preg_split('/\r\n|\r|\n/',$block); + if(isset($checkDL[1]) && substr($checkDL[1],0,2) == ': ') + { + $definitionList .= $block . "\n\n"; + $definitionListOn = true; + continue; + } + elseif($definitionListOn) + { + $cleanBlocks[] = $definitionList; + $definitionList = ""; + $definitionListOn = false; + } + + $block = trim($block, "\n"); + + $cleanBlocks[] = $block; + } + return $cleanBlocks; + } + + public function arrayBlocksToMarkdown(array $arrayBlocks) + { + $markdown = ''; + + foreach($arrayBlocks as $block) + { + $markdown .= $block . "\n\n"; + } + + return $markdown; + } + + protected function isComplete($codeblock) + { + $lines = explode("\n", $codeblock); + if(count($lines) > 1) + { + $lastLine = array_pop($lines); + if(substr($lastLine,0,2) == '``') + { + return true; + } + return false; + } + return false; + } } \ No newline at end of file diff --git a/system/Extensions/TwigCsrfExtension.php b/system/Extensions/TwigCsrfExtension.php index 0418815..70faa98 100644 --- a/system/Extensions/TwigCsrfExtension.php +++ b/system/Extensions/TwigCsrfExtension.php @@ -1,31 +1,31 @@ -guard = $guard; - } - - public function getFunctions() - { - return [ - new \Twig_SimpleFunction('csrf_field', array($this, 'csrfField' )) - ]; - } - - public function csrfField() - { - return ' - - - '; - } +guard = $guard; + } + + public function getFunctions() + { + return [ + new \Twig_SimpleFunction('csrf_field', array($this, 'csrfField' )) + ]; + } + + public function csrfField() + { + return ' + + + '; + } } \ No newline at end of file diff --git a/system/Extensions/TwigMarkdownExtension.php b/system/Extensions/TwigMarkdownExtension.php index efc7345..4d0c0c7 100644 --- a/system/Extensions/TwigMarkdownExtension.php +++ b/system/Extensions/TwigMarkdownExtension.php @@ -1,24 +1,24 @@ -text($markdown); - - return $parsedown->markup($markdownArray, false); - } +text($markdown); + + return $parsedown->markup($markdownArray, false); + } } \ No newline at end of file diff --git a/system/Extensions/TwigUserExtension.php b/system/Extensions/TwigUserExtension.php index 18bdb46..4a7b04d 100644 --- a/system/Extensions/TwigUserExtension.php +++ b/system/Extensions/TwigUserExtension.php @@ -1,34 +1,34 @@ -view = $view; - } - - public function __invoke(Request $request, Response $response, $next) - { - if(isset($_SESSION['old'])) - { - $this->view->getEnvironment()->addGlobal('old', $_SESSION['old']); - } - if(!empty($request->getParams())) - { - $_SESSION['old'] = $request->getParams(); - } - - $response = $next($request, $response); - return $response; - } +view = $view; + } + + public function __invoke(Request $request, Response $response, $next) + { + if(isset($_SESSION['old'])) + { + $this->view->getEnvironment()->addGlobal('old', $_SESSION['old']); + } + if(!empty($request->getParams())) + { + $_SESSION['old'] = $request->getParams(); + } + + $response = $next($request, $response); + return $response; + } } \ No newline at end of file diff --git a/system/Middleware/RedirectIfAuthenticated.php b/system/Middleware/RedirectIfAuthenticated.php index b9b73d9..f7bdfe1 100644 --- a/system/Middleware/RedirectIfAuthenticated.php +++ b/system/Middleware/RedirectIfAuthenticated.php @@ -1,27 +1,27 @@ -router = $router; - } - - public function __invoke(Request $request, Response $response, $next) - { - if(isset($_SESSION['login'])) - { - $response = $response->withRedirect($this->router->pathFor('content.raw')); - } - - return $next($request, $response); - } +router = $router; + } + + public function __invoke(Request $request, Response $response, $next) + { + if(isset($_SESSION['login'])) + { + $response = $response->withRedirect($this->router->pathFor('content.raw')); + } + + return $next($request, $response); + } } \ No newline at end of file diff --git a/system/Middleware/RedirectIfNoAdmin.php b/system/Middleware/RedirectIfNoAdmin.php index 3102bfa..047e1a9 100644 --- a/system/Middleware/RedirectIfNoAdmin.php +++ b/system/Middleware/RedirectIfNoAdmin.php @@ -1,32 +1,32 @@ -router = $router; - } - - public function __invoke(Request $request, Response $response, $next) - { - if(!isset($_SESSION['login']) || !isset($_SESSION['role'])) - { - $response = $response->withRedirect($this->router->pathFor('auth.show')); - } - - if($_SESSION['role'] != 'administrator') - { - $response = $response->withRedirect($this->router->pathFor('content.raw')); - } - - return $next($request, $response); - } +router = $router; + } + + public function __invoke(Request $request, Response $response, $next) + { + if(!isset($_SESSION['login']) || !isset($_SESSION['role'])) + { + $response = $response->withRedirect($this->router->pathFor('auth.show')); + } + + if($_SESSION['role'] != 'administrator') + { + $response = $response->withRedirect($this->router->pathFor('content.raw')); + } + + return $next($request, $response); + } } \ No newline at end of file diff --git a/system/Middleware/RedirectIfUnauthenticated.php b/system/Middleware/RedirectIfUnauthenticated.php index bc3d8bc..8004ce2 100644 --- a/system/Middleware/RedirectIfUnauthenticated.php +++ b/system/Middleware/RedirectIfUnauthenticated.php @@ -1,27 +1,27 @@ -router = $router; - } - - public function __invoke(Request $request, Response $response, $next) - { - if(!isset($_SESSION['login'])) - { - return $response->withRedirect($this->router->pathFor('auth.show')); - } - - return $next($request, $response); - } +router = $router; + } + + public function __invoke(Request $request, Response $response, $next) + { + if(!isset($_SESSION['login'])) + { + return $response->withRedirect($this->router->pathFor('auth.show')); + } + + return $next($request, $response); + } } \ No newline at end of file diff --git a/system/Middleware/RestrictApiAccess.php b/system/Middleware/RestrictApiAccess.php index bc2e414..00adb99 100644 --- a/system/Middleware/RestrictApiAccess.php +++ b/system/Middleware/RestrictApiAccess.php @@ -1,26 +1,26 @@ -router = $router; - } - - public function __invoke(Request $request, Response $response, $next) - { - if(!isset($_SESSION['login']) || !isset($_SESSION['role'])) - { - return $response->withJson(['errors' => ['access denied']], 403); - } - return $next($request, $response); - } +router = $router; + } + + public function __invoke(Request $request, Response $response, $next) + { + if(!isset($_SESSION['login']) || !isset($_SESSION['role'])) + { + return $response->withJson(['errors' => ['access denied']], 403); + } + return $next($request, $response); + } } \ No newline at end of file diff --git a/system/Middleware/ValidationErrorsMiddleware.php b/system/Middleware/ValidationErrorsMiddleware.php index 4217c66..9010707 100644 --- a/system/Middleware/ValidationErrorsMiddleware.php +++ b/system/Middleware/ValidationErrorsMiddleware.php @@ -1,29 +1,29 @@ -view = $view; - } - - public function __invoke(Request $request, Response $response, $next) - { - if(isset($_SESSION['errors'])) - { - $this->view->getEnvironment()->addGlobal('errors', $_SESSION['errors']); - - unset($_SESSION['errors']); - } - - return $next($request, $response); - } +view = $view; + } + + public function __invoke(Request $request, Response $response, $next) + { + if(isset($_SESSION['errors'])) + { + $this->view->getEnvironment()->addGlobal('errors', $_SESSION['errors']); + + unset($_SESSION['errors']); + } + + return $next($request, $response); + } } \ No newline at end of file diff --git a/system/Models/Field.php b/system/Models/Field.php index 409aaa0..ad481c2 100644 --- a/system/Models/Field.php +++ b/system/Models/Field.php @@ -1,286 +1,286 @@ -setName($fieldName); - - $type = isset($fieldConfigs['type']) ? $fieldConfigs['type'] : false; - $this->setType($type); - - $label = isset($fieldConfigs['label']) ? $fieldConfigs['label'] : false; - $this->setLabel($label); - - $checkboxlabel = isset($fieldConfigs['checkboxlabel']) ? $fieldConfigs['checkboxlabel'] : false; - $this->setCheckboxLabel($checkboxlabel); - - $options = isset($fieldConfigs['options']) ? $fieldConfigs['options'] : array(); - $this->setOptions($options); - - $this->setAttributes($fieldConfigs); - - $this->setAttributeValues($fieldConfigs); - - $this->setHelpers($fieldConfigs); - } - - private function setName($name) - { - $this->name = $name; - } - - public function getName() - { - return $this->name; - } - - private function setType($type) - { - if(in_array($type, $this->types)) - { - $this->type = $type; - } - } - - public function getType() - { - return $this->type; - } - - public function setLabel($label) - { - $this->label = $label; - } - - public function getLabel() - { - return $this->label; - } - - public function setCheckboxLabel($label) - { - $this->checkboxLabel = $label; - } - - public function getCheckboxLabel() - { - return $this->checkboxLabel; - } - - public function setContent($content) - { - $this->content = $content; - } - - public function getContent() - { - return $this->content; - } - - private function setOptions(array $options) - { - foreach($options as $key => $value) - { - $this->options[$key] = $value; - } - } - - public function getOptions() - { - if(isset($this->options)) - { - return $this->options; - } - return false; - } - - private function setAttributes($fieldConfigs) - { - foreach($fieldConfigs as $key => $value) - { - if(is_string($key) && in_array($key, $this->attr)) - { - $this->attributes[$key] = $value; - } - } - } - - /* get all attributes of the field and return them as a string. For usage in templates */ - public function getAttributes() - { - $string = false; - - foreach($this->attributes as $key => $attribute) - { - $string .= ' ' . $key; - } - - return $string; - } - - /* set a single attribute. Used e.g. in controller to change the value */ - public function setAttribute($key, $value) - { - $this->attributes[$key] = $value; - } - - public function unsetAttribute($key) - { - unset($this->attributes[$key]); - } - - /* get a single attribute, if it is defined. For usage in templates like getAttribute('required') */ - public function getAttribute($key) - { - if(isset($this->attributes[$key])) - { - return $this->attributes[$key]; - } - - return false; - } - - private function setAttributeValues($fieldConfigs) - { - foreach($fieldConfigs as $key => $value) - { - if(is_string($key) && in_array($key, $this->attrValues)) - { - $this->attributeValues[$key] = $value; - } - } - } - - /* get all attributes as string. For usage in template */ - public function getAttributeValues() - { - $string = false; - - foreach($this->attributeValues as $key => $attribute) - { - $string .= ' ' . $key . '="' . $attribute . '"'; - } - - return $string; - } - - public function setAttributeValue($key, $value) - { - /* pretty dirty, but you should not add a value for a simple checkbox */ - if($key == 'value' && $this->type == 'checkbox') - { - return; - } - - $this->attributeValues[$key] = $value; - } - - public function getAttributeValue($key) - { - if(isset($this->attributeValues[$key])) - { - return $this->attributeValues[$key]; - } - - return false; - } - - - public function setHelpers($fieldConfigs) - { - foreach($fieldConfigs as $key => $config) - { - if(is_string($key) && in_array($key, $this->helpers)) - { - $this->$key = $config; - } - } - } - - public function getHelper($helperName) - { - if(isset($this->$helperName)) - { - return $this->$helperName; - } - return false; - } +setName($fieldName); + + $type = isset($fieldConfigs['type']) ? $fieldConfigs['type'] : false; + $this->setType($type); + + $label = isset($fieldConfigs['label']) ? $fieldConfigs['label'] : false; + $this->setLabel($label); + + $checkboxlabel = isset($fieldConfigs['checkboxlabel']) ? $fieldConfigs['checkboxlabel'] : false; + $this->setCheckboxLabel($checkboxlabel); + + $options = isset($fieldConfigs['options']) ? $fieldConfigs['options'] : array(); + $this->setOptions($options); + + $this->setAttributes($fieldConfigs); + + $this->setAttributeValues($fieldConfigs); + + $this->setHelpers($fieldConfigs); + } + + private function setName($name) + { + $this->name = $name; + } + + public function getName() + { + return $this->name; + } + + private function setType($type) + { + if(in_array($type, $this->types)) + { + $this->type = $type; + } + } + + public function getType() + { + return $this->type; + } + + public function setLabel($label) + { + $this->label = $label; + } + + public function getLabel() + { + return $this->label; + } + + public function setCheckboxLabel($label) + { + $this->checkboxLabel = $label; + } + + public function getCheckboxLabel() + { + return $this->checkboxLabel; + } + + public function setContent($content) + { + $this->content = $content; + } + + public function getContent() + { + return $this->content; + } + + private function setOptions(array $options) + { + foreach($options as $key => $value) + { + $this->options[$key] = $value; + } + } + + public function getOptions() + { + if(isset($this->options)) + { + return $this->options; + } + return false; + } + + private function setAttributes($fieldConfigs) + { + foreach($fieldConfigs as $key => $value) + { + if(is_string($key) && in_array($key, $this->attr)) + { + $this->attributes[$key] = $value; + } + } + } + + /* get all attributes of the field and return them as a string. For usage in templates */ + public function getAttributes() + { + $string = false; + + foreach($this->attributes as $key => $attribute) + { + $string .= ' ' . $key; + } + + return $string; + } + + /* set a single attribute. Used e.g. in controller to change the value */ + public function setAttribute($key, $value) + { + $this->attributes[$key] = $value; + } + + public function unsetAttribute($key) + { + unset($this->attributes[$key]); + } + + /* get a single attribute, if it is defined. For usage in templates like getAttribute('required') */ + public function getAttribute($key) + { + if(isset($this->attributes[$key])) + { + return $this->attributes[$key]; + } + + return false; + } + + private function setAttributeValues($fieldConfigs) + { + foreach($fieldConfigs as $key => $value) + { + if(is_string($key) && in_array($key, $this->attrValues)) + { + $this->attributeValues[$key] = $value; + } + } + } + + /* get all attributes as string. For usage in template */ + public function getAttributeValues() + { + $string = false; + + foreach($this->attributeValues as $key => $attribute) + { + $string .= ' ' . $key . '="' . $attribute . '"'; + } + + return $string; + } + + public function setAttributeValue($key, $value) + { + /* pretty dirty, but you should not add a value for a simple checkbox */ + if($key == 'value' && $this->type == 'checkbox') + { + return; + } + + $this->attributeValues[$key] = $value; + } + + public function getAttributeValue($key) + { + if(isset($this->attributeValues[$key])) + { + return $this->attributeValues[$key]; + } + + return false; + } + + + public function setHelpers($fieldConfigs) + { + foreach($fieldConfigs as $key => $config) + { + if(is_string($key) && in_array($key, $this->helpers)) + { + $this->$key = $config; + } + } + } + + public function getHelper($helperName) + { + if(isset($this->$helperName)) + { + return $this->$helperName; + } + return false; + } } \ No newline at end of file diff --git a/system/Models/Fields.php b/system/Models/Fields.php index d144ca3..a304e75 100644 --- a/system/Models/Fields.php +++ b/system/Models/Fields.php @@ -1,104 +1,104 @@ - $fieldConfigurations) - { - if($fieldConfigurations['type'] == 'fieldset') - { - # if it is a fieldset, then create a subset for the containing field and read them with a recursive function - $subSettings = $objectSettings; - $subSettings['forms'] = $fieldConfigurations; - - $fieldset = array(); - $fieldset['type'] = 'fieldset'; - $fieldset['legend'] = $fieldConfigurations['legend']; - $fieldset['fields'] = $this->getFields($userSettings, $objectType, $objectName, $subSettings, $formType); - $fields[] = $fieldset; - } - else - { - # For label, helptext and description you can use the value of another field. This is useful e.g. to localize the label of public forms via plugin settings. - if(isset($fieldConfigurations['label']) && isset($userSettings[$objectType][$objectName][$fieldConfigurations['label']])) - { - $fieldConfigurations['label'] = $userSettings[$objectType][$objectName][$fieldConfigurations['label']]; - } - if(isset($fieldConfigurations['help']) && isset($userSettings[$objectType][$objectName][$fieldConfigurations['help']])) - { - $fieldConfigurations['help'] = $userSettings[$objectType][$objectName][$fieldConfigurations['help']]; - } - if(isset($fieldConfigurations['description']) && isset($userSettings[$objectType][$objectName][$fieldConfigurations['description']])) - { - $fieldConfigurations['description'] = $userSettings[$objectType][$objectName][$fieldConfigurations['description']]; - } - - # for each field generate a new field object with the field name and the field configurations - $field = new Field($fieldName, $fieldConfigurations); - - # handle the value for the field - $userValue = false; - - # first, add the default value from the original plugin or theme settings - if(isset($objectSettings['settings'][$fieldName])) - { - $userValue = $objectSettings['settings'][$fieldName]; - } - - # now overwrite the default values with the user values stored in the user settings - if(isset($userSettings[$objectType][$objectName][$fieldName])) - { - $userValue = $userSettings[$objectType][$objectName][$fieldName]; - } - - # now overwrite user-values, if there are old-input values from the actual form (e.g. after input error) - if(isset($_SESSION['old'][$objectName][$fieldName])) - { - $userValue = $_SESSION['old'][$objectName][$fieldName]; - } - - # Now prepopulate the field object with the value */ - if($field->getType() == "textarea" || $field->getType() == "paragraph") - { - if($userValue) - { - $field->setContent($userValue); - } - } - elseif($field->getType() == "checkbox") - { - # checkboxes need a special treatment, because field does not exist in settings if unchecked by user - if(isset($userSettings[$objectType][$objectName][$fieldName])) - { - $field->setAttribute('checked', 'true'); - } - else - { - $field->unsetAttribute('chhecked'); - } - } - else - { - $field->setAttributeValue('value', $userValue); - } - - # add the field to the field-List - $fields[] = $field; - - } - } - return $fields; - } + $fieldConfigurations) + { + if($fieldConfigurations['type'] == 'fieldset') + { + # if it is a fieldset, then create a subset for the containing field and read them with a recursive function + $subSettings = $objectSettings; + $subSettings['forms'] = $fieldConfigurations; + + $fieldset = array(); + $fieldset['type'] = 'fieldset'; + $fieldset['legend'] = $fieldConfigurations['legend']; + $fieldset['fields'] = $this->getFields($userSettings, $objectType, $objectName, $subSettings, $formType); + $fields[] = $fieldset; + } + else + { + # For label, helptext and description you can use the value of another field. This is useful e.g. to localize the label of public forms via plugin settings. + if(isset($fieldConfigurations['label']) && isset($userSettings[$objectType][$objectName][$fieldConfigurations['label']])) + { + $fieldConfigurations['label'] = $userSettings[$objectType][$objectName][$fieldConfigurations['label']]; + } + if(isset($fieldConfigurations['help']) && isset($userSettings[$objectType][$objectName][$fieldConfigurations['help']])) + { + $fieldConfigurations['help'] = $userSettings[$objectType][$objectName][$fieldConfigurations['help']]; + } + if(isset($fieldConfigurations['description']) && isset($userSettings[$objectType][$objectName][$fieldConfigurations['description']])) + { + $fieldConfigurations['description'] = $userSettings[$objectType][$objectName][$fieldConfigurations['description']]; + } + + # for each field generate a new field object with the field name and the field configurations + $field = new Field($fieldName, $fieldConfigurations); + + # handle the value for the field + $userValue = false; + + # first, add the default value from the original plugin or theme settings + if(isset($objectSettings['settings'][$fieldName])) + { + $userValue = $objectSettings['settings'][$fieldName]; + } + + # now overwrite the default values with the user values stored in the user settings + if(isset($userSettings[$objectType][$objectName][$fieldName])) + { + $userValue = $userSettings[$objectType][$objectName][$fieldName]; + } + + # now overwrite user-values, if there are old-input values from the actual form (e.g. after input error) + if(isset($_SESSION['old'][$objectName][$fieldName])) + { + $userValue = $_SESSION['old'][$objectName][$fieldName]; + } + + # Now prepopulate the field object with the value */ + if($field->getType() == "textarea" || $field->getType() == "paragraph") + { + if($userValue) + { + $field->setContent($userValue); + } + } + elseif($field->getType() == "checkbox") + { + # checkboxes need a special treatment, because field does not exist in settings if unchecked by user + if(isset($userSettings[$objectType][$objectName][$fieldName])) + { + $field->setAttribute('checked', 'true'); + } + else + { + $field->unsetAttribute('chhecked'); + } + } + else + { + $field->setAttributeValue('value', $userValue); + } + + # add the field to the field-List + $fields[] = $field; + + } + } + return $fields; + } } \ No newline at end of file diff --git a/system/Models/Folder.php b/system/Models/Folder.php index df9b16b..0d09e35 100644 --- a/system/Models/Folder.php +++ b/system/Models/Folder.php @@ -1,434 +1,434 @@ - $item) - { - if (!in_array($item, array(".",".."))) - { - $nameParts = self::getStringParts($item); - $fileType = array_pop($nameParts); - - if($fileType == 'md' OR $fileType == 'txt' ) - { - $folderContent[] = $item; - } - } - } - return $folderContent; - } - - - /* - * scans content of a folder recursively - * vars: folder path as string - * returns: multi-dimensional array with names of folders and files - */ - public static function scanFolder($folderPath, $draft = false) - { - $folderItems = scandir($folderPath); - $folderContent = array(); - - # if it is the live version and if it is a folder that is not published, then do not show the folder and its content. - if(!$draft && !in_array('index.md', $folderItems)){ return false; } - - foreach ($folderItems as $key => $item) - { - if (!in_array($item, array(".",".."))) - { - if (is_dir($folderPath . DIRECTORY_SEPARATOR . $item)) - { - $subFolder = $item; - $folderContent[$subFolder] = self::scanFolder($folderPath . DIRECTORY_SEPARATOR . $subFolder, $draft); - } - else - { - $nameParts = self::getStringParts($item); - $fileType = array_pop($nameParts); - - if($fileType == 'md') - { - $folderContent[] = $item; - } - - if($draft === true && $fileType == 'txt') - { - if(isset($last) && ($last == implode($nameParts)) ) - { - array_pop($folderContent); - $item = $item . 'md'; - } - $folderContent[] = $item; - } - - /* store the name of the last file */ - $last = implode($nameParts); - } - } - } - return $folderContent; - } - - /* - * Transforms array of folder item into an array of item-objects with additional information for each item - * vars: multidimensional array with folder- and file-names - * returns: array of objects. Each object contains information about an item (file or folder). - */ - public static function getFolderContentDetails(array $folderContent, $baseUrl, $fullSlugWithFolder = NULL, $fullSlugWithoutFolder = NULL, $fullPath = NULL, $keyPath = NULL, $chapter = NULL) - { - $contentDetails = []; - $iteration = 0; - $chapternr = 1; - - foreach($folderContent as $key => $name) - { - $item = new \stdClass(); - - if(is_array($name)) - { - $nameParts = self::getStringParts($key); - - $fileType = ''; - if(in_array('index.md', $name)) - { - $fileType = 'md'; - $status = 'published'; - } - if(in_array('index.txt', $name)) - { - $fileType = 'txt'; - $status = 'unpublished'; - } - if(in_array('index.txtmd', $name)) - { - $fileType = 'txt'; - $status = 'modified'; - } - - $item->originalName = $key; - $item->elementType = 'folder'; - $item->status = $status; - $item->fileType = $fileType; - $item->order = count($nameParts) > 1 ? array_shift($nameParts) : NULL; - $item->name = implode(" ",$nameParts); - $item->name = iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name); - $item->slug = implode("-",$nameParts); - $item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug)); - $item->path = $fullPath . DIRECTORY_SEPARATOR . $key; - $item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug; - $item->urlRel = $fullSlugWithFolder . '/' . $item->slug; - $item->urlAbs = $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug; - $item->key = $iteration; - $item->keyPath = isset($keyPath) ? $keyPath . '.' . $iteration : $iteration; - $item->keyPathArray = explode('.', $item->keyPath); - $item->chapter = $chapter ? $chapter . '.' . $chapternr : $chapternr; - $item->active = false; - $item->activeParent = false; - - $item->folderContent = self::getFolderContentDetails($name, $baseUrl, $item->urlRel, $item->urlRelWoF, $item->path, $item->keyPath, $item->chapter); - } - else - { - # do not use files in base folder (only folders are allowed) - if(!isset($keyPath)) continue; - - # do not use index files - if($name == 'index.md' || $name == 'index.txt' || $name == 'index.txtmd' ) continue; - - $nameParts = self::getStringParts($name); - $fileType = array_pop($nameParts); - - if($fileType == 'md') - { - $status = 'published'; - } - elseif($fileType == 'txt') - { - $status = 'unpublished'; - } - else - { - $fileType = 'txt'; - $status = 'modified'; - } - - $item->originalName = $name; - $item->elementType = 'file'; - $item->status = $status; - $item->fileType = $fileType; - $item->order = count($nameParts) > 1 ? array_shift($nameParts) : NULL; - $item->name = implode(" ",$nameParts); - $item->name = iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name); - $item->slug = implode("-",$nameParts); - $item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug)); - $item->path = $fullPath . DIRECTORY_SEPARATOR . $name; - $item->key = $iteration; - $item->keyPath = $keyPath . '.' . $iteration; - $item->keyPathArray = explode('.',$item->keyPath); - $item->chapter = $chapter . '.' . $chapternr; - $item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug; - $item->urlRel = $fullSlugWithFolder . '/' . $item->slug; - $item->urlAbs = $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug; - $item->active = false; - $item->activeParent = false; - } - $iteration++; - $chapternr++; - $contentDetails[] = $item; - } - return $contentDetails; - } - - public static function getItemForUrl($folderContentDetails, $url, $result = NULL) - { - foreach($folderContentDetails as $key => $item) - { - if($item->urlRel === $url) - { - # set item active, needed for move item in navigation - $item->active = true; - $result = $item; - } - elseif($item->elementType === "folder") - { - $result = self::getItemForUrl($item->folderContent, $url, $result); - } - } - return $result; - } - - public static function getPagingForItem($content, $item) - { - $keyPos = count($item->keyPathArray)-1; - $thisChapArray = $item->keyPathArray; - $nextItemArray = $item->keyPathArray; - $prevItemArray = $item->keyPathArray; - - $item->thisChapter = false; - $item->prevItem = false; - $item->nextItem = false; - - - /************************ - * ADD THIS CHAPTER * - ************************/ - - if($keyPos > 0) - { - array_pop($thisChapArray); - $item->thisChapter = self::getItemWithKeyPath($content, $thisChapArray); - } - - /************************ - * ADD NEXT ITEM * - ************************/ - - if($item->elementType == 'folder') - { - /* get the first element in the folder */ - $item->nextItem = isset($item->folderContent[0]) ? clone($item->folderContent[0]) : false; - } - - if(!$item->nextItem) - { - $nextItemArray[$keyPos]++; - $item->nextItem = self::getItemWithKeyPath($content, $nextItemArray); - } - - while(!$item->nextItem) - { - array_pop($nextItemArray); - if(empty($nextItemArray)) break; - $newKeyPos = count($nextItemArray)-1; - $nextItemArray[$newKeyPos]++; - $item->nextItem = self::getItemWithKeyPath($content, $nextItemArray); - } - - /************************ - * ADD PREVIOUS ITEM * - ************************/ - - if($prevItemArray[$keyPos] > 0) - { - $prevItemArray[$keyPos]--; - $item->prevItem = self::getItemWithKeyPath($content, $prevItemArray); - - if($item->prevItem && $item->prevItem->elementType == 'folder' && !empty($item->prevItem->folderContent)) - { - /* get last item in folder */ - $item->prevItem = self::getLastItemOfFolder($item->prevItem); - } - } - else - { - $item->prevItem = $item->thisChapter; - } - - if($item->prevItem && $item->prevItem->elementType == 'folder'){ unset($item->prevItem->folderContent); } - if($item->nextItem && $item->nextItem->elementType == 'folder'){ unset($item->nextItem->folderContent); } - if($item->thisChapter){unset($item->thisChapter->folderContent); } - - return $item; - } - - /* - * Gets a copy of an item with a key - * @param array $content with the full structure of the content as multidimensional array - * @param array $searchArray with the key as a one-dimensional array like array(0,3,4) - * @return array $item - */ - - public static function getItemWithKeyPath($content, array $searchArray) - { - $item = false; - - foreach($searchArray as $key => $itemKey) - { - $item = isset($content[$itemKey]) ? clone($content[$itemKey]) : false; - - unset($searchArray[$key]); - if(!empty($searchArray) && $item) - { - return self::getItemWithKeyPath($item->folderContent, $searchArray); - } - } - return $item; - } - - # https://www.quora.com/Learning-PHP-Is-there-a-way-to-get-the-value-of-multi-dimensional-array-by-specifying-the-key-with-a-variable - # NOT IN USE - public static function getItemWithKeyPathNew($array, array $keys) - { - $item = $array; - - foreach ($keys as $key) - { - $item = isset($item[$key]->folderContent) ? $item[$key]->folderContent : $item[$key]; - } - - return $item; - } - - /* - * Extracts an item with a key https://stackoverflow.com/questions/52097092/php-delete-value-of-array-with-dynamic-key - * @param array $content with the full structure of the content as multidimensional array - * @param array $searchArray with the key as a one-dimensional array like array(0,3,4) - * @return array $item - * NOT IN USE ?? - */ - - public static function extractItemWithKeyPath($structure, array $keys) - { - $result = &$structure; - $last = array_pop($keys); - - foreach ($keys as $key) { - if(isset($result[$key]->folderContent)) - { - $result = &$result[$key]->folderContent; - } - else - { - $result = &$result[$key]; - } - } - - $item = $result[$last]; - unset($result[$last]); - - return array('structure' => $structure, 'item' => $item); - } - - /* get breadcrumb as copied array, set elements active in original and mark parent element in original */ - public static function getBreadcrumb($content, $searchArray, $i = NULL, $breadcrumb = NULL) - { - if(!$i){ $i = 0; $breadcrumb = array();} - - while($i < count($searchArray)) - { - $item = $content[$searchArray[$i]]; - - if($i == count($searchArray)-1) - { - $item->active = true; - } - else - { - $item->activeParent = true; - } - /* - $item->active = true; - if($i == count($searchArray)-2) - { - $item->activeParent = true; - } - */ - - $copy = clone($item); - if($copy->elementType == 'folder') - { - unset($copy->folderContent); - $content = $item->folderContent; - } - $breadcrumb[] = $copy; - - $i++; - return self::getBreadcrumb($content, $searchArray, $i++, $breadcrumb); - } - return $breadcrumb; - } - - public static function getParentItem($content, $searchArray, $iteration = NULL) - { - if(!$iteration){ $iteration = 0; } - while($iteration < count($searchArray)-2) - { - $content = $content[$searchArray[$iteration]]->folderContent; - $iteration++; - return self::getParentItem($content, $searchArray, $iteration); - } - return $content[$searchArray[$iteration]]; - } - - private static function getLastItemOfFolder($folder) - { - $lastItem = end($folder->folderContent); - if(is_object($lastItem) && $lastItem->elementType == 'folder' && !empty($lastItem->folderContent)) - { - return self::getLastItemOfFolder($lastItem); - } - return $lastItem; - } - - public static function getStringParts($name) - { - return preg_split('/[\-\.\_\=\+\?\!\*\#\(\)\/ ]/',$name); - } - - public static function getFileType($fileName) - { - $parts = preg_split('/\./',$fileName); - return end($parts); - } - - public static function splitFileName($fileName) - { - $parts = preg_split('/\./',$fileName); - return $parts; - } + $item) + { + if (!in_array($item, array(".",".."))) + { + $nameParts = self::getStringParts($item); + $fileType = array_pop($nameParts); + + if($fileType == 'md' OR $fileType == 'txt' ) + { + $folderContent[] = $item; + } + } + } + return $folderContent; + } + + + /* + * scans content of a folder recursively + * vars: folder path as string + * returns: multi-dimensional array with names of folders and files + */ + public static function scanFolder($folderPath, $draft = false) + { + $folderItems = scandir($folderPath); + $folderContent = array(); + + # if it is the live version and if it is a folder that is not published, then do not show the folder and its content. + if(!$draft && !in_array('index.md', $folderItems)){ return false; } + + foreach ($folderItems as $key => $item) + { + if (!in_array($item, array(".",".."))) + { + if (is_dir($folderPath . DIRECTORY_SEPARATOR . $item)) + { + $subFolder = $item; + $folderContent[$subFolder] = self::scanFolder($folderPath . DIRECTORY_SEPARATOR . $subFolder, $draft); + } + else + { + $nameParts = self::getStringParts($item); + $fileType = array_pop($nameParts); + + if($fileType == 'md') + { + $folderContent[] = $item; + } + + if($draft === true && $fileType == 'txt') + { + if(isset($last) && ($last == implode($nameParts)) ) + { + array_pop($folderContent); + $item = $item . 'md'; + } + $folderContent[] = $item; + } + + /* store the name of the last file */ + $last = implode($nameParts); + } + } + } + return $folderContent; + } + + /* + * Transforms array of folder item into an array of item-objects with additional information for each item + * vars: multidimensional array with folder- and file-names + * returns: array of objects. Each object contains information about an item (file or folder). + */ + public static function getFolderContentDetails(array $folderContent, $baseUrl, $fullSlugWithFolder = NULL, $fullSlugWithoutFolder = NULL, $fullPath = NULL, $keyPath = NULL, $chapter = NULL) + { + $contentDetails = []; + $iteration = 0; + $chapternr = 1; + + foreach($folderContent as $key => $name) + { + $item = new \stdClass(); + + if(is_array($name)) + { + $nameParts = self::getStringParts($key); + + $fileType = ''; + if(in_array('index.md', $name)) + { + $fileType = 'md'; + $status = 'published'; + } + if(in_array('index.txt', $name)) + { + $fileType = 'txt'; + $status = 'unpublished'; + } + if(in_array('index.txtmd', $name)) + { + $fileType = 'txt'; + $status = 'modified'; + } + + $item->originalName = $key; + $item->elementType = 'folder'; + $item->status = $status; + $item->fileType = $fileType; + $item->order = count($nameParts) > 1 ? array_shift($nameParts) : NULL; + $item->name = implode(" ",$nameParts); + $item->name = iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name); + $item->slug = implode("-",$nameParts); + $item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug)); + $item->path = $fullPath . DIRECTORY_SEPARATOR . $key; + $item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug; + $item->urlRel = $fullSlugWithFolder . '/' . $item->slug; + $item->urlAbs = $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug; + $item->key = $iteration; + $item->keyPath = isset($keyPath) ? $keyPath . '.' . $iteration : $iteration; + $item->keyPathArray = explode('.', $item->keyPath); + $item->chapter = $chapter ? $chapter . '.' . $chapternr : $chapternr; + $item->active = false; + $item->activeParent = false; + + $item->folderContent = self::getFolderContentDetails($name, $baseUrl, $item->urlRel, $item->urlRelWoF, $item->path, $item->keyPath, $item->chapter); + } + else + { + # do not use files in base folder (only folders are allowed) + if(!isset($keyPath)) continue; + + # do not use index files + if($name == 'index.md' || $name == 'index.txt' || $name == 'index.txtmd' ) continue; + + $nameParts = self::getStringParts($name); + $fileType = array_pop($nameParts); + + if($fileType == 'md') + { + $status = 'published'; + } + elseif($fileType == 'txt') + { + $status = 'unpublished'; + } + else + { + $fileType = 'txt'; + $status = 'modified'; + } + + $item->originalName = $name; + $item->elementType = 'file'; + $item->status = $status; + $item->fileType = $fileType; + $item->order = count($nameParts) > 1 ? array_shift($nameParts) : NULL; + $item->name = implode(" ",$nameParts); + $item->name = iconv(mb_detect_encoding($item->name, mb_detect_order(), true), "UTF-8", $item->name); + $item->slug = implode("-",$nameParts); + $item->slug = URLify::filter(iconv(mb_detect_encoding($item->slug, mb_detect_order(), true), "UTF-8", $item->slug)); + $item->path = $fullPath . DIRECTORY_SEPARATOR . $name; + $item->key = $iteration; + $item->keyPath = $keyPath . '.' . $iteration; + $item->keyPathArray = explode('.',$item->keyPath); + $item->chapter = $chapter . '.' . $chapternr; + $item->urlRelWoF = $fullSlugWithoutFolder . '/' . $item->slug; + $item->urlRel = $fullSlugWithFolder . '/' . $item->slug; + $item->urlAbs = $baseUrl . $fullSlugWithoutFolder . '/' . $item->slug; + $item->active = false; + $item->activeParent = false; + } + $iteration++; + $chapternr++; + $contentDetails[] = $item; + } + return $contentDetails; + } + + public static function getItemForUrl($folderContentDetails, $url, $result = NULL) + { + foreach($folderContentDetails as $key => $item) + { + if($item->urlRel === $url) + { + # set item active, needed for move item in navigation + $item->active = true; + $result = $item; + } + elseif($item->elementType === "folder") + { + $result = self::getItemForUrl($item->folderContent, $url, $result); + } + } + return $result; + } + + public static function getPagingForItem($content, $item) + { + $keyPos = count($item->keyPathArray)-1; + $thisChapArray = $item->keyPathArray; + $nextItemArray = $item->keyPathArray; + $prevItemArray = $item->keyPathArray; + + $item->thisChapter = false; + $item->prevItem = false; + $item->nextItem = false; + + + /************************ + * ADD THIS CHAPTER * + ************************/ + + if($keyPos > 0) + { + array_pop($thisChapArray); + $item->thisChapter = self::getItemWithKeyPath($content, $thisChapArray); + } + + /************************ + * ADD NEXT ITEM * + ************************/ + + if($item->elementType == 'folder') + { + /* get the first element in the folder */ + $item->nextItem = isset($item->folderContent[0]) ? clone($item->folderContent[0]) : false; + } + + if(!$item->nextItem) + { + $nextItemArray[$keyPos]++; + $item->nextItem = self::getItemWithKeyPath($content, $nextItemArray); + } + + while(!$item->nextItem) + { + array_pop($nextItemArray); + if(empty($nextItemArray)) break; + $newKeyPos = count($nextItemArray)-1; + $nextItemArray[$newKeyPos]++; + $item->nextItem = self::getItemWithKeyPath($content, $nextItemArray); + } + + /************************ + * ADD PREVIOUS ITEM * + ************************/ + + if($prevItemArray[$keyPos] > 0) + { + $prevItemArray[$keyPos]--; + $item->prevItem = self::getItemWithKeyPath($content, $prevItemArray); + + if($item->prevItem && $item->prevItem->elementType == 'folder' && !empty($item->prevItem->folderContent)) + { + /* get last item in folder */ + $item->prevItem = self::getLastItemOfFolder($item->prevItem); + } + } + else + { + $item->prevItem = $item->thisChapter; + } + + if($item->prevItem && $item->prevItem->elementType == 'folder'){ unset($item->prevItem->folderContent); } + if($item->nextItem && $item->nextItem->elementType == 'folder'){ unset($item->nextItem->folderContent); } + if($item->thisChapter){unset($item->thisChapter->folderContent); } + + return $item; + } + + /* + * Gets a copy of an item with a key + * @param array $content with the full structure of the content as multidimensional array + * @param array $searchArray with the key as a one-dimensional array like array(0,3,4) + * @return array $item + */ + + public static function getItemWithKeyPath($content, array $searchArray) + { + $item = false; + + foreach($searchArray as $key => $itemKey) + { + $item = isset($content[$itemKey]) ? clone($content[$itemKey]) : false; + + unset($searchArray[$key]); + if(!empty($searchArray) && $item) + { + return self::getItemWithKeyPath($item->folderContent, $searchArray); + } + } + return $item; + } + + # https://www.quora.com/Learning-PHP-Is-there-a-way-to-get-the-value-of-multi-dimensional-array-by-specifying-the-key-with-a-variable + # NOT IN USE + public static function getItemWithKeyPathNew($array, array $keys) + { + $item = $array; + + foreach ($keys as $key) + { + $item = isset($item[$key]->folderContent) ? $item[$key]->folderContent : $item[$key]; + } + + return $item; + } + + /* + * Extracts an item with a key https://stackoverflow.com/questions/52097092/php-delete-value-of-array-with-dynamic-key + * @param array $content with the full structure of the content as multidimensional array + * @param array $searchArray with the key as a one-dimensional array like array(0,3,4) + * @return array $item + * NOT IN USE ?? + */ + + public static function extractItemWithKeyPath($structure, array $keys) + { + $result = &$structure; + $last = array_pop($keys); + + foreach ($keys as $key) { + if(isset($result[$key]->folderContent)) + { + $result = &$result[$key]->folderContent; + } + else + { + $result = &$result[$key]; + } + } + + $item = $result[$last]; + unset($result[$last]); + + return array('structure' => $structure, 'item' => $item); + } + + /* get breadcrumb as copied array, set elements active in original and mark parent element in original */ + public static function getBreadcrumb($content, $searchArray, $i = NULL, $breadcrumb = NULL) + { + if(!$i){ $i = 0; $breadcrumb = array();} + + while($i < count($searchArray)) + { + $item = $content[$searchArray[$i]]; + + if($i == count($searchArray)-1) + { + $item->active = true; + } + else + { + $item->activeParent = true; + } + /* + $item->active = true; + if($i == count($searchArray)-2) + { + $item->activeParent = true; + } + */ + + $copy = clone($item); + if($copy->elementType == 'folder') + { + unset($copy->folderContent); + $content = $item->folderContent; + } + $breadcrumb[] = $copy; + + $i++; + return self::getBreadcrumb($content, $searchArray, $i++, $breadcrumb); + } + return $breadcrumb; + } + + public static function getParentItem($content, $searchArray, $iteration = NULL) + { + if(!$iteration){ $iteration = 0; } + while($iteration < count($searchArray)-2) + { + $content = $content[$searchArray[$iteration]]->folderContent; + $iteration++; + return self::getParentItem($content, $searchArray, $iteration); + } + return $content[$searchArray[$iteration]]; + } + + private static function getLastItemOfFolder($folder) + { + $lastItem = end($folder->folderContent); + if(is_object($lastItem) && $lastItem->elementType == 'folder' && !empty($lastItem->folderContent)) + { + return self::getLastItemOfFolder($lastItem); + } + return $lastItem; + } + + public static function getStringParts($name) + { + return preg_split('/[\-\.\_\=\+\?\!\*\#\(\)\/ ]/',$name); + } + + public static function getFileType($fileName) + { + $parts = preg_split('/\./',$fileName); + return end($parts); + } + + public static function splitFileName($fileName) + { + $parts = preg_split('/\./',$fileName); + return $parts; + } } \ No newline at end of file diff --git a/system/Models/Helpers.php b/system/Models/Helpers.php index 4175f57..ce81bf5 100644 --- a/system/Models/Helpers.php +++ b/system/Models/Helpers.php @@ -1,28 +1,28 @@ -'; - $table .= ''; - foreach($timer as $breakpoint => $time) - { - $duration = $time - $lastTime; - - $table .= ''; - $table .= ''; - $table .= ''; - $table .= ''; - $table .= ''; - - $lastTime = $time; - } - $table .= '
BreakpointTimeDuration
' . $breakpoint . '' . $time . '' . $duration . '
'; - echo $table; - exit; - } +'; + $table .= ''; + foreach($timer as $breakpoint => $time) + { + $duration = $time - $lastTime; + + $table .= ''; + $table .= ''; + $table .= ''; + $table .= ''; + $table .= ''; + + $lastTime = $time; + } + $table .= '
BreakpointTimeDuration
' . $breakpoint . '' . $time . '' . $duration . '
'; + echo $table; + exit; + } } \ No newline at end of file diff --git a/system/Models/ProcessImage.php b/system/Models/ProcessImage.php index 27901f7..2922633 100644 --- a/system/Models/ProcessImage.php +++ b/system/Models/ProcessImage.php @@ -1,336 +1,336 @@ -clearTempFolder(); - - # decode the image from base64-string - $imageDecoded = $this->decodeImage($image); - $imageData = $imageDecoded["image"]; - $imageType = $imageDecoded["type"]; - - # transform image-stream into image - $image = imagecreatefromstring($imageData); - - # get the size of the original image - $imageSize = $this->getImageSize($image); - - # check the desired sizes and calculate the height, if not set - $desiredSizes = $this->setHeight($imageSize, $desiredSizes); - - # resize the images - $resizedImages = $this->imageResize($image, $imageSize, $desiredSizes, $imageType); - - $basePath = getcwd() . DIRECTORY_SEPARATOR . 'media'; - $tmpFolder = $basePath . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR; - - $this->saveOriginal($tmpFolder, $imageData, 'original', $imageType); - - if($imageType == "gif" && $this->detectAnimatedGif($imageData)) - { - $this->saveOriginal($tmpFolder, $imageData, 'live', $imageType); - - return true; - } - - # temporary store resized images - foreach($resizedImages as $key => $resizedImage) - { - $this->saveImage($tmpFolder, $resizedImage, $key, $imageType); - } - - return true; - } - - public function detectAnimatedGif($image_file_contents) - { - $is_animated = preg_match('#(\x00\x21\xF9\x04.{4}\x00\x2C.*){2,}#s', $image_file_contents); - if ($is_animated == 1) - { - return true; - } - return false; - } - - public function publishImage(array $desiredSizes, $name = false) - { - /* get images from tmp folder */ - $basePath = getcwd() . DIRECTORY_SEPARATOR . 'media'; - $tmpFolder = $basePath . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR; - $originalFolder = $basePath . DIRECTORY_SEPARATOR . 'original' . DIRECTORY_SEPARATOR; - $liveFolder = $basePath . DIRECTORY_SEPARATOR . 'live' . DIRECTORY_SEPARATOR; - - if(!file_exists($originalFolder)){ mkdir($originalFolder, 0774, true); } - if(!file_exists($liveFolder)){ mkdir($liveFolder, 0774, true); } - - $name = $name ? $name : uniqid(); - - $files = scandir($tmpFolder); - $success = true; - - foreach($files as $file) - { - if (!in_array($file, array(".",".."))) - { - $tmpfilename = explode(".", $file); - - if($tmpfilename[0] == 'original') - { - $success = rename($tmpFolder . $file, $originalFolder . $name . '-' . $file); - } - else - { - $success = rename($tmpFolder . $file, $liveFolder . $name . '-' . $file); - } - } - } - - if($success) - { - return 'media/live/' . $name . '-live.' . $tmpfilename[1]; - } - - return false; - } - - public function decodeImage(string $image) - { - $imageParts = explode(";base64,", $image); - $imageType = explode("/", $imageParts[0]); - $imageData = base64_decode($imageParts[1]); - - if ($imageData !== false) - { - return array("image" => $imageData, "type" => $imageType[1]); - } - - return false; - } - - public function getImageSize($image) - { - $width = imagesx($image); - $height = imagesy($image); - return array('width' => $width, 'height' => $height); - } - - public function setHeight(array $imageSize, array $desiredSizes) - { - foreach($desiredSizes as $key => $desiredSize) - { - if($desiredSize['width'] > $imageSize['width']) - { - $desiredSizes[$key] = $imageSize; - continue; - } - - if(!isset($desiredSize['height'])) - { - $resizeFactor = $imageSize['width'] / $desiredSize['width']; - $desiredSizes[$key]['height'] = round( ($imageSize['height'] / $resizeFactor), 0); - } - } - return $desiredSizes; - } - - public function imageResize($imageData, array $imageSize, array $desiredSizes, $imageType) - { - $copiedImages = array(); - $source_aspect_ratio = $imageSize['width'] / $imageSize['height']; - - foreach($desiredSizes as $key => $desiredSize) - { - $desired_aspect_ratio = $desiredSize['width'] / $desiredSize['height']; - - if ( $source_aspect_ratio > $desired_aspect_ratio ) - { - # when source image is wider - $temp_height = $desiredSize['height']; - $temp_width = ( int ) ($desiredSize['height'] * $source_aspect_ratio); - $temp_width = round($temp_width, 0); - } - else - { - # when source image is similar or taller - $temp_width = $desiredSize['width']; - $temp_height = ( int ) ($desiredSize['width'] / $source_aspect_ratio); - $temp_height = round($temp_height, 0); - } - - # Create a temporary GD image with desired size - $temp_gdim = imagecreatetruecolor( $temp_width, $temp_height ); - - if ($imageType == "gif") - { - $transparent_index = imagecolortransparent($imageData); - imagepalettecopy($imageData, $temp_gdim); - imagefill($temp_gdim, 0, 0, $transparent_index); - imagecolortransparent($temp_gdim, $transparent_index); - imagetruecolortopalette($temp_gdim, true, 256); - } - elseif($imageType == "png") - { - imagealphablending($temp_gdim, false); - imagesavealpha($temp_gdim, true); - $transparent = imagecolorallocatealpha($temp_gdim, 255, 255, 255, 127); - imagefilledrectangle($temp_gdim, 0, 0, $temp_width, $temp_height, $transparent); - } - - # resize image - imagecopyresampled( - $temp_gdim, - $imageData, - 0, 0, - 0, 0, - $temp_width, $temp_height, - $imageSize['width'], $imageSize['height'] - ); - - $copiedImages[$key] = $temp_gdim; - - /* - - # Copy cropped region from temporary image into the desired GD image - $x0 = ( $temp_width - $desiredSize['width'] ) / 2; - $y0 = ( $temp_height - $desiredSize['height'] ) / 2; - - $desired_gdim = imagecreatetruecolor( $desiredSize['width'], $desiredSize['height'] ); - - if ($imageType == "gif") - { - imagepalettecopy($temp_gdim, $desired_gdim); - imagefill($desired_gdim, 0, 0, $transparent_index); - imagecolortransparent($desired_gdim, $transparent_index); - imagetruecolortopalette($desired_gdim, true, 256); - } - elseif($imageType == "png") - { - imagealphablending($desired_gdim, false); - imagesavealpha($desired_gdim,true); - $transparent = imagecolorallocatealpha($desired_gdim, 255, 255, 255, 127); - imagefilledrectangle($desired_gdim, 0, 0, $desired_size['with'], $desired_size['height'], $transparent); - } - - imagecopyresampled( - $desired_gdim, - $temp_gdim, - 0, 0, - 0, 0, - $x0, $y0, - $desiredSize['width'], $desiredSize['height'] - ); - $copiedImages[$key] = $desired_gdim; - - */ - } - return $copiedImages; - } - - public function saveOriginal($folder, $image, $name, $type) - { - if(!file_exists($folder)) - { - mkdir($folder, 0774, true); - } - - $path = $folder . $name . '.' . $type; - - file_put_contents($path, $image); - } - - public function saveImage($folder, $image, $name, $type) - { - if(!file_exists($folder)) - { - mkdir($folder, 0774, true); - } - - if($type == "png") - { - $result = imagepng( $image, $folder . '/' . $name . '.png' ); - } - elseif($type == "gif") - { - $result = imagegif( $image, $folder . '/' . $name . '.gif' ); - } - else - { - $result = imagejpeg( $image, $folder . '/' . $name . '.jpeg' ); - $type = 'jpeg'; - } - - imagedestroy($image); - - if($result) - { - return $name . '.' . $type; - } - - return false; - } - - public function clearTempFolder() - { - $folder = getcwd() . DIRECTORY_SEPARATOR . 'media' . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR; - - if(!file_exists($folder)) - { - mkdir($folder, 0774, true); - return true; - } - - $files = scandir($folder); - $result = true; - - foreach($files as $file) - { - if (!in_array($file, array(".",".."))) - { - $filelink = $folder . $file; - if(!unlink($filelink)) - { - $success = false; - } - } - } - - return $result; - } - - public function deleteImage($name) - { - $baseFolder = getcwd() . DIRECTORY_SEPARATOR . 'media' . DIRECTORY_SEPARATOR; - $original = $baseFolder . 'original' . DIRECTORY_SEPARATOR . $name . '*'; - $live = $baseFolder . 'live' . DIRECTORY_SEPARATOR . $name . '*'; - $success = true; - - foreach(glob($original) as $image) - { - if(!unlink($image)) - { - $success = false; - } - } - - foreach(glob($live) as $image) - { - if(!unlink($image)) - { - $success = false; - } - } - - return $success; - } -} - - +clearTempFolder(); + + # decode the image from base64-string + $imageDecoded = $this->decodeImage($image); + $imageData = $imageDecoded["image"]; + $imageType = $imageDecoded["type"]; + + # transform image-stream into image + $image = imagecreatefromstring($imageData); + + # get the size of the original image + $imageSize = $this->getImageSize($image); + + # check the desired sizes and calculate the height, if not set + $desiredSizes = $this->setHeight($imageSize, $desiredSizes); + + # resize the images + $resizedImages = $this->imageResize($image, $imageSize, $desiredSizes, $imageType); + + $basePath = getcwd() . DIRECTORY_SEPARATOR . 'media'; + $tmpFolder = $basePath . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR; + + $this->saveOriginal($tmpFolder, $imageData, 'original', $imageType); + + if($imageType == "gif" && $this->detectAnimatedGif($imageData)) + { + $this->saveOriginal($tmpFolder, $imageData, 'live', $imageType); + + return true; + } + + # temporary store resized images + foreach($resizedImages as $key => $resizedImage) + { + $this->saveImage($tmpFolder, $resizedImage, $key, $imageType); + } + + return true; + } + + public function detectAnimatedGif($image_file_contents) + { + $is_animated = preg_match('#(\x00\x21\xF9\x04.{4}\x00\x2C.*){2,}#s', $image_file_contents); + if ($is_animated == 1) + { + return true; + } + return false; + } + + public function publishImage(array $desiredSizes, $name = false) + { + /* get images from tmp folder */ + $basePath = getcwd() . DIRECTORY_SEPARATOR . 'media'; + $tmpFolder = $basePath . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR; + $originalFolder = $basePath . DIRECTORY_SEPARATOR . 'original' . DIRECTORY_SEPARATOR; + $liveFolder = $basePath . DIRECTORY_SEPARATOR . 'live' . DIRECTORY_SEPARATOR; + + if(!file_exists($originalFolder)){ mkdir($originalFolder, 0774, true); } + if(!file_exists($liveFolder)){ mkdir($liveFolder, 0774, true); } + + $name = $name ? $name : uniqid(); + + $files = scandir($tmpFolder); + $success = true; + + foreach($files as $file) + { + if (!in_array($file, array(".",".."))) + { + $tmpfilename = explode(".", $file); + + if($tmpfilename[0] == 'original') + { + $success = rename($tmpFolder . $file, $originalFolder . $name . '-' . $file); + } + else + { + $success = rename($tmpFolder . $file, $liveFolder . $name . '-' . $file); + } + } + } + + if($success) + { + return 'media/live/' . $name . '-live.' . $tmpfilename[1]; + } + + return false; + } + + public function decodeImage(string $image) + { + $imageParts = explode(";base64,", $image); + $imageType = explode("/", $imageParts[0]); + $imageData = base64_decode($imageParts[1]); + + if ($imageData !== false) + { + return array("image" => $imageData, "type" => $imageType[1]); + } + + return false; + } + + public function getImageSize($image) + { + $width = imagesx($image); + $height = imagesy($image); + return array('width' => $width, 'height' => $height); + } + + public function setHeight(array $imageSize, array $desiredSizes) + { + foreach($desiredSizes as $key => $desiredSize) + { + if($desiredSize['width'] > $imageSize['width']) + { + $desiredSizes[$key] = $imageSize; + continue; + } + + if(!isset($desiredSize['height'])) + { + $resizeFactor = $imageSize['width'] / $desiredSize['width']; + $desiredSizes[$key]['height'] = round( ($imageSize['height'] / $resizeFactor), 0); + } + } + return $desiredSizes; + } + + public function imageResize($imageData, array $imageSize, array $desiredSizes, $imageType) + { + $copiedImages = array(); + $source_aspect_ratio = $imageSize['width'] / $imageSize['height']; + + foreach($desiredSizes as $key => $desiredSize) + { + $desired_aspect_ratio = $desiredSize['width'] / $desiredSize['height']; + + if ( $source_aspect_ratio > $desired_aspect_ratio ) + { + # when source image is wider + $temp_height = $desiredSize['height']; + $temp_width = ( int ) ($desiredSize['height'] * $source_aspect_ratio); + $temp_width = round($temp_width, 0); + } + else + { + # when source image is similar or taller + $temp_width = $desiredSize['width']; + $temp_height = ( int ) ($desiredSize['width'] / $source_aspect_ratio); + $temp_height = round($temp_height, 0); + } + + # Create a temporary GD image with desired size + $temp_gdim = imagecreatetruecolor( $temp_width, $temp_height ); + + if ($imageType == "gif") + { + $transparent_index = imagecolortransparent($imageData); + imagepalettecopy($imageData, $temp_gdim); + imagefill($temp_gdim, 0, 0, $transparent_index); + imagecolortransparent($temp_gdim, $transparent_index); + imagetruecolortopalette($temp_gdim, true, 256); + } + elseif($imageType == "png") + { + imagealphablending($temp_gdim, false); + imagesavealpha($temp_gdim, true); + $transparent = imagecolorallocatealpha($temp_gdim, 255, 255, 255, 127); + imagefilledrectangle($temp_gdim, 0, 0, $temp_width, $temp_height, $transparent); + } + + # resize image + imagecopyresampled( + $temp_gdim, + $imageData, + 0, 0, + 0, 0, + $temp_width, $temp_height, + $imageSize['width'], $imageSize['height'] + ); + + $copiedImages[$key] = $temp_gdim; + + /* + + # Copy cropped region from temporary image into the desired GD image + $x0 = ( $temp_width - $desiredSize['width'] ) / 2; + $y0 = ( $temp_height - $desiredSize['height'] ) / 2; + + $desired_gdim = imagecreatetruecolor( $desiredSize['width'], $desiredSize['height'] ); + + if ($imageType == "gif") + { + imagepalettecopy($temp_gdim, $desired_gdim); + imagefill($desired_gdim, 0, 0, $transparent_index); + imagecolortransparent($desired_gdim, $transparent_index); + imagetruecolortopalette($desired_gdim, true, 256); + } + elseif($imageType == "png") + { + imagealphablending($desired_gdim, false); + imagesavealpha($desired_gdim,true); + $transparent = imagecolorallocatealpha($desired_gdim, 255, 255, 255, 127); + imagefilledrectangle($desired_gdim, 0, 0, $desired_size['with'], $desired_size['height'], $transparent); + } + + imagecopyresampled( + $desired_gdim, + $temp_gdim, + 0, 0, + 0, 0, + $x0, $y0, + $desiredSize['width'], $desiredSize['height'] + ); + $copiedImages[$key] = $desired_gdim; + + */ + } + return $copiedImages; + } + + public function saveOriginal($folder, $image, $name, $type) + { + if(!file_exists($folder)) + { + mkdir($folder, 0774, true); + } + + $path = $folder . $name . '.' . $type; + + file_put_contents($path, $image); + } + + public function saveImage($folder, $image, $name, $type) + { + if(!file_exists($folder)) + { + mkdir($folder, 0774, true); + } + + if($type == "png") + { + $result = imagepng( $image, $folder . '/' . $name . '.png' ); + } + elseif($type == "gif") + { + $result = imagegif( $image, $folder . '/' . $name . '.gif' ); + } + else + { + $result = imagejpeg( $image, $folder . '/' . $name . '.jpeg' ); + $type = 'jpeg'; + } + + imagedestroy($image); + + if($result) + { + return $name . '.' . $type; + } + + return false; + } + + public function clearTempFolder() + { + $folder = getcwd() . DIRECTORY_SEPARATOR . 'media' . DIRECTORY_SEPARATOR . 'tmp' . DIRECTORY_SEPARATOR; + + if(!file_exists($folder)) + { + mkdir($folder, 0774, true); + return true; + } + + $files = scandir($folder); + $result = true; + + foreach($files as $file) + { + if (!in_array($file, array(".",".."))) + { + $filelink = $folder . $file; + if(!unlink($filelink)) + { + $success = false; + } + } + } + + return $result; + } + + public function deleteImage($name) + { + $baseFolder = getcwd() . DIRECTORY_SEPARATOR . 'media' . DIRECTORY_SEPARATOR; + $original = $baseFolder . 'original' . DIRECTORY_SEPARATOR . $name . '*'; + $live = $baseFolder . 'live' . DIRECTORY_SEPARATOR . $name . '*'; + $success = true; + + foreach(glob($original) as $image) + { + if(!unlink($image)) + { + $success = false; + } + } + + foreach(glob($live) as $image) + { + if(!unlink($image)) + { + $success = false; + } + } + + return $success; + } +} + + ?> \ No newline at end of file diff --git a/system/Models/User.php b/system/Models/User.php index a3f4344..ded5290 100644 --- a/system/Models/User.php +++ b/system/Models/User.php @@ -1,98 +1,98 @@ - $user) - { - if($user == '.logins'){ continue; } - $cleanUser[] = str_replace('.yaml', '', $user); - } - - return $cleanUser; - } - - public function getUser($username) - { - $user = $this->getYaml('settings/users', $username . '.yaml'); - return $user; - } - - public function createUser($params) - { - $userdata = array( - 'username' => $params['username'], - 'email' => $params['email'], - 'password' => $this->generatePassword($params['password']), - 'userrole' => $params['userrole'] - ); - - if($this->updateYaml('settings/users', $userdata['username'] . '.yaml', $userdata)) - { - return $userdata['username']; - } - return false; - } - - public function updateUser($params) - { - $userdata = $this->getUser($params['username']); - - if(isset($params['password'])) - { - $params['password'] = $this->generatePassword($params['password']); - } - - $update = array_merge($userdata, $params); - - $this->updateYaml('settings/users', $userdata['username'] . '.yaml', $update); - - return $userdata['username']; - } - - public function deleteUser($username) - { - if($this->getUser($username)) - { - unlink('settings/users/' . $username . '.yaml'); - } - } - - public function getUserroles() - { - return array('administrator', 'editor'); - } - - public function login($username) - { - $user = $this->getUser($username); - - if($user) - { - $user['lastlogin'] = time(); - unset($user['password']); - $this->updateUser($user); - - $_SESSION['user'] = $user['username']; - $_SESSION['role'] = $user['userrole']; - $_SESSION['login'] = $user['lastlogin']; - } - } - - public function generatePassword($password) - { - return \password_hash($password, PASSWORD_DEFAULT, ['cost' => 10]); - } + $user) + { + if($user == '.logins'){ continue; } + $cleanUser[] = str_replace('.yaml', '', $user); + } + + return $cleanUser; + } + + public function getUser($username) + { + $user = $this->getYaml('settings/users', $username . '.yaml'); + return $user; + } + + public function createUser($params) + { + $userdata = array( + 'username' => $params['username'], + 'email' => $params['email'], + 'password' => $this->generatePassword($params['password']), + 'userrole' => $params['userrole'] + ); + + if($this->updateYaml('settings/users', $userdata['username'] . '.yaml', $userdata)) + { + return $userdata['username']; + } + return false; + } + + public function updateUser($params) + { + $userdata = $this->getUser($params['username']); + + if(isset($params['password'])) + { + $params['password'] = $this->generatePassword($params['password']); + } + + $update = array_merge($userdata, $params); + + $this->updateYaml('settings/users', $userdata['username'] . '.yaml', $update); + + return $userdata['username']; + } + + public function deleteUser($username) + { + if($this->getUser($username)) + { + unlink('settings/users/' . $username . '.yaml'); + } + } + + public function getUserroles() + { + return array('administrator', 'editor'); + } + + public function login($username) + { + $user = $this->getUser($username); + + if($user) + { + $user['lastlogin'] = time(); + unset($user['password']); + $this->updateUser($user); + + $_SESSION['user'] = $user['username']; + $_SESSION['role'] = $user['userrole']; + $_SESSION['login'] = $user['lastlogin']; + } + } + + public function generatePassword($password) + { + return \password_hash($password, PASSWORD_DEFAULT, ['cost' => 10]); + } } \ No newline at end of file diff --git a/system/Models/Validation.php b/system/Models/Validation.php index 4a1f4d8..d6a76a2 100644 --- a/system/Models/Validation.php +++ b/system/Models/Validation.php @@ -1,418 +1,418 @@ -getUser($value); - if($userdata){ return false; } - return true; - }, 'taken'); - - Validator::addRule('userExists', function($field, $value, array $params, array $fields) use ($user) - { - $userdata = $user->getUser($value); - if($userdata){ return true; } - return false; - }, 'does not exist'); - - Validator::addRule('checkPassword', function($field, $value, array $params, array $fields) use ($user) - { - $userdata = $user->getUser($fields['username']); - if($userdata && password_verify($value, $userdata['password'])){ return true; } - return false; - }, 'wrong password'); - - Validator::addRule('emailAvailable', function($field, $value, array $params, array $fields) - { - $email = 'testmail@gmail.com'; - if($email){ return false; } - return true; - }, 'taken'); - - Validator::addRule('emailKnown', function($field, $value, array $params, array $fields) - { - $email = 'testmail@gmail.com'; - if(!$email){ return false; } - return true; - }, 'unknown'); - - Validator::addRule('noSpecialChars', function($field, $value, array $params, array $fields) - { - $format = '/[!@#$%^&*()_+=\[\]{};\':"\\|,.<>\/?]/'; - if ( preg_match($format, $value)) - { - return false; - } - return true; - }, 'contains special characters'); - - Validator::addRule('noHTML', function($field, $value, array $params, array $fields) - { - if ( $value == strip_tags($value) ) - { - return true; - } - return false; - }, 'contains html'); - - Validator::addRule('markdownSecure', function($field, $value, array $params, array $fields) - { - /* strip out code blocks and blockquotes */ - $value = preg_replace('/`{4,}[\s\S]+?`{4,}/', '', $value); - $value = preg_replace('/`{3,}[\s\S]+?`{3,}/', '', $value); - $value = preg_replace('/`{2,}[\s\S]+?`{2,}/', '', $value); - $value = preg_replace('/`{1,}[\s\S]+?`{1,}/', '', $value); - $value = preg_replace('/>[\s\S]+?[\n\r]/', '', $value); - - if ( $value == strip_tags($value) ) - { - return true; - } - return false; - }, 'not secure. For code please use markdown `inline-code` or ````fenced code blocks````.'); - } - - /** - * validation for signup form - * - * @param array $params with form data. - * @return obj $v the validation object passed to a result method. - */ - - public function signin(array $params) - { - $v = new Validator($params); - $v->rule('required', ['username', 'password'])->message("Required"); - $v->rule('alphaNum', 'username')->message("Invalid characters"); - $v->rule('lengthBetween', 'password', 5, 20)->message("Length between 5 - 20"); - $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20"); - - if($v->validate()) - { - return true; - } - else - { - return false; - } - } - - /** - * validation for signup form - * - * @param array $params with form data. - * @return obj $v the validation object passed to a result method. - */ - - public function newUser(array $params, $userroles) - { - $v = new Validator($params); - $v->rule('required', ['username', 'email', 'password'])->message("required"); - $v->rule('alphaNum', 'username')->message("invalid characters"); - $v->rule('lengthBetween', 'password', 5, 20)->message("Length between 5 - 20"); - $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20"); - $v->rule('userAvailable', 'username')->message("User already exists"); - $v->rule('email', 'email')->message("e-mail is invalid"); - $v->rule('in', 'userrole', $userroles); - - return $this->validationResult($v); - } - - public function existingUser(array $params, $userroles) - { - $v = new Validator($params); - $v->rule('required', ['username', 'email', 'userrole'])->message("required"); - $v->rule('alphaNum', 'username')->message("invalid"); - $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20"); - $v->rule('userExists', 'username')->message("user does not exist"); - $v->rule('email', 'email')->message("e-mail is invalid"); - $v->rule('in', 'userrole', $userroles); - - return $this->validationResult($v); - } - - public function username($username) - { - $v = new Validator($username); - $v->rule('alphaNum', 'username')->message("Only alpha-numeric characters allowed"); - $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20"); - - return $this->validationResult($v); - } - - /** - * validation for changing the password - * - * @param array $params with form data. - * @return obj $v the validation object passed to a result method. - */ - - public function newPassword(array $params) - { - $v = new Validator($params); - $v->rule('required', ['password', 'newpassword']); - $v->rule('lengthBetween', 'newpassword', 5, 20); - $v->rule('checkPassword', 'password')->message("Password is wrong"); - - return $this->validationResult($v); - } - - /** - * validation for system settings - * - * @param array $params with form data. - * @return obj $v the validation object passed to a result method. - */ - - public function settings(array $params, array $copyright, $name = false) - { - $v = new Validator($params); - - $v->rule('required', ['title', 'author', 'copyright', 'year', 'editor']); - $v->rule('lengthBetween', 'title', 2, 20); - $v->rule('lengthBetween', 'author', 2, 40); - $v->rule('regex', 'title', '/^[\pL0-9_ \-]*$/u'); - $v->rule('regex', 'author', '/^[\pL_ \-]*$/u'); - $v->rule('integer', 'year'); - $v->rule('length', 'year', 4); - $v->rule('in', 'editor', ['raw', 'visual']); - $v->rule('in', 'copyright', $copyright); - - return $this->validationResult($v, $name); - } - - /** - * validation for content editor - * - * @param array $params with form data. - * @return true or $v->errors with array of errors to use in json-response - */ - - public function editorInput(array $params) - { - $v = new Validator($params); - - $v->rule('required', ['title', 'content', 'url']); - $v->rule('lengthBetween', 'title', 2, 100); - $v->rule('noHTML', 'title'); - $v->rule('markdownSecure', 'content'); - - if($v->validate()) - { - return true; - } - else - { - return $v->errors(); - } - } - - public function blockInput(array $params) - { - $v = new Validator($params); - - $v->rule('required', ['markdown', 'block_id', 'url']); - $v->rule('markdownSecure', 'markdown'); - $v->rule('regex', 'block_id', '/^[0-9.]+$/i'); - - if($v->validate()) - { - return true; - } - else - { - return $v->errors(); - } - } - - /** - * validation for resort navigation - * - * @param array $params with form data. - * @return true or $v->errors with array of errors to use in json-response - */ - - public function navigationSort(array $params) - { - $v = new Validator($params); - - $v->rule('required', ['item_id', 'parent_id_from', 'parent_id_to']); - $v->rule('regex', 'item_id', '/^[0-9.]+$/i'); - $v->rule('regex', 'parent_id_from', '/^[a-zA-Z0-9.]+$/i'); - $v->rule('regex', 'parent_id_to', '/^[a-zA-Z0-9.]+$/i'); - $v->rule('integer', 'index_new'); - - if($v->validate()) - { - return true; - } - else - { - return $v->errors(); - } - } - - /** - * validation for new navigation items - * - * @param array $params with form data. - * @return true or $v->errors with array of errors to use in json-response - */ - - public function navigationItem(array $params) - { - $v = new Validator($params); - - $v->rule('required', ['folder_id', 'item_name', 'type', 'url']); - $v->rule('regex', 'folder_id', '/^[0-9.]+$/i'); - $v->rule('noSpecialChars', 'item_name'); - $v->rule('lengthBetween', 'item_name', 1, 40); - $v->rule('in', 'type', ['file', 'folder']); - - if($v->validate()) - { - return true; - } - else - { - return $v->errors(); - } - } - - /** - * validation for dynamic fields ( settings for themes and plugins) - * - * @param string $fieldName with the name of the field. - * @param array or string $fieldValue with the values of the field. - * @param string $objectName with the name of the plugin or theme. - * @param array $fieldDefinitions with the field definitions as multi-dimensional array. - * @return obj $v the validation object passed to a result method. - */ - - public function objectField($fieldName, $fieldValue, $objectName, $fieldDefinitions) - { - $v = new Validator(array($fieldName => $fieldValue)); - - if(isset($fieldDefinitions['required'])) - { - $v->rule('required', $fieldName); - } - - switch($fieldDefinitions['type']) - { - case "select": - /* create array with option keys as value */ - $options = array(); - foreach($fieldDefinitions['options'] as $key => $value){ $options[] = $key; } - $v->rule('in', $fieldName, $options); - break; - case "radio": - $v->rule('in', $fieldName, $fieldDefinitions['options']); - break; - case "checkboxlist": - /* create array with option keys as value */ - $options = array(); - foreach($fieldDefinitions['options'] as $key => $value){ $options[] = $key; } - /* loop over input values and check, if the options of the field definitions (options for checkboxlist) contains the key (input from user, key is used as value, value is used as label) */ - foreach($fieldValue as $key => $value) - { - $v->rule('in', $key, $options); - } - break; - case "color": - $v->rule('regex', $fieldName, '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/'); - break; - case "email": - $v->rule('email', $fieldName); - break; - case "date": - $v->rule('date', $fieldName); - break; - case "checkbox": - $v->rule('accepted', $fieldName); - break; - case "url": - $v->rule('lengthMax', $fieldName, 200); - $v->rule('url', $fieldName); - break; - case "text": - $v->rule('lengthMax', $fieldName, 200); - $v->rule('regex', $fieldName, '/^[\pL0-9_ \-\.\?\!\/\:]*$/u'); - break; - case "textarea": - $v->rule('lengthMax', $fieldName, 1000); - $v->rule('noHTML', $fieldName); - // $v->rule('regex', $fieldName, '/<[^<]+>/'); - break; - case "paragraph": - $v->rule('lengthMax', $fieldName, 1000); - $v->rule('noHTML', $fieldName); - break; - case "password": - $v->rule('lengthMax', $fieldName, 100); - break; - default: - $v->rule('lengthMax', $fieldName, 1000); - $v->rule('regex', $fieldName, '/^[\pL0-9_ \-]*$/u'); - } - - return $this->validationResult($v, $objectName); - } - - /** - * result for validation - * - * @param obj $v the validation object. - * @return bool - */ - - public function validationResult($v, $name = false) - { - if($v->validate()) - { - return true; - } - else - { - if($name) - { - if(isset($_SESSION['errors'][$name])) - { - foreach ($v->errors() as $key => $val) - { - $_SESSION['errors'][$name][$key] = $val; - break; - } - } - else - { - $_SESSION['errors'][$name] = $v->errors(); - } - } - else - { - $_SESSION['errors'] = $v->errors(); - } - return false; - } - } +getUser($value); + if($userdata){ return false; } + return true; + }, 'taken'); + + Validator::addRule('userExists', function($field, $value, array $params, array $fields) use ($user) + { + $userdata = $user->getUser($value); + if($userdata){ return true; } + return false; + }, 'does not exist'); + + Validator::addRule('checkPassword', function($field, $value, array $params, array $fields) use ($user) + { + $userdata = $user->getUser($fields['username']); + if($userdata && password_verify($value, $userdata['password'])){ return true; } + return false; + }, 'wrong password'); + + Validator::addRule('emailAvailable', function($field, $value, array $params, array $fields) + { + $email = 'testmail@gmail.com'; + if($email){ return false; } + return true; + }, 'taken'); + + Validator::addRule('emailKnown', function($field, $value, array $params, array $fields) + { + $email = 'testmail@gmail.com'; + if(!$email){ return false; } + return true; + }, 'unknown'); + + Validator::addRule('noSpecialChars', function($field, $value, array $params, array $fields) + { + $format = '/[!@#$%^&*()_+=\[\]{};\':"\\|,.<>\/?]/'; + if ( preg_match($format, $value)) + { + return false; + } + return true; + }, 'contains special characters'); + + Validator::addRule('noHTML', function($field, $value, array $params, array $fields) + { + if ( $value == strip_tags($value) ) + { + return true; + } + return false; + }, 'contains html'); + + Validator::addRule('markdownSecure', function($field, $value, array $params, array $fields) + { + /* strip out code blocks and blockquotes */ + $value = preg_replace('/`{4,}[\s\S]+?`{4,}/', '', $value); + $value = preg_replace('/`{3,}[\s\S]+?`{3,}/', '', $value); + $value = preg_replace('/`{2,}[\s\S]+?`{2,}/', '', $value); + $value = preg_replace('/`{1,}[\s\S]+?`{1,}/', '', $value); + $value = preg_replace('/>[\s\S]+?[\n\r]/', '', $value); + + if ( $value == strip_tags($value) ) + { + return true; + } + return false; + }, 'not secure. For code please use markdown `inline-code` or ````fenced code blocks````.'); + } + + /** + * validation for signup form + * + * @param array $params with form data. + * @return obj $v the validation object passed to a result method. + */ + + public function signin(array $params) + { + $v = new Validator($params); + $v->rule('required', ['username', 'password'])->message("Required"); + $v->rule('alphaNum', 'username')->message("Invalid characters"); + $v->rule('lengthBetween', 'password', 5, 20)->message("Length between 5 - 20"); + $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20"); + + if($v->validate()) + { + return true; + } + else + { + return false; + } + } + + /** + * validation for signup form + * + * @param array $params with form data. + * @return obj $v the validation object passed to a result method. + */ + + public function newUser(array $params, $userroles) + { + $v = new Validator($params); + $v->rule('required', ['username', 'email', 'password'])->message("required"); + $v->rule('alphaNum', 'username')->message("invalid characters"); + $v->rule('lengthBetween', 'password', 5, 20)->message("Length between 5 - 20"); + $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20"); + $v->rule('userAvailable', 'username')->message("User already exists"); + $v->rule('email', 'email')->message("e-mail is invalid"); + $v->rule('in', 'userrole', $userroles); + + return $this->validationResult($v); + } + + public function existingUser(array $params, $userroles) + { + $v = new Validator($params); + $v->rule('required', ['username', 'email', 'userrole'])->message("required"); + $v->rule('alphaNum', 'username')->message("invalid"); + $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20"); + $v->rule('userExists', 'username')->message("user does not exist"); + $v->rule('email', 'email')->message("e-mail is invalid"); + $v->rule('in', 'userrole', $userroles); + + return $this->validationResult($v); + } + + public function username($username) + { + $v = new Validator($username); + $v->rule('alphaNum', 'username')->message("Only alpha-numeric characters allowed"); + $v->rule('lengthBetween', 'username', 3, 20)->message("Length between 3 - 20"); + + return $this->validationResult($v); + } + + /** + * validation for changing the password + * + * @param array $params with form data. + * @return obj $v the validation object passed to a result method. + */ + + public function newPassword(array $params) + { + $v = new Validator($params); + $v->rule('required', ['password', 'newpassword']); + $v->rule('lengthBetween', 'newpassword', 5, 20); + $v->rule('checkPassword', 'password')->message("Password is wrong"); + + return $this->validationResult($v); + } + + /** + * validation for system settings + * + * @param array $params with form data. + * @return obj $v the validation object passed to a result method. + */ + + public function settings(array $params, array $copyright, $name = false) + { + $v = new Validator($params); + + $v->rule('required', ['title', 'author', 'copyright', 'year', 'editor']); + $v->rule('lengthBetween', 'title', 2, 20); + $v->rule('lengthBetween', 'author', 2, 40); + $v->rule('regex', 'title', '/^[\pL0-9_ \-]*$/u'); + $v->rule('regex', 'author', '/^[\pL_ \-]*$/u'); + $v->rule('integer', 'year'); + $v->rule('length', 'year', 4); + $v->rule('in', 'editor', ['raw', 'visual']); + $v->rule('in', 'copyright', $copyright); + + return $this->validationResult($v, $name); + } + + /** + * validation for content editor + * + * @param array $params with form data. + * @return true or $v->errors with array of errors to use in json-response + */ + + public function editorInput(array $params) + { + $v = new Validator($params); + + $v->rule('required', ['title', 'content', 'url']); + $v->rule('lengthBetween', 'title', 2, 100); + $v->rule('noHTML', 'title'); + $v->rule('markdownSecure', 'content'); + + if($v->validate()) + { + return true; + } + else + { + return $v->errors(); + } + } + + public function blockInput(array $params) + { + $v = new Validator($params); + + $v->rule('required', ['markdown', 'block_id', 'url']); + $v->rule('markdownSecure', 'markdown'); + $v->rule('regex', 'block_id', '/^[0-9.]+$/i'); + + if($v->validate()) + { + return true; + } + else + { + return $v->errors(); + } + } + + /** + * validation for resort navigation + * + * @param array $params with form data. + * @return true or $v->errors with array of errors to use in json-response + */ + + public function navigationSort(array $params) + { + $v = new Validator($params); + + $v->rule('required', ['item_id', 'parent_id_from', 'parent_id_to']); + $v->rule('regex', 'item_id', '/^[0-9.]+$/i'); + $v->rule('regex', 'parent_id_from', '/^[a-zA-Z0-9.]+$/i'); + $v->rule('regex', 'parent_id_to', '/^[a-zA-Z0-9.]+$/i'); + $v->rule('integer', 'index_new'); + + if($v->validate()) + { + return true; + } + else + { + return $v->errors(); + } + } + + /** + * validation for new navigation items + * + * @param array $params with form data. + * @return true or $v->errors with array of errors to use in json-response + */ + + public function navigationItem(array $params) + { + $v = new Validator($params); + + $v->rule('required', ['folder_id', 'item_name', 'type', 'url']); + $v->rule('regex', 'folder_id', '/^[0-9.]+$/i'); + $v->rule('noSpecialChars', 'item_name'); + $v->rule('lengthBetween', 'item_name', 1, 40); + $v->rule('in', 'type', ['file', 'folder']); + + if($v->validate()) + { + return true; + } + else + { + return $v->errors(); + } + } + + /** + * validation for dynamic fields ( settings for themes and plugins) + * + * @param string $fieldName with the name of the field. + * @param array or string $fieldValue with the values of the field. + * @param string $objectName with the name of the plugin or theme. + * @param array $fieldDefinitions with the field definitions as multi-dimensional array. + * @return obj $v the validation object passed to a result method. + */ + + public function objectField($fieldName, $fieldValue, $objectName, $fieldDefinitions) + { + $v = new Validator(array($fieldName => $fieldValue)); + + if(isset($fieldDefinitions['required'])) + { + $v->rule('required', $fieldName); + } + + switch($fieldDefinitions['type']) + { + case "select": + /* create array with option keys as value */ + $options = array(); + foreach($fieldDefinitions['options'] as $key => $value){ $options[] = $key; } + $v->rule('in', $fieldName, $options); + break; + case "radio": + $v->rule('in', $fieldName, $fieldDefinitions['options']); + break; + case "checkboxlist": + /* create array with option keys as value */ + $options = array(); + foreach($fieldDefinitions['options'] as $key => $value){ $options[] = $key; } + /* loop over input values and check, if the options of the field definitions (options for checkboxlist) contains the key (input from user, key is used as value, value is used as label) */ + foreach($fieldValue as $key => $value) + { + $v->rule('in', $key, $options); + } + break; + case "color": + $v->rule('regex', $fieldName, '/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})$/'); + break; + case "email": + $v->rule('email', $fieldName); + break; + case "date": + $v->rule('date', $fieldName); + break; + case "checkbox": + $v->rule('accepted', $fieldName); + break; + case "url": + $v->rule('lengthMax', $fieldName, 200); + $v->rule('url', $fieldName); + break; + case "text": + $v->rule('lengthMax', $fieldName, 200); + $v->rule('regex', $fieldName, '/^[\pL0-9_ \-\.\?\!\/\:]*$/u'); + break; + case "textarea": + $v->rule('lengthMax', $fieldName, 1000); + $v->rule('noHTML', $fieldName); + // $v->rule('regex', $fieldName, '/<[^<]+>/'); + break; + case "paragraph": + $v->rule('lengthMax', $fieldName, 1000); + $v->rule('noHTML', $fieldName); + break; + case "password": + $v->rule('lengthMax', $fieldName, 100); + break; + default: + $v->rule('lengthMax', $fieldName, 1000); + $v->rule('regex', $fieldName, '/^[\pL0-9_ \-]*$/u'); + } + + return $this->validationResult($v, $objectName); + } + + /** + * result for validation + * + * @param obj $v the validation object. + * @return bool + */ + + public function validationResult($v, $name = false) + { + if($v->validate()) + { + return true; + } + else + { + if($name) + { + if(isset($_SESSION['errors'][$name])) + { + foreach ($v->errors() as $key => $val) + { + $_SESSION['errors'][$name][$key] = $val; + break; + } + } + else + { + $_SESSION['errors'][$name] = $v->errors(); + } + } + else + { + $_SESSION['errors'] = $v->errors(); + } + return false; + } + } } \ No newline at end of file diff --git a/system/Models/VersionCheck.php b/system/Models/VersionCheck.php index 4350993..59d62d6 100644 --- a/system/Models/VersionCheck.php +++ b/system/Models/VersionCheck.php @@ -1,25 +1,25 @@ -array( - 'method' => "GET", - 'header' => "Referer: $url\r\n" - ) - ); - - $context = stream_context_create($opts); - - if(false === ($version = @file_get_contents('http://typemill.net/api/v1/checkversion', false, $context))) - { - return false; - } - $version = json_decode($version); - return $version->system->typemill; - } +array( + 'method' => "GET", + 'header' => "Referer: $url\r\n" + ) + ); + + $context = stream_context_create($opts); + + if(false === ($version = @file_get_contents('http://typemill.net/api/v1/checkversion', false, $context))) + { + return false; + } + $version = json_decode($version); + return $version->system->typemill; + } } \ No newline at end of file diff --git a/system/Models/Write.php b/system/Models/Write.php index d76abf8..bc10ca7 100644 --- a/system/Models/Write.php +++ b/system/Models/Write.php @@ -1,131 +1,160 @@ -basePath = $basePath; - } - - public function checkPath($folder) - { - $folderPath = $this->basePath . $folder; - - if(!is_dir($folderPath)) - { - if(@mkdir($folderPath, 0774, true)) - { - return true; - } - else - { -# throw new \Exception("The folder '{$folder}' is missing and we could not create it. Please create the folder manually on your server."); - return false; - } - } - - if(@is_writable($folderPath)) - { - return true; - } - else - { -# throw new \Exception("Please make the folder '{$folder}' writable."); - return false; - } - return true; - } - - protected function checkFile($folder, $file) - { - if(!file_exists($this->basePath . $folder . DIRECTORY_SEPARATOR . $file)) - { - return false; - } - return true; - } - - public function writeFile($folder, $file, $data) - { - if($this->checkPath($folder)) - { - $filePath = $this->basePath . $folder . DIRECTORY_SEPARATOR . $file; - - $openFile = @fopen($filePath, "w"); - - if(!$openFile) - { - return false; - } - - fwrite($openFile, $data); - fclose($openFile); - - return true; - } - return false; - } - - public function getFile($folderName, $fileName) - { - if($this->checkFile($folderName, $fileName)) - { - $fileContent = file_get_contents($folderName . DIRECTORY_SEPARATOR . $fileName); - return $fileContent; - } - return false; - } - - public function moveElement($item, $folderPath, $index) - { - $filetypes = array('md', 'txt'); - - # set new order as string - $newOrder = ($index < 10) ? '0' . $index : $index; - - # create new path with foldername or filename but without file-type - $newPath = $this->basePath . 'content' . $folderPath . DIRECTORY_SEPARATOR . $newOrder . '-' . str_replace(" ", "-", $item->name); - - if($item->elementType == 'folder') - { - $oldPath = $this->basePath . 'content' . $item->path; - if(@rename($oldPath, $newPath)) - { - return true; - } - return false; - } - - # create old path but without filetype - $oldPath = substr($item->path, 0, strpos($item->path, ".")); - $oldPath = $this->basePath . 'content' . $oldPath; - - $result = true; - - foreach($filetypes as $filetype) - { - $oldFilePath = $oldPath . '.' . $filetype; - $newFilePath = $newPath . '.' . $filetype; - - #check if file with filetype exists and rename - if($oldFilePath != $newFilePath && file_exists($oldFilePath)) - { - if(@rename($oldFilePath, $newFilePath)) - { - $result = $result; - } - else - { - $result = false; - } - } - } - - return $result; - } +basePath = $basePath; + } + + public function checkPath($folder) + { + $folderPath = $this->basePath . $folder; + + if(!is_dir($folderPath)) + { + if(@mkdir($folderPath, 0774, true)) + { + return true; + } + else + { +# throw new \Exception("The folder '{$folder}' is missing and we could not create it. Please create the folder manually on your server."); + return false; + } + } + + if(@is_writable($folderPath)) + { + return true; + } + else + { +# throw new \Exception("Please make the folder '{$folder}' writable."); + return false; + } + return true; + } + + protected function checkFile($folder, $file) + { + if(!file_exists($this->basePath . $folder . DIRECTORY_SEPARATOR . $file)) + { + return false; + } + return true; + } + + protected function checkFileWithPath($filepath) + { + if(!file_exists($this->basePath . $filepath)) + { + return false; + } + return true; + } + + public function writeFile($folder, $file, $data) + { + if($this->checkPath($folder)) + { + $filePath = $this->basePath . $folder . DIRECTORY_SEPARATOR . $file; + + $openFile = @fopen($filePath, "w"); + + if(!$openFile) + { + return false; + } + + fwrite($openFile, $data); + fclose($openFile); + + return true; + } + return false; + } + + public function getFile($folderName, $fileName) + { + if($this->checkFile($folderName, $fileName)) + { + $fileContent = file_get_contents($folderName . DIRECTORY_SEPARATOR . $fileName); + return $fileContent; + } + return false; + } + + public function getFileWithPath($filepath) + { + if($this->checkFileWithPath($filepath)) + { + $fileContent = file_get_contents($filepath); + return $fileContent; + } + return false; + } + + public function deleteFileWithPath($filepath) + { + if($this->checkFileWithPath($filepath)) + { + unlink($this->basePath . $filepath); + return true; + } + return false; + } + + public function moveElement($item, $folderPath, $index) + { + $filetypes = array('md', 'txt'); + + # set new order as string + $newOrder = ($index < 10) ? '0' . $index : $index; + + # create new path with foldername or filename but without file-type + $newPath = $this->basePath . 'content' . $folderPath . DIRECTORY_SEPARATOR . $newOrder . '-' . str_replace(" ", "-", $item->name); + + if($item->elementType == 'folder') + { + $oldPath = $this->basePath . 'content' . $item->path; + if(@rename($oldPath, $newPath)) + { + return true; + } + return false; + } + + # create old path but without filetype + $oldPath = substr($item->path, 0, strpos($item->path, ".")); + $oldPath = $this->basePath . 'content' . $oldPath; + + $result = true; + + foreach($filetypes as $filetype) + { + $oldFilePath = $oldPath . '.' . $filetype; + $newFilePath = $newPath . '.' . $filetype; + + #check if file with filetype exists and rename + if($oldFilePath != $newFilePath && file_exists($oldFilePath)) + { + if(@rename($oldFilePath, $newFilePath)) + { + $result = $result; + } + else + { + $result = false; + } + } + } + + return $result; + } } \ No newline at end of file diff --git a/system/Models/WriteCache.php b/system/Models/WriteCache.php index 9721618..59709fc 100644 --- a/system/Models/WriteCache.php +++ b/system/Models/WriteCache.php @@ -1,84 +1,84 @@ -checkPath($folderName)) - { - return false; - } - - if(!$this->checkFile($folderName, $fileName)) - { - $this->writeFile($folderName, $fileName, time()); - return false; - } - - $lastRefresh = file_get_contents($folderName . DIRECTORY_SEPARATOR . $fileName); - - if(time() - $lastRefresh > $duration) - { - return false; - } - - return true; - } - - /** - * Updates a cache file. - * Serializes an object and writes it to the cache file together with a file that holds the last refresh time. - * @param object $cacheData has to be an object (e.g. navigation object). - * @param string $cacheFile has to be the name of the file you want to update (in case there are more than one cache files. - */ - public function updateCache($folderName, $cacheFileName, $requestFileName, $cacheData) - { - $sCacheData = serialize($cacheData); - $this->writeFile($folderName, $cacheFileName, $sCacheData); - if($requestFileName) - { - $this->writeFile($folderName, $requestFileName, time()); - } - } - - /** - * Get the recent cache. - * Takes a filename, gets the file and unserializes the cache into an object. - * @param string $fileName is the name of the cache file. - */ - public function getCache($folderName, $cacheFileName) - { - $sCacheData = $this->getFile($folderName, $cacheFileName); - if($sCacheData) - { - return unserialize($sCacheData); - } - return false; - } - - /** - * @todo Create a function to clear a specific cache file - */ - public function clearCache($name) - { - } - - /** - * @todo Create a function to clear all cache files - */ - public function clearAllCacheFiles() - { - } +checkPath($folderName)) + { + return false; + } + + if(!$this->checkFile($folderName, $fileName)) + { + $this->writeFile($folderName, $fileName, time()); + return false; + } + + $lastRefresh = file_get_contents($folderName . DIRECTORY_SEPARATOR . $fileName); + + if(time() - $lastRefresh > $duration) + { + return false; + } + + return true; + } + + /** + * Updates a cache file. + * Serializes an object and writes it to the cache file together with a file that holds the last refresh time. + * @param object $cacheData has to be an object (e.g. navigation object). + * @param string $cacheFile has to be the name of the file you want to update (in case there are more than one cache files. + */ + public function updateCache($folderName, $cacheFileName, $requestFileName, $cacheData) + { + $sCacheData = serialize($cacheData); + $this->writeFile($folderName, $cacheFileName, $sCacheData); + if($requestFileName) + { + $this->writeFile($folderName, $requestFileName, time()); + } + } + + /** + * Get the recent cache. + * Takes a filename, gets the file and unserializes the cache into an object. + * @param string $fileName is the name of the cache file. + */ + public function getCache($folderName, $cacheFileName) + { + $sCacheData = $this->getFile($folderName, $cacheFileName); + if($sCacheData) + { + return unserialize($sCacheData); + } + return false; + } + + /** + * @todo Create a function to clear a specific cache file + */ + public function clearCache($name) + { + } + + /** + * @todo Create a function to clear all cache files + */ + public function clearAllCacheFiles() + { + } } \ No newline at end of file diff --git a/system/Models/WriteSitemap.php b/system/Models/WriteSitemap.php index d7090a7..409591f 100644 --- a/system/Models/WriteSitemap.php +++ b/system/Models/WriteSitemap.php @@ -1,45 +1,45 @@ -' . "\n"; - $sitemap .= '' . "\n"; - $sitemap = $this->addUrlSet($sitemap, $baseUrl); - $sitemap .= $this->generateUrlSets($data); - $sitemap .= ''; - - $this->writeFile($folderName, $sitemapFileName, $sitemap); - $this->writeFile($folderName, $requestFileName, time()); - } - - public function generateUrlSets($data) - { - $urlset = ''; - - foreach($data as $item) - { - if($item->elementType == 'folder') - { - $urlset = $this->addUrlSet($urlset, $item->urlAbs); - $urlset .= $this->generateUrlSets($item->folderContent, $urlset); - } - else - { - $urlset = $this->addUrlSet($urlset, $item->urlAbs); - } - } - return $urlset; - } - - public function addUrlSet($urlset, $url) - { - $urlset .= ' ' . "\n"; - $urlset .= ' ' . $url . '' . "\n"; - $urlset .= ' ' . "\n"; - return $urlset; - } +' . "\n"; + $sitemap .= '' . "\n"; + $sitemap = $this->addUrlSet($sitemap, $baseUrl); + $sitemap .= $this->generateUrlSets($data); + $sitemap .= ''; + + $this->writeFile($folderName, $sitemapFileName, $sitemap); + $this->writeFile($folderName, $requestFileName, time()); + } + + public function generateUrlSets($data) + { + $urlset = ''; + + foreach($data as $item) + { + if($item->elementType == 'folder') + { + $urlset = $this->addUrlSet($urlset, $item->urlAbs); + $urlset .= $this->generateUrlSets($item->folderContent, $urlset); + } + else + { + $urlset = $this->addUrlSet($urlset, $item->urlAbs); + } + } + return $urlset; + } + + public function addUrlSet($urlset, $url) + { + $urlset .= ' ' . "\n"; + $urlset .= ' ' . $url . '' . "\n"; + $urlset .= ' ' . "\n"; + return $urlset; + } } \ No newline at end of file diff --git a/system/Models/WriteYaml.php b/system/Models/WriteYaml.php index 7577493..6473dcc 100644 --- a/system/Models/WriteYaml.php +++ b/system/Models/WriteYaml.php @@ -1,38 +1,38 @@ -getFile($folderName, $yamlFileName); - - if($yaml) - { - return \Symfony\Component\Yaml\Yaml::parse($yaml); - } - return false; - } - - /** - * Writes a yaml file. - * @param string $fileName is the name of the Yaml Folder. - * @param string $yamlFileName is the name of the Yaml File. - * @param array $contentArray is the content as an array. - */ - public function updateYaml($folderName, $yamlFileName, $contentArray) - { - $yaml = \Symfony\Component\Yaml\Yaml::dump($contentArray,6); - if($this->writeFile($folderName, $yamlFileName, $yaml)) - { - return true; - } - return false; - } +getFile($folderName, $yamlFileName); + + if($yaml) + { + return \Symfony\Component\Yaml\Yaml::parse($yaml); + } + return false; + } + + /** + * Writes a yaml file. + * @param string $fileName is the name of the Yaml Folder. + * @param string $yamlFileName is the name of the Yaml File. + * @param array $contentArray is the content as an array. + */ + public function updateYaml($folderName, $yamlFileName, $contentArray) + { + $yaml = \Symfony\Component\Yaml\Yaml::dump($contentArray,6); + if($this->writeFile($folderName, $yamlFileName, $yaml)) + { + return true; + } + return false; + } } \ No newline at end of file diff --git a/system/Plugin.php b/system/Plugin.php index 7230035..1ff598e 100644 --- a/system/Plugin.php +++ b/system/Plugin.php @@ -1,149 +1,194 @@ -container = $container; - } - - protected function getSettings() - { - return $this->container->get('settings'); - } - - protected function getPluginSettings($plugin) - { - return $this->container->get('settings')['plugins'][$plugin]; - } - - protected function getRoute() - { - return $this->container['request']->getUri(); - } - - protected function getPath() - { - return $this->container['request']->getUri()->getPath(); - } - - protected function getDispatcher() - { - return $this->$dispatcher; - } - - protected function getTwig() - { - return $this->container['view']; - } - - protected function addTwigGlobal($name, $class) - { - $this->container->view->getEnvironment()->addGlobal($name, $class); - } - - protected function addTwigFilter($name, $filter) - { - $filter = new \Twig_SimpleFilter($name, $filter); - $this->container->view->getEnvironment()->addFilter($filter); - } - - protected function addTwigFunction($name, $function) - { - $function = new \Twig_SimpleFunction($name, $function); - $this->container->view->getEnvironment()->addFunction($function); - } - - protected function addJS($JS) - { - $this->container->assets->addJS($JS); - } - - protected function addInlineJS($JS) - { - $this->container->assets->addInlineJS($JS); - } - - protected function addCSS($CSS) - { - $this->container->assets->addCSS($CSS); - } - - protected function addInlineCSS($CSS) - { - $this->container->assets->addInlineCSS($CSS); - } - - protected function markdownToHtml($markdown) - { - $parsedown = new ParsedownExtension(); - - $contentArray = $parsedown->text($markdown); - $html = $parsedown->markup($contentArray,false); - - return $html; - } - - protected function getFormData($pluginName) - { - $flash = $this->container->flash->getMessages(); - - if(isset($flash['formdata'])) - { - $yaml = new Models\WriteYaml(); - $formdata = $yaml->getYaml('settings', 'formdata.yaml'); - $yaml->updateYaml('settings', 'formdata.yaml', ''); - - if($flash['formdata'][0] == $pluginName && isset($formdata[$pluginName])) - { - return $formdata[$pluginName]; - } - } - elseif(isset($flash['publicform']) && $flash['publicform'][0] == 'bot') - { - return 'bot'; - } - return false; - } - - protected function generateForm($pluginName) - { - $fieldsModel = new Fields(); - - $pluginDefinitions = \Typemill\Settings::getObjectSettings('plugins', $pluginName); - $settings = $this->getSettings(); - $buttonlabel = isset($settings['plugins'][$pluginName]['button_label']) ? $settings['plugins'][$pluginName]['button_label'] : false; - $recaptcha = isset($settings['plugins'][$pluginName]['recaptcha']) ? $settings['plugins'][$pluginName]['recaptcha_webkey'] : false; - - if(isset($pluginDefinitions['public']['fields'])) - { - # add simple honeypot spam protection - $pluginDefinitions['public']['fields']['personal-mail'] = ['type' => 'text', 'class' => 'personal-mail']; - - # get all the fields and prefill them with the dafault-data, the user-data or old input data - $fields = $fieldsModel->getFields($settings, 'plugins', $pluginName, $pluginDefinitions, 'public'); - - # get Twig Instance - $twig = $this->getTwig(); - - # render each field and add it to the form - $form = $twig->fetch('/partials/form.twig', ['fields' => $fields, 'itemName' => $pluginName, 'object' => 'plugins', 'recaptcha_webkey' => $recaptcha, 'buttonlabel' => $buttonlabel]); - } - - return $form; - } +container = $container; + } + + protected function isXhr() + { + if($this->container['request']->isXhr()) + { + return true; + } + return false; + } + + protected function getParams() + { + return $this->container['request']->getParams(); + } + + protected function returnJson($data) + { + return $this->container['response'] + ->withHeader("Content-Type", "application/json") + ->withStatus(200) + ->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); + } + + protected function returnJsonError($data) + { + return $this->container['response'] + ->withHeader("Content-Type", "application/json") + ->withStatus(400) + ->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT)); + } + + protected function getSettings() + { + return $this->container->get('settings'); + } + + protected function getPluginSettings($plugin) + { + return $this->container->get('settings')['plugins'][$plugin]; + } + + protected function getRoute() + { + return $this->container['request']->getUri(); + } + + protected function getPath() + { + return $this->container['request']->getUri()->getPath(); + } + + protected function getDispatcher() + { + return $this->$dispatcher; + } + + protected function getTwig() + { + return $this->container['view']; + } + + protected function addTwigGlobal($name, $class) + { + $this->container->view->getEnvironment()->addGlobal($name, $class); + } + + protected function addTwigFilter($name, $filter) + { + $filter = new \Twig_SimpleFilter($name, $filter); + $this->container->view->getEnvironment()->addFilter($filter); + } + + protected function addTwigFunction($name, $function) + { + $function = new \Twig_SimpleFunction($name, $function); + $this->container->view->getEnvironment()->addFunction($function); + } + + protected function addJS($JS) + { + $this->container->assets->addJS($JS); + } + + protected function addInlineJS($JS) + { + $this->container->assets->addInlineJS($JS); + } + + protected function addCSS($CSS) + { + $this->container->assets->addCSS($CSS); + } + + protected function addInlineCSS($CSS) + { + $this->container->assets->addInlineCSS($CSS); + } + + protected function activateAxios() + { + $this->container->assets->activateAxios(); + } + + protected function activateVue() + { + $this->container->assets->activateVue(); + } + + protected function activateTachyons() + { + $this->container->assets->activateTachyons(); + } + + protected function markdownToHtml($markdown) + { + $parsedown = new ParsedownExtension(); + + $contentArray = $parsedown->text($markdown); + $html = $parsedown->markup($contentArray,false); + + return $html; + } + + protected function getFormData($pluginName) + { + $flash = $this->container->flash->getMessages(); + + if(isset($flash['formdata'])) + { + $yaml = new Models\WriteYaml(); + $formdata = $yaml->getYaml('settings', 'formdata.yaml'); + $yaml->updateYaml('settings', 'formdata.yaml', ''); + + if($flash['formdata'][0] == $pluginName && isset($formdata[$pluginName])) + { + return $formdata[$pluginName]; + } + } + elseif(isset($flash['publicform']) && $flash['publicform'][0] == 'bot') + { + return 'bot'; + } + return false; + } + + protected function generateForm($pluginName) + { + $fieldsModel = new Fields(); + + $pluginDefinitions = \Typemill\Settings::getObjectSettings('plugins', $pluginName); + $settings = $this->getSettings(); + $buttonlabel = isset($settings['plugins'][$pluginName]['button_label']) ? $settings['plugins'][$pluginName]['button_label'] : false; + $recaptcha = isset($settings['plugins'][$pluginName]['recaptcha']) ? $settings['plugins'][$pluginName]['recaptcha_webkey'] : false; + + if(isset($pluginDefinitions['public']['fields'])) + { + # add simple honeypot spam protection + $pluginDefinitions['public']['fields']['personal-mail'] = ['type' => 'text', 'class' => 'personal-mail']; + + # get all the fields and prefill them with the dafault-data, the user-data or old input data + $fields = $fieldsModel->getFields($settings, 'plugins', $pluginName, $pluginDefinitions, 'public'); + + # get Twig Instance + $twig = $this->getTwig(); + + # render each field and add it to the form + $form = $twig->fetch('/partials/form.twig', ['fields' => $fields, 'itemName' => $pluginName, 'object' => 'plugins', 'recaptcha_webkey' => $recaptcha, 'buttonlabel' => $buttonlabel]); + } + + return $form; + } } \ No newline at end of file diff --git a/system/Plugins.php b/system/Plugins.php index c33f51f..f40000a 100644 --- a/system/Plugins.php +++ b/system/Plugins.php @@ -1,112 +1,112 @@ -scanPluginFolder(); - $classNames = array(); - - /* iterate over plugin folders */ - foreach($pluginFolder as $plugin) - { - $className = '\\Plugins\\' . $plugin . '\\' . $plugin; - - /* if plugin-class and subscribe-method exists, add classname to array */ - if(class_exists($className) /* && method_exists($className, 'getSubscribedEvents') */) - { - $classNames[] = array('className' => $className, 'name' => $plugin); - } - } - return $classNames; - } - - public function getNewRoutes($className, $routes) - { - /* if route-method exists in plugin-class */ - if(method_exists($className, 'addNewRoutes')) - { - /* add the routes */ - $pluginRoutes = $className::addNewRoutes(); - - /* multi-dimensional or simple array of routes */ - if(isset($pluginRoutes[0])) - { - /* if they are properly formatted, add them to routes array */ - foreach($pluginRoutes as $pluginRoute) - { - if($this->checkRouteArray($routes,$pluginRoute)) - { - $pluginRoute['route'] = strtolower($pluginRoute['route']); - $routes[] = $pluginRoute; - } - } - } - elseif(is_array($routes)) - { - if($this->checkRouteArray($routes,$pluginRoutes)) - { - $pluginRoutes['route'] = strtolower($pluginRoutes['route']); - $routes[] = $pluginRoutes; - } - } - } - - return $routes; - } - - public function getNewMiddleware($className, $middleware) - { - if(method_exists($className, 'addNewMiddleware')) - { - $pluginMiddleware = $className::addNewMiddleware(); - - if($pluginMiddleware) - { - $middleware[] = $pluginMiddleware; - } - } - - return $middleware; - } - - private function checkRouteArray($routes,$route) - { - if( - isset($route['httpMethod']) AND in_array($route['httpMethod'], array('get','post','put','delete','head','patch','options')) - AND isset($route['route']) AND is_string($route['route']) - AND isset($route['class']) AND is_string($route['class']) - AND !$this->in_array_r(strtolower($route['route']),$routes)) - { - return true; - } - return false; - } - - private function in_array_r($needle, $haystack, $strict = false) - { - foreach ($haystack as $item) - { - if (($strict ? $item === $needle : $item == $needle) || (is_array($item) && $this->in_array_r($needle, $item, $strict))) - { - return true; - } - } - return false; - } - - private function scanPluginFolder() - { - $pluginsDir = __DIR__ . '/../plugins'; - - /* check if plugins directory exists */ - if(!is_dir($pluginsDir)){ return array(); } - - /* get all plugins folder */ - $plugins = array_diff(scandir($pluginsDir), array('..', '.')); - - return $plugins; - } +scanPluginFolder(); + $classNames = array(); + + /* iterate over plugin folders */ + foreach($pluginFolder as $plugin) + { + $className = '\\Plugins\\' . $plugin . '\\' . $plugin; + + /* if plugin-class and subscribe-method exists, add classname to array */ + if(class_exists($className) /* && method_exists($className, 'getSubscribedEvents') */) + { + $classNames[] = array('className' => $className, 'name' => $plugin); + } + } + return $classNames; + } + + public function getNewRoutes($className, $routes) + { + /* if route-method exists in plugin-class */ + if(method_exists($className, 'addNewRoutes')) + { + /* add the routes */ + $pluginRoutes = $className::addNewRoutes(); + + /* multi-dimensional or simple array of routes */ + if(isset($pluginRoutes[0])) + { + /* if they are properly formatted, add them to routes array */ + foreach($pluginRoutes as $pluginRoute) + { + if($this->checkRouteArray($routes,$pluginRoute)) + { + $pluginRoute['route'] = strtolower($pluginRoute['route']); + $routes[] = $pluginRoute; + } + } + } + elseif(is_array($routes)) + { + if($this->checkRouteArray($routes,$pluginRoutes)) + { + $pluginRoutes['route'] = strtolower($pluginRoutes['route']); + $routes[] = $pluginRoutes; + } + } + } + + return $routes; + } + + public function getNewMiddleware($className, $middleware) + { + if(method_exists($className, 'addNewMiddleware')) + { + $pluginMiddleware = $className::addNewMiddleware(); + + if($pluginMiddleware) + { + $middleware[] = $pluginMiddleware; + } + } + + return $middleware; + } + + private function checkRouteArray($routes,$route) + { + if( + isset($route['httpMethod']) AND in_array($route['httpMethod'], array('get','post','put','delete','head','patch','options')) + AND isset($route['route']) AND is_string($route['route']) + AND isset($route['class']) AND is_string($route['class']) + AND !$this->in_array_r(strtolower($route['route']),$routes)) + { + return true; + } + return false; + } + + private function in_array_r($needle, $haystack, $strict = false) + { + foreach ($haystack as $item) + { + if (($strict ? $item === $needle : $item == $needle) || (is_array($item) && $this->in_array_r($needle, $item, $strict))) + { + return true; + } + } + return false; + } + + private function scanPluginFolder() + { + $pluginsDir = __DIR__ . '/../plugins'; + + /* check if plugins directory exists */ + if(!is_dir($pluginsDir)){ return array(); } + + /* get all plugins folder */ + $plugins = array_diff(scandir($pluginsDir), array('..', '.')); + + return $plugins; + } } \ No newline at end of file diff --git a/system/Routes/Api.php b/system/Routes/Api.php index b52ffba..712f211 100644 --- a/system/Routes/Api.php +++ b/system/Routes/Api.php @@ -1,29 +1,29 @@ -get('/api/v1/themes', SettingsController::class . ':getThemeSettings')->setName('api.themes')->add(new RestrictApiAccess($container['router'])); - -$app->post('/api/v1/article/markdown', ContentApiController::class . ':getArticleMarkdown')->setName('api.article.markdown')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/article/html', ContentApiController::class . ':getArticleHtml')->setName('api.article.html')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/article/publish', ContentApiController::class . ':publishArticle')->setName('api.article.publish')->add(new RestrictApiAccess($container['router'])); -$app->delete('/api/v1/article/unpublish', ContentApiController::class . ':unpublishArticle')->setName('api.article.unpublish')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/article', ContentApiController::class . ':createArticle')->setName('api.article.create')->add(new RestrictApiAccess($container['router'])); -$app->put('/api/v1/article', ContentApiController::class . ':updateArticle')->setName('api.article.update')->add(new RestrictApiAccess($container['router'])); -$app->delete('/api/v1/article', ContentApiController::class . ':deleteArticle')->setName('api.article.delete')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/article/sort', ContentApiController::class . ':sortArticle')->setName('api.article.sort')->add(new RestrictApiAccess($container['router'])); -$app->post('/api/v1/basefolder', ContentApiController::class . ':createBaseFolder')->setName('api.basefolder.create')->add(new RestrictApiAccess($container['router'])); -$app->get('/api/v1/navigation', ContentApiController::class . ':getNavigation')->setName('api.navigation.get')->add(new RestrictApiAccess($container['router'])); - -$app->post('/api/v1/block', ContentApiController::class . ':addBlock')->setName('api.block.add')->add(new RestrictApiAccess($container['router'])); -$app->put('/api/v1/block', ContentApiController::class . ':updateBlock')->setName('api.block.update')->add(new RestrictApiAccess($container['router'])); -$app->delete('/api/v1/block', ContentApiController::class . ':deleteBlock')->setName('api.block.delete')->add(new RestrictApiAccess($container['router'])); -$app->put('/api/v1/moveblock', ContentApiController::class . ':moveBlock')->setName('api.block.move')->add(new RestrictApiAccess($container['router'])); - -$app->post('/api/v1/image', ContentApiController::class . ':createImage')->setName('api.image.create')->add(new RestrictApiAccess($container['router'])); -$app->put('/api/v1/image', ContentApiController::class . ':publishImage')->setName('api.image.publish')->add(new RestrictApiAccess($container['router'])); - +get('/api/v1/themes', SettingsController::class . ':getThemeSettings')->setName('api.themes')->add(new RestrictApiAccess($container['router'])); + +$app->post('/api/v1/article/markdown', ContentApiController::class . ':getArticleMarkdown')->setName('api.article.markdown')->add(new RestrictApiAccess($container['router'])); +$app->post('/api/v1/article/html', ContentApiController::class . ':getArticleHtml')->setName('api.article.html')->add(new RestrictApiAccess($container['router'])); +$app->post('/api/v1/article/publish', ContentApiController::class . ':publishArticle')->setName('api.article.publish')->add(new RestrictApiAccess($container['router'])); +$app->delete('/api/v1/article/unpublish', ContentApiController::class . ':unpublishArticle')->setName('api.article.unpublish')->add(new RestrictApiAccess($container['router'])); +$app->post('/api/v1/article', ContentApiController::class . ':createArticle')->setName('api.article.create')->add(new RestrictApiAccess($container['router'])); +$app->put('/api/v1/article', ContentApiController::class . ':updateArticle')->setName('api.article.update')->add(new RestrictApiAccess($container['router'])); +$app->delete('/api/v1/article', ContentApiController::class . ':deleteArticle')->setName('api.article.delete')->add(new RestrictApiAccess($container['router'])); +$app->post('/api/v1/article/sort', ContentApiController::class . ':sortArticle')->setName('api.article.sort')->add(new RestrictApiAccess($container['router'])); +$app->post('/api/v1/basefolder', ContentApiController::class . ':createBaseFolder')->setName('api.basefolder.create')->add(new RestrictApiAccess($container['router'])); +$app->get('/api/v1/navigation', ContentApiController::class . ':getNavigation')->setName('api.navigation.get')->add(new RestrictApiAccess($container['router'])); + +$app->post('/api/v1/block', ContentApiController::class . ':addBlock')->setName('api.block.add')->add(new RestrictApiAccess($container['router'])); +$app->put('/api/v1/block', ContentApiController::class . ':updateBlock')->setName('api.block.update')->add(new RestrictApiAccess($container['router'])); +$app->delete('/api/v1/block', ContentApiController::class . ':deleteBlock')->setName('api.block.delete')->add(new RestrictApiAccess($container['router'])); +$app->put('/api/v1/moveblock', ContentApiController::class . ':moveBlock')->setName('api.block.move')->add(new RestrictApiAccess($container['router'])); + +$app->post('/api/v1/image', ContentApiController::class . ':createImage')->setName('api.image.create')->add(new RestrictApiAccess($container['router'])); +$app->put('/api/v1/image', ContentApiController::class . ':publishImage')->setName('api.image.publish')->add(new RestrictApiAccess($container['router'])); + $app->post('/api/v1/video', ContentApiController::class . ':saveVideoImage')->setName('api.video.save')->add(new RestrictApiAccess($container['router'])); \ No newline at end of file diff --git a/system/Routes/Web.php b/system/Routes/Web.php index a8e0080..46c7cf2 100644 --- a/system/Routes/Web.php +++ b/system/Routes/Web.php @@ -1,72 +1,72 @@ -get('/setup', SetupController::class . ':show')->setName('setup.show'); - $app->post('/setup', SetupController::class . ':create')->setName('setup.create'); -} -else -{ - $app->get('/setup', AuthController::class . ':redirect'); -} -if($settings['settings']['welcome']) -{ - $app->get('/setup/welcome', SetupController::class . ':welcome')->setName('setup.welcome')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); -} -else -{ - $app->get('/setup/welcome', AuthController::class . ':redirect')->setName('setup.welcome'); -} - -$app->post('/tm/formpost', FormController::class . ':savePublicForm')->setName('form.save'); - -$app->get('/tm', AuthController::class . ':redirect'); -$app->get('/tm/login', AuthController::class . ':show')->setName('auth.show')->add(new RedirectIfAuthenticated($container['router'])); -$app->post('/tm/login', AuthController::class . ':login')->setName('auth.login')->add(new RedirectIfAuthenticated($container['router'])); -$app->get('/tm/logout', AuthController::class . ':logout')->setName('auth.logout')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); - -$app->get('/tm/settings', SettingsController::class . ':showSettings')->setName('settings.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); -$app->post('/tm/settings', SettingsController::class . ':saveSettings')->setName('settings.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); -$app->get('/tm/themes', SettingsController::class . ':showThemes')->setName('themes.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); -$app->post('/tm/themes', SettingsController::class . ':saveThemes')->setName('themes.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); -$app->get('/tm/plugins', SettingsController::class . ':showPlugins')->setName('plugins.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); -$app->post('/tm/plugins', SettingsController::class . ':savePlugins')->setName('plugins.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); -$app->get('/tm/user/new', SettingsController::class . ':newUser')->setName('user.new')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); -$app->post('/tm/user/create', SettingsController::class . ':createUser')->setName('user.create')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); - -$app->post('/tm/user/update', SettingsController::class . ':updateUser')->setName('user.update')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); -$app->post('/tm/user/delete', SettingsController::class . ':deleteUser')->setName('user.delete')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); -$app->get('/tm/user/{username}', SettingsController::class . ':showUser')->setName('user.show')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); -$app->get('/tm/user', SettingsController::class . ':listUser')->setName('user.list')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); - -$app->get('/tm/content/raw[/{params:.*}]', ContentBackendController::class . ':showContent')->setName('content.raw')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); -$app->get('/tm/content/visual[/{params:.*}]', ContentBackendController::class . ':showBlox')->setName('content.visual')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); -$app->get('/tm/content[/{params:.*}]', ContentBackendController::class . ':showEmpty')->setName('content.empty')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); - -foreach($routes as $pluginRoute) -{ - $method = $pluginRoute['httpMethod']; - $route = $pluginRoute['route']; - $class = $pluginRoute['class']; - - if(isset($pluginRoute['name'])) - { - $app->{$method}($route, $class)->setName($pluginRoute['name']); - } - else - { - $app->{$method}($route, $class); - } -} - +get('/setup', SetupController::class . ':show')->setName('setup.show'); + $app->post('/setup', SetupController::class . ':create')->setName('setup.create'); +} +else +{ + $app->get('/setup', AuthController::class . ':redirect'); +} +if($settings['settings']['welcome']) +{ + $app->get('/setup/welcome', SetupController::class . ':welcome')->setName('setup.welcome')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); +} +else +{ + $app->get('/setup/welcome', AuthController::class . ':redirect')->setName('setup.welcome'); +} + +$app->post('/tm/formpost', FormController::class . ':savePublicForm')->setName('form.save'); + +$app->get('/tm', AuthController::class . ':redirect'); +$app->get('/tm/login', AuthController::class . ':show')->setName('auth.show')->add(new RedirectIfAuthenticated($container['router'])); +$app->post('/tm/login', AuthController::class . ':login')->setName('auth.login')->add(new RedirectIfAuthenticated($container['router'])); +$app->get('/tm/logout', AuthController::class . ':logout')->setName('auth.logout')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); + +$app->get('/tm/settings', SettingsController::class . ':showSettings')->setName('settings.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); +$app->post('/tm/settings', SettingsController::class . ':saveSettings')->setName('settings.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); +$app->get('/tm/themes', SettingsController::class . ':showThemes')->setName('themes.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); +$app->post('/tm/themes', SettingsController::class . ':saveThemes')->setName('themes.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); +$app->get('/tm/plugins', SettingsController::class . ':showPlugins')->setName('plugins.show')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); +$app->post('/tm/plugins', SettingsController::class . ':savePlugins')->setName('plugins.save')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); +$app->get('/tm/user/new', SettingsController::class . ':newUser')->setName('user.new')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); +$app->post('/tm/user/create', SettingsController::class . ':createUser')->setName('user.create')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); + +$app->post('/tm/user/update', SettingsController::class . ':updateUser')->setName('user.update')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); +$app->post('/tm/user/delete', SettingsController::class . ':deleteUser')->setName('user.delete')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); +$app->get('/tm/user/{username}', SettingsController::class . ':showUser')->setName('user.show')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); +$app->get('/tm/user', SettingsController::class . ':listUser')->setName('user.list')->add(new RedirectIfNoAdmin($container['router'], $container['flash'])); + +$app->get('/tm/content/raw[/{params:.*}]', ContentBackendController::class . ':showContent')->setName('content.raw')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); +$app->get('/tm/content/visual[/{params:.*}]', ContentBackendController::class . ':showBlox')->setName('content.visual')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); +$app->get('/tm/content[/{params:.*}]', ContentBackendController::class . ':showEmpty')->setName('content.empty')->add(new RedirectIfUnauthenticated($container['router'], $container['flash'])); + +foreach($routes as $pluginRoute) +{ + $method = $pluginRoute['httpMethod']; + $route = $pluginRoute['route']; + $class = $pluginRoute['class']; + + if(isset($pluginRoute['name'])) + { + $app->{$method}($route, $class)->setName($pluginRoute['name']); + } + else + { + $app->{$method}($route, $class); + } +} + $app->get('/[{params:.*}]', PageController::class . ':index')->setName('home'); \ No newline at end of file diff --git a/system/Settings.php b/system/Settings.php index 8899da7..896cc2b 100644 --- a/system/Settings.php +++ b/system/Settings.php @@ -1,124 +1,124 @@ - $settings); - } - - public static function getDefaultSettings() - { - $rootPath = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR; - - return [ - 'determineRouteBeforeAppMiddleware' => true, - 'displayErrorDetails' => false, - 'title' => 'TYPEMILL', - 'author' => 'Unknown', - 'copyright' => 'Copyright', - 'language' => 'en', - 'startpage' => true, - 'rootPath' => $rootPath, - 'theme' => ($theme = 'typemill'), - 'themeFolder' => ($themeFolder = 'themes'), - 'themeBasePath' => $rootPath, - 'themePath' => $rootPath . $themeFolder . DIRECTORY_SEPARATOR . $theme, - 'settingsPath' => $rootPath . 'settings', - 'userPath' => $rootPath . 'settings' . DIRECTORY_SEPARATOR . 'users', - 'authorPath' => __DIR__ . DIRECTORY_SEPARATOR . 'author' . DIRECTORY_SEPARATOR, - 'editor' => 'raw', - 'contentFolder' => 'content', - 'cache' => true, - 'cachePath' => $rootPath . 'cache', - 'version' => '1.2.15', - 'setup' => true, - 'welcome' => true, - 'images' => ['live' => ['width' => 820], 'mlibrary' => ['width' => 50, 'height' => 50]], - ]; - } - - public static function getUserSettings() - { - $yaml = new Models\WriteYaml(); - - $userSettings = $yaml->getYaml('settings', 'settings.yaml'); - - return $userSettings; - } - - public static function getObjectSettings($objectType, $objectName) - { - $yaml = new Models\WriteYaml(); - - $objectFolder = $objectType . DIRECTORY_SEPARATOR . $objectName; - $objectFile = $objectName . '.yaml'; - $objectSettings = $yaml->getYaml($objectFolder, $objectFile); - - return $objectSettings; - } - - public static function createSettings() - { - $yaml = new Models\WriteYaml(); - - # create initial settings file with only setup false - if($yaml->updateYaml('settings', 'settings.yaml', array('setup' => false))) - { - return true; - } - return false; - } - - public static function updateSettings($settings) - { - # only allow if usersettings already exists (setup has been done) - $userSettings = self::getUserSettings(); - - if($userSettings) - { - # whitelist settings that can be stored in usersettings (values are not relevant here, only keys) - $allowedUserSettings = ['displayErrorDetails' => false, - 'title' => false, - 'copyright' => false, - 'language' => false, - 'startpage' => false, - 'author' => false, - 'year' => false, - 'theme' => false, - 'editor' => false, - 'setup' => false, - 'welcome' => false, - 'images' => false, - 'plugins' => false, - 'themes' => false, - 'latestVersion' => false - ]; - - # cleanup the existing usersettings - $userSettings = array_intersect_key($userSettings, $allowedUserSettings); - - # cleanup the new settings passed as an argument - $settings = array_intersect_key($settings, $allowedUserSettings); - - # merge usersettings with new settings - $settings = array_merge($userSettings, $settings); - - /* write settings to yaml */ - $yaml = new Models\WriteYaml(); - $yaml->updateYaml('settings', 'settings.yaml', $settings); - } - } + $settings); + } + + public static function getDefaultSettings() + { + $rootPath = __DIR__ . DIRECTORY_SEPARATOR . '..' . DIRECTORY_SEPARATOR; + + return [ + 'determineRouteBeforeAppMiddleware' => true, + 'displayErrorDetails' => false, + 'title' => 'TYPEMILL', + 'author' => 'Unknown', + 'copyright' => 'Copyright', + 'language' => 'en', + 'startpage' => true, + 'rootPath' => $rootPath, + 'theme' => ($theme = 'typemill'), + 'themeFolder' => ($themeFolder = 'themes'), + 'themeBasePath' => $rootPath, + 'themePath' => $rootPath . $themeFolder . DIRECTORY_SEPARATOR . $theme, + 'settingsPath' => $rootPath . 'settings', + 'userPath' => $rootPath . 'settings' . DIRECTORY_SEPARATOR . 'users', + 'authorPath' => __DIR__ . DIRECTORY_SEPARATOR . 'author' . DIRECTORY_SEPARATOR, + 'editor' => 'raw', + 'contentFolder' => 'content', + 'cache' => true, + 'cachePath' => $rootPath . 'cache', + 'version' => '1.2.15', + 'setup' => true, + 'welcome' => true, + 'images' => ['live' => ['width' => 820], 'mlibrary' => ['width' => 50, 'height' => 50]], + ]; + } + + public static function getUserSettings() + { + $yaml = new Models\WriteYaml(); + + $userSettings = $yaml->getYaml('settings', 'settings.yaml'); + + return $userSettings; + } + + public static function getObjectSettings($objectType, $objectName) + { + $yaml = new Models\WriteYaml(); + + $objectFolder = $objectType . DIRECTORY_SEPARATOR . $objectName; + $objectFile = $objectName . '.yaml'; + $objectSettings = $yaml->getYaml($objectFolder, $objectFile); + + return $objectSettings; + } + + public static function createSettings() + { + $yaml = new Models\WriteYaml(); + + # create initial settings file with only setup false + if($yaml->updateYaml('settings', 'settings.yaml', array('setup' => false))) + { + return true; + } + return false; + } + + public static function updateSettings($settings) + { + # only allow if usersettings already exists (setup has been done) + $userSettings = self::getUserSettings(); + + if($userSettings) + { + # whitelist settings that can be stored in usersettings (values are not relevant here, only keys) + $allowedUserSettings = ['displayErrorDetails' => false, + 'title' => false, + 'copyright' => false, + 'language' => false, + 'startpage' => false, + 'author' => false, + 'year' => false, + 'theme' => false, + 'editor' => false, + 'setup' => false, + 'welcome' => false, + 'images' => false, + 'plugins' => false, + 'themes' => false, + 'latestVersion' => false + ]; + + # cleanup the existing usersettings + $userSettings = array_intersect_key($userSettings, $allowedUserSettings); + + # cleanup the new settings passed as an argument + $settings = array_intersect_key($settings, $allowedUserSettings); + + # merge usersettings with new settings + $settings = array_merge($userSettings, $settings); + + /* write settings to yaml */ + $yaml = new Models\WriteYaml(); + $yaml->updateYaml('settings', 'settings.yaml', $settings); + } + } } \ No newline at end of file diff --git a/system/author/auth/login.twig b/system/author/auth/login.twig index 50a4797..517b9fb 100644 --- a/system/author/auth/login.twig +++ b/system/author/auth/login.twig @@ -1,47 +1,47 @@ -{% extends 'layouts/layoutAuth.twig' %} -{% block title %}Login{% endblock %} - -{% block content %} - -
- -
-

Remember to bookmark this page

-
-
-
- -
-
- - - {% if errors.signup_username %} - {{ errors.username | first }} - {% endif %} -
-
- - - {% if errors.password %} - {{ errors.password | first }} - {% endif %} -
-
- -
- - {{ csrf_field() | raw }} - - {% if messages.time %} -
wait {{ messages.time }} sec
- - {% endif %} -
-
-
- -
-
+{% extends 'layouts/layoutAuth.twig' %} +{% block title %}Login{% endblock %} + +{% block content %} + +
+ +
+

Remember to bookmark this page

+
+
+
+ +
+
+ + + {% if errors.signup_username %} + {{ errors.username | first }} + {% endif %} +
+
+ + + {% if errors.password %} + {{ errors.password | first }} + {% endif %} +
+
+ +
+ + {{ csrf_field() | raw }} + + {% if messages.time %} +
wait {{ messages.time }} sec
+ + {% endif %} +
+
+
+ +
+
{% endblock %} \ No newline at end of file diff --git a/system/author/auth/setup.twig b/system/author/auth/setup.twig index 01546a6..405ff67 100644 --- a/system/author/auth/setup.twig +++ b/system/author/auth/setup.twig @@ -1,41 +1,41 @@ -{% extends 'layouts/layoutAuth.twig' %} -{% block title %}Setup{% endblock %} - -{% block content %} - -
-
-
- -
-
- - - {% if errors.username %} - {{ errors.username | first }} - {% endif %} -
-
- - - {% if errors.email %} - {{ errors.email | first }} - {% endif %} -
-
- - - {% if errors.password %} - {{ errors.password | first }} - {% endif %} -
-
- - - {{ csrf_field() | raw }} - -
-
-
- +{% extends 'layouts/layoutAuth.twig' %} +{% block title %}Setup{% endblock %} + +{% block content %} + +
+
+
+ +
+
+ + + {% if errors.username %} + {{ errors.username | first }} + {% endif %} +
+
+ + + {% if errors.email %} + {{ errors.email | first }} + {% endif %} +
+
+ + + {% if errors.password %} + {{ errors.password | first }} + {% endif %} +
+
+ + + {{ csrf_field() | raw }} + +
+
+
+ {% endblock %} \ No newline at end of file diff --git a/system/author/auth/welcome.twig b/system/author/auth/welcome.twig index 18612ad..3939812 100644 --- a/system/author/auth/welcome.twig +++ b/system/author/auth/welcome.twig @@ -1,49 +1,49 @@ -{% extends 'layouts/layoutAuth.twig' %} - -{% block title %}Setup Welcome{% endblock %} - -{% block content %} - -
- -
-
-

Hurra!

-

Your account has been created and you are logged in now.

-

Next step: Visit the author panel and setup your new website. You can configure the system, choose themes and add plugins.

-

New: Write beautiful math formulas with markdown with direct preview in the visual editor. We completely refactored it!!

-

Get help: If you have any questions, please consult the docs or open a new issue on github.

-
- Configure your website -
- - - -
- +{% extends 'layouts/layoutAuth.twig' %} + +{% block title %}Setup Welcome{% endblock %} + +{% block content %} + +
+ +
+
+

Hurra!

+

Your account has been created and you are logged in now.

+

Next step: Visit the author panel and setup your new website. You can configure the system, choose themes and add plugins.

+

New: Write beautiful math formulas with markdown with direct preview in the visual editor. We completely refactored it!!

+

Get help: If you have any questions, please consult the docs or open a new issue on github.

+
+ Configure your website +
+ + + +
+ {% endblock %} \ No newline at end of file diff --git a/system/author/css/fontello/config.json b/system/author/css/fontello/config.json index 6dff708..7bb2797 100644 --- a/system/author/css/fontello/config.json +++ b/system/author/css/fontello/config.json @@ -1,202 +1,202 @@ -{ - "name": "", - "css_prefix_text": "icon-", - "css_use_suffix": false, - "hinting": true, - "units_per_em": 1000, - "ascent": 850, - "glyphs": [ - { - "uid": "381da2c2f7fd51f8de877c044d7f439d", - "css": "picture", - "code": 59392, - "src": "fontawesome" - }, - { - "uid": "5211af474d3a9848f67f945e2ccaf143", - "css": "cancel", - "code": 59393, - "src": "fontawesome" - }, - { - "uid": "44e04715aecbca7f266a17d5a7863c68", - "css": "plus", - "code": 59394, - "src": "fontawesome" - }, - { - "uid": "d7271d490b71df4311e32cdacae8b331", - "css": "home", - "code": 59395, - "src": "fontawesome" - }, - { - "uid": "0ddd3e8201ccc7d41f7b7c9d27eca6c1", - "css": "link", - "code": 59396, - "src": "fontawesome" - }, - { - "uid": "e99461abfef3923546da8d745372c995", - "css": "cog", - "code": 59397, - "src": "fontawesome" - }, - { - "uid": "a8cb1c217f02b073db3670c061cc54d2", - "css": "italic", - "code": 59398, - "src": "fontawesome" - }, - { - "uid": "02cca871bb69da75e8ee286b7055832c", - "css": "bold", - "code": 59399, - "src": "fontawesome" - }, - { - "uid": "8b9e6a8dd8f67f7c003ed8e7e5ee0857", - "css": "off", - "code": 59400, - "src": "fontawesome" - }, - { - "uid": "872d9516df93eb6b776cc4d94bd97dac", - "css": "video", - "code": 59401, - "src": "fontawesome" - }, - { - "uid": "47a1f80457068fbeab69fdb83d7d0817", - "css": "youtube-play", - "code": 61802, - "src": "fontawesome" - }, - { - "uid": "c5fd68d8253e605e7a78a0c75255b692", - "css": "math", - "code": 61466, - "src": "mfglabs" - }, - { - "uid": "6605ee6441bf499ffa3c63d3c7409471", - "css": "move", - "code": 61511, - "src": "fontawesome" - }, - { - "uid": "e15f0d620a7897e2035c18c80142f6d9", - "css": "link-ext", - "code": 61582, - "src": "fontawesome" - }, - { - "uid": "a2a74f5e7b7d9ba054897d8c795a326a", - "css": "list-bullet", - "code": 61642, - "src": "fontawesome" - }, - { - "uid": "f6766a8b042c2453a4e153af03294383", - "css": "list-numbered", - "code": 61643, - "src": "fontawesome" - }, - { - "uid": "d4a4a38a40b728f46dad1de4ac950231", - "css": "underline", - "code": 61645, - "src": "fontawesome" - }, - { - "uid": "8fb55fd696d9a0f58f3b27c1d8633750", - "css": "table", - "code": 61646, - "src": "fontawesome" - }, - { - "uid": "5408be43f7c42bccee419c6be53fdef5", - "css": "doc-text", - "code": 61686, - "src": "fontawesome" - }, - { - "uid": "ab95e1351ebaec5850101097cbf7097f", - "css": "quote-left", - "code": 61709, - "src": "fontawesome" - }, - { - "uid": "b091a8bd0fdade174951f17d936f51e4", - "css": "folder-empty", - "code": 61716, - "src": "fontawesome" - }, - { - "uid": "7034e4d22866af82bef811f52fb1ba46", - "css": "code", - "code": 61729, - "src": "fontawesome" - }, - { - "uid": "4e88371fb8857dacc1f66afe6314e426", - "css": "superscript", - "code": 61739, - "src": "fontawesome" - }, - { - "uid": "0c708edd8fae2376b3370aa56d40cf9e", - "css": "header", - "code": 61916, - "src": "fontawesome" - }, - { - "uid": "c5845105a87df2ee1999826d90622f6a", - "css": "paragraph", - "code": 61917, - "src": "fontawesome" - }, - { - "uid": "eeec3208c90b7b48e804919d0d2d4a41", - "css": "upload", - "code": 59402, - "src": "fontawesome" - }, - { - "uid": "861ab06e455e2de3232ebef67d60d708", - "css": "minus", - "code": 59403, - "src": "fontawesome" - }, - { - "uid": "2qh229aneb95ds2afi7dbdsxz9jrbhcl", - "css": "colon", - "code": 59404, - "src": "modernpics" - }, - { - "uid": "c819c6225685bae2eed1b8da13e629fa", - "css": "list-alt", - "code": 59406, - "src": "fontawesome" - }, - { - "uid": "d2d6ab0dd4fb9365b1d5756380484bbb", - "css": "pi", - "code": 59405, - "src": "typicons" - }, - { - "uid": "d3b3f17bc3eb7cd809a07bbd4d178bee", - "css": "resize-vertical", - "code": 59407, - "src": "fontawesome" - }, - { - "uid": "2f5ef6f6b7aaebc56458ab4e865beff5", - "css": "bookmark-empty", - "code": 61591, - "src": "fontawesome" - } - ] +{ + "name": "", + "css_prefix_text": "icon-", + "css_use_suffix": false, + "hinting": true, + "units_per_em": 1000, + "ascent": 850, + "glyphs": [ + { + "uid": "381da2c2f7fd51f8de877c044d7f439d", + "css": "picture", + "code": 59392, + "src": "fontawesome" + }, + { + "uid": "5211af474d3a9848f67f945e2ccaf143", + "css": "cancel", + "code": 59393, + "src": "fontawesome" + }, + { + "uid": "44e04715aecbca7f266a17d5a7863c68", + "css": "plus", + "code": 59394, + "src": "fontawesome" + }, + { + "uid": "d7271d490b71df4311e32cdacae8b331", + "css": "home", + "code": 59395, + "src": "fontawesome" + }, + { + "uid": "0ddd3e8201ccc7d41f7b7c9d27eca6c1", + "css": "link", + "code": 59396, + "src": "fontawesome" + }, + { + "uid": "e99461abfef3923546da8d745372c995", + "css": "cog", + "code": 59397, + "src": "fontawesome" + }, + { + "uid": "a8cb1c217f02b073db3670c061cc54d2", + "css": "italic", + "code": 59398, + "src": "fontawesome" + }, + { + "uid": "02cca871bb69da75e8ee286b7055832c", + "css": "bold", + "code": 59399, + "src": "fontawesome" + }, + { + "uid": "8b9e6a8dd8f67f7c003ed8e7e5ee0857", + "css": "off", + "code": 59400, + "src": "fontawesome" + }, + { + "uid": "872d9516df93eb6b776cc4d94bd97dac", + "css": "video", + "code": 59401, + "src": "fontawesome" + }, + { + "uid": "47a1f80457068fbeab69fdb83d7d0817", + "css": "youtube-play", + "code": 61802, + "src": "fontawesome" + }, + { + "uid": "c5fd68d8253e605e7a78a0c75255b692", + "css": "math", + "code": 61466, + "src": "mfglabs" + }, + { + "uid": "6605ee6441bf499ffa3c63d3c7409471", + "css": "move", + "code": 61511, + "src": "fontawesome" + }, + { + "uid": "e15f0d620a7897e2035c18c80142f6d9", + "css": "link-ext", + "code": 61582, + "src": "fontawesome" + }, + { + "uid": "a2a74f5e7b7d9ba054897d8c795a326a", + "css": "list-bullet", + "code": 61642, + "src": "fontawesome" + }, + { + "uid": "f6766a8b042c2453a4e153af03294383", + "css": "list-numbered", + "code": 61643, + "src": "fontawesome" + }, + { + "uid": "d4a4a38a40b728f46dad1de4ac950231", + "css": "underline", + "code": 61645, + "src": "fontawesome" + }, + { + "uid": "8fb55fd696d9a0f58f3b27c1d8633750", + "css": "table", + "code": 61646, + "src": "fontawesome" + }, + { + "uid": "5408be43f7c42bccee419c6be53fdef5", + "css": "doc-text", + "code": 61686, + "src": "fontawesome" + }, + { + "uid": "ab95e1351ebaec5850101097cbf7097f", + "css": "quote-left", + "code": 61709, + "src": "fontawesome" + }, + { + "uid": "b091a8bd0fdade174951f17d936f51e4", + "css": "folder-empty", + "code": 61716, + "src": "fontawesome" + }, + { + "uid": "7034e4d22866af82bef811f52fb1ba46", + "css": "code", + "code": 61729, + "src": "fontawesome" + }, + { + "uid": "4e88371fb8857dacc1f66afe6314e426", + "css": "superscript", + "code": 61739, + "src": "fontawesome" + }, + { + "uid": "0c708edd8fae2376b3370aa56d40cf9e", + "css": "header", + "code": 61916, + "src": "fontawesome" + }, + { + "uid": "c5845105a87df2ee1999826d90622f6a", + "css": "paragraph", + "code": 61917, + "src": "fontawesome" + }, + { + "uid": "eeec3208c90b7b48e804919d0d2d4a41", + "css": "upload", + "code": 59402, + "src": "fontawesome" + }, + { + "uid": "861ab06e455e2de3232ebef67d60d708", + "css": "minus", + "code": 59403, + "src": "fontawesome" + }, + { + "uid": "2qh229aneb95ds2afi7dbdsxz9jrbhcl", + "css": "colon", + "code": 59404, + "src": "modernpics" + }, + { + "uid": "c819c6225685bae2eed1b8da13e629fa", + "css": "list-alt", + "code": 59406, + "src": "fontawesome" + }, + { + "uid": "d2d6ab0dd4fb9365b1d5756380484bbb", + "css": "pi", + "code": 59405, + "src": "typicons" + }, + { + "uid": "d3b3f17bc3eb7cd809a07bbd4d178bee", + "css": "resize-vertical", + "code": 59407, + "src": "fontawesome" + }, + { + "uid": "2f5ef6f6b7aaebc56458ab4e865beff5", + "css": "bookmark-empty", + "code": 61591, + "src": "fontawesome" + } + ] } \ No newline at end of file diff --git a/system/author/css/fontello/css/fontello-codes.css b/system/author/css/fontello/css/fontello-codes.css index e590212..28361c8 100644 --- a/system/author/css/fontello/css/fontello-codes.css +++ b/system/author/css/fontello/css/fontello-codes.css @@ -1,33 +1,33 @@ - -.icon-picture:before { content: '\e800'; } /* '' */ -.icon-cancel:before { content: '\e801'; } /* '' */ -.icon-plus:before { content: '\e802'; } /* '' */ -.icon-home:before { content: '\e803'; } /* '' */ -.icon-link:before { content: '\e804'; } /* '' */ -.icon-cog:before { content: '\e805'; } /* '' */ -.icon-italic:before { content: '\e806'; } /* '' */ -.icon-bold:before { content: '\e807'; } /* '' */ -.icon-off:before { content: '\e808'; } /* '' */ -.icon-video:before { content: '\e809'; } /* '' */ -.icon-upload:before { content: '\e80a'; } /* '' */ -.icon-minus:before { content: '\e80b'; } /* '' */ -.icon-colon:before { content: '\e80c'; } /* '' */ -.icon-pi:before { content: '\e80d'; } /* '' */ -.icon-list-alt:before { content: '\e80e'; } /* '' */ -.icon-resize-vertical:before { content: '\e80f'; } /* '' */ -.icon-math:before { content: '\f01a'; } /* '' */ -.icon-move:before { content: '\f047'; } /* '' */ -.icon-link-ext:before { content: '\f08e'; } /* '' */ -.icon-bookmark-empty:before { content: '\f097'; } /* '' */ -.icon-list-bullet:before { content: '\f0ca'; } /* '' */ -.icon-list-numbered:before { content: '\f0cb'; } /* '' */ -.icon-underline:before { content: '\f0cd'; } /* '' */ -.icon-table:before { content: '\f0ce'; } /* '' */ -.icon-doc-text:before { content: '\f0f6'; } /* '' */ -.icon-quote-left:before { content: '\f10d'; } /* '' */ -.icon-folder-empty:before { content: '\f114'; } /* '' */ -.icon-code:before { content: '\f121'; } /* '' */ -.icon-superscript:before { content: '\f12b'; } /* '' */ -.icon-youtube-play:before { content: '\f16a'; } /* '' */ -.icon-header:before { content: '\f1dc'; } /* '' */ + +.icon-picture:before { content: '\e800'; } /* '' */ +.icon-cancel:before { content: '\e801'; } /* '' */ +.icon-plus:before { content: '\e802'; } /* '' */ +.icon-home:before { content: '\e803'; } /* '' */ +.icon-link:before { content: '\e804'; } /* '' */ +.icon-cog:before { content: '\e805'; } /* '' */ +.icon-italic:before { content: '\e806'; } /* '' */ +.icon-bold:before { content: '\e807'; } /* '' */ +.icon-off:before { content: '\e808'; } /* '' */ +.icon-video:before { content: '\e809'; } /* '' */ +.icon-upload:before { content: '\e80a'; } /* '' */ +.icon-minus:before { content: '\e80b'; } /* '' */ +.icon-colon:before { content: '\e80c'; } /* '' */ +.icon-pi:before { content: '\e80d'; } /* '' */ +.icon-list-alt:before { content: '\e80e'; } /* '' */ +.icon-resize-vertical:before { content: '\e80f'; } /* '' */ +.icon-math:before { content: '\f01a'; } /* '' */ +.icon-move:before { content: '\f047'; } /* '' */ +.icon-link-ext:before { content: '\f08e'; } /* '' */ +.icon-bookmark-empty:before { content: '\f097'; } /* '' */ +.icon-list-bullet:before { content: '\f0ca'; } /* '' */ +.icon-list-numbered:before { content: '\f0cb'; } /* '' */ +.icon-underline:before { content: '\f0cd'; } /* '' */ +.icon-table:before { content: '\f0ce'; } /* '' */ +.icon-doc-text:before { content: '\f0f6'; } /* '' */ +.icon-quote-left:before { content: '\f10d'; } /* '' */ +.icon-folder-empty:before { content: '\f114'; } /* '' */ +.icon-code:before { content: '\f121'; } /* '' */ +.icon-superscript:before { content: '\f12b'; } /* '' */ +.icon-youtube-play:before { content: '\f16a'; } /* '' */ +.icon-header:before { content: '\f1dc'; } /* '' */ .icon-paragraph:before { content: '\f1dd'; } /* '' */ \ No newline at end of file diff --git a/system/author/css/fontello/css/fontello-embedded.css b/system/author/css/fontello/css/fontello-embedded.css index 8881f9e..10996c8 100644 --- a/system/author/css/fontello/css/fontello-embedded.css +++ b/system/author/css/fontello/css/fontello-embedded.css @@ -1,86 +1,86 @@ -@font-face { - font-family: 'fontello'; - src: url('../font/fontello.eot?71992743'); - src: url('../font/fontello.eot?71992743#iefix') format('embedded-opentype'), - url('../font/fontello.svg?71992743#fontello') format('svg'); - font-weight: normal; - font-style: normal; -} -@font-face { - font-family: 'fontello'; - src: url('data:application/octet-stream;base64,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') format('woff'), - url('data:application/octet-stream;base64,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UlggPFkusSsBFCstsDwsIyAuRrACJUZQWCA8WS6xKwEUKy2wPSwjIC5GsAIlRlJYIDxZIyAuRrACJUZQWCA8WS6xKwEUKy2wPiywNSsjIC5GsAIlRlJYIDxZLrErARQrLbA/LLA2K4ogIDywBCNCijgjIC5GsAIlRlJYIDxZLrErARQrsARDLrArKy2wQCywABawBCWwBCYgLkcjRyNhsAlDKyMgPCAuIzixKwEUKy2wQSyxCAQlQrAAFrAEJbAEJSAuRyNHI2EgsAQjQrAJQysgsGBQWCCwQFFYswIgAyAbswImAxpZQkIjIEewBEOwAmIgsABQWLBAYFlmsAFjYCCwASsgiophILACQ2BkI7ADQ2FkUFiwAkNhG7ADQ2BZsAMlsAJiILAAUFiwQGBZZrABY2GwAiVGYTgjIDwjOBshICBGI0ewASsjYTghWbErARQrLbBCLLA1Ky6xKwEUKy2wQyywNishIyAgPLAEI0IjOLErARQrsARDLrArKy2wRCywABUgR7AAI0KyAAEBFRQTLrAxKi2wRSywABUgR7AAI0KyAAEBFRQTLrAxKi2wRiyxAAEUE7AyKi2wRyywNCotsEgssAAWRSMgLiBGiiNhOLErARQrLbBJLLAII0KwSCstsEossgAAQSstsEsssgABQSstsEwssgEAQSstsE0ssgEBQSstsE4ssgAAQistsE8ssgABQistsFAssgEAQistsFEssgEBQistsFIssgAAPistsFMssgABPistsFQssgEAPistsFUssgEBPistsFYssgAAQCstsFcssgABQCstsFgssgEAQCstsFkssgEBQCstsFossgAAQystsFsssgABQystsFwssgEAQystsF0ssgEBQystsF4ssgAAPystsF8ssgABPystsGAssgEAPystsGEssgEBPystsGIssDcrLrErARQrLbBjLLA3K7A7Ky2wZCywNyuwPCstsGUssAAWsDcrsD0rLbBmLLA4Ky6xKwEUKy2wZyywOCuwOystsGgssDgrsDwrLbBpLLA4K7A9Ky2waiywOSsusSsBFCstsGsssDkrsDsrLbBsLLA5K7A8Ky2wbSywOSuwPSstsG4ssDorLrErARQrLbBvLLA6K7A7Ky2wcCywOiuwPCstsHEssDorsD0rLbByLLMJBAIDRVghGyMhWUIrsAhlsAMkUHiwARUwLQBLuADIUlixAQGOWbABuQgACABjcLEABUKyAAEAKrEABUKzCgIBCCqxAAVCsw4AAQgqsQAGQroCwAABAAkqsQAHQroAQAABAAkqsQMARLEkAYhRWLBAiFixA2REsSYBiFFYugiAAAEEQIhjVFixAwBEWVlZWbMMAgEMKrgB/4WwBI2xAgBEAAA=') format('truetype'); -} -/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ -/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ -/* -@media screen and (-webkit-min-device-pixel-ratio:0) { - @font-face { - font-family: 'fontello'; - src: url('../font/fontello.svg?71992743#fontello') format('svg'); - } -} -*/ - - [class^="icon-"]:before, [class*=" icon-"]:before { - font-family: "fontello"; - font-style: normal; - font-weight: normal; - speak: none; - - display: inline-block; - text-decoration: inherit; - width: 1em; - margin-right: .2em; - text-align: center; - /* opacity: .8; */ - - /* For safety - reset parent styles, that can break glyph codes*/ - font-variant: normal; - text-transform: none; - - /* fix buttons height, for twitter bootstrap */ - line-height: 1em; - - /* Animation center compensation - margins should be symmetric */ - /* remove if not needed */ - margin-left: .2em; - - /* you can be more comfortable with increased icons size */ - /* font-size: 120%; */ - - /* Uncomment for 3D effect */ - /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ -} -.icon-picture:before { content: '\e800'; } /* '' */ -.icon-cancel:before { content: '\e801'; } /* '' */ -.icon-plus:before { content: '\e802'; } /* '' */ -.icon-home:before { content: '\e803'; } /* '' */ -.icon-link:before { content: '\e804'; } /* '' */ -.icon-cog:before { content: '\e805'; } /* '' */ -.icon-italic:before { content: '\e806'; } /* '' */ -.icon-bold:before { content: '\e807'; } /* '' */ -.icon-off:before { content: '\e808'; } /* '' */ -.icon-video:before { content: '\e809'; } /* '' */ -.icon-upload:before { content: '\e80a'; } /* '' */ -.icon-minus:before { content: '\e80b'; } /* '' */ -.icon-colon:before { content: '\e80c'; } /* '' */ -.icon-pi:before { content: '\e80d'; } /* '' */ -.icon-list-alt:before { content: '\e80e'; } /* '' */ -.icon-resize-vertical:before { content: '\e80f'; } /* '' */ -.icon-math:before { content: '\f01a'; } /* '' */ -.icon-move:before { content: '\f047'; } /* '' */ -.icon-link-ext:before { content: '\f08e'; } /* '' */ -.icon-bookmark-empty:before { content: '\f097'; } /* '' */ -.icon-list-bullet:before { content: '\f0ca'; } /* '' */ -.icon-list-numbered:before { content: '\f0cb'; } /* '' */ -.icon-underline:before { content: '\f0cd'; } /* '' */ -.icon-table:before { content: '\f0ce'; } /* '' */ -.icon-doc-text:before { content: '\f0f6'; } /* '' */ -.icon-quote-left:before { content: '\f10d'; } /* '' */ -.icon-folder-empty:before { content: '\f114'; } /* '' */ -.icon-code:before { content: '\f121'; } /* '' */ -.icon-superscript:before { content: '\f12b'; } /* '' */ -.icon-youtube-play:before { content: '\f16a'; } /* '' */ -.icon-header:before { content: '\f1dc'; } /* '' */ +@font-face { + font-family: 'fontello'; + src: url('../font/fontello.eot?71992743'); + src: url('../font/fontello.eot?71992743#iefix') format('embedded-opentype'), + url('../font/fontello.svg?71992743#fontello') format('svg'); + font-weight: normal; + font-style: normal; +} +@font-face { + font-family: 'fontello'; + src: url('data:application/octet-stream;base64,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') format('woff'), + url('data:application/octet-stream;base64,AAEAAAAPAIAAAwBwR1NVQiCLJXoAAAD8AAAAVE9TLzI+IFLLAAABUAAAAFZjbWFwUx0kigAAAagAAAOKY3Z0IAbV/wQAADA4AAAAIGZwZ22KkZBZAAAwWAAAC3BnYXNwAAAAEAAAMDAAAAAIZ2x5Zu+A668AAAU0AAAlemhlYWQVRoZjAAAqsAAAADZoaGVhB4EDuQAAKugAAAAkaG10eG9U//cAACsMAAAAhGxvY2GMupYjAAArkAAAAERtYXhwAaYMYAAAK9QAAAAgbmFtZcydHyEAACv0AAACzXBvc3QgFT6NAAAuxAAAAWpwcmVw5UErvAAAO8gAAACGAAEAAAAKADAAPgACREZMVAAObGF0bgAaAAQAAAAAAAAAAQAAAAQAAAAAAAAAAQAAAAFsaWdhAAgAAAABAAAAAQAEAAQAAAABAAgAAQAGAAAAAQAAAAEDXwGQAAUAAAJ6ArwAAACMAnoCvAAAAeAAMQECAAACAAUDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFBmRWQAQOgA8d0DUv9qAFoDUgCWAAAAAQAAAAAAAAAAAAUAAAADAAAALAAAAAQAAAH6AAEAAAAAAPQAAwABAAAALAADAAoAAAH6AAQAyAAAAB4AEAADAA7oD/Aa8EfwjvCX8MvwzvD28Q3xFPEh8SvxavHd//8AAOgA8BrwR/CO8JfwyvDN8PbxDfEU8SHxK/Fq8dz//wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAeADwAPAA8ADwAPAA+AEAAQABAAEAAQABAAEAAAAABAAIAAwAEAAUABgAHAAgACQAKAAsADAANAA4ADwAQABEAEgATABQAFQAWABcAGAAZABoAGwAcAB0AHgAfACAAAAEGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAZAAAAAAAAAAIAAA6AAAAOgAAAAAAQAA6AEAAOgBAAAAAgAA6AIAAOgCAAAAAwAA6AMAAOgDAAAABAAA6AQAAOgEAAAABQAA6AUAAOgFAAAABgAA6AYAAOgGAAAABwAA6AcAAOgHAAAACAAA6AgAAOgIAAAACQAA6AkAAOgJAAAACgAA6AoAAOgKAAAACwAA6AsAAOgLAAAADAAA6AwAAOgMAAAADQAA6A0AAOgNAAAADgAA6A4AAOgOAAAADwAA6A8AAOgPAAAAEAAA8BoAAPAaAAAAEQAA8EcAAPBHAAAAEgAA8I4AAPCOAAAAEwAA8JcAAPCXAAAAFAAA8MoAAPDKAAAAFQAA8MsAAPDLAAAAFgAA8M0AAPDNAAAAFwAA8M4AAPDOAAAAGAAA8PYAAPD2AAAAGQAA8Q0AAPENAAAAGgAA8RQAAPEUAAAAGwAA8SEAAPEhAAAAHAAA8SsAAPErAAAAHQAA8WoAAPFqAAAAHgAA8dwAAPHcAAAAHwAA8d0AAPHdAAAAIAAAAAT///+xBC8DCwAIAA8AHwAvAFVAUh0UAgEDDwEAAQ4NDAkEAgAcFQIEAgRHAAIABAACBG0ABgcBAwEGA2AAAQAAAgEAYAAEBQUEVAAEBAVYAAUEBUwREC4rJiMZFxAfER8TExIIBRcrARQOASY0Nh4BARUhNTcXASUhIgYHERQWNyEyNicRNCYXERQGByEiJjcRNDY3ITIWAWU+Wj4+Wj4CPPzusloBHQEe/IMHCgEMBgN9BwwBClE0JfyDJDYBNCUDfSU0AhEtPgJCVkIEOv76+muzWQEdoQoI/VoHDAEKCAKmCAoS/VolNAE2JAKmJTQBNgABAAD/7wLUAoYAJAAeQBsiGRAHBAACAUcDAQIAAm8BAQAAZhQcFBQEBRgrJRQPAQYiLwEHBiIvASY0PwEnJjQ/ATYyHwE3NjIfARYUDwEXFgLUD0wQLBCkpBAsEEwQEKSkEBBMECwQpKQQLBBMDw+kpA9wFhBMDw+lpQ8PTBAsEKSkECwQTBAQpKQQEEwPLg+kpA8AAQAA//kDEgMLACMAKUAmAAQDBG8AAQABcAUBAwAAA1QFAQMDAFgCAQADAEwjMyUjMyMGBRorARUUBicjFRQGByMiJjc1IyImJzU0NjczNTQ2OwEyFhcVMzIWAxIgFuggFmsWIAHoFx4BIBboHhdrFx4B6BceAbdrFiAB6RYeASAV6R4XaxceAegWICAW6CAAAgAA//kDkgLFABAAMQAuQCsuJiUYFQ8ODQgBAwwBAAECRwQBAwEDbwABAAFvAgEAAGYqKCMiIREUBQUXKwERFAYHIzUjFSMiJicRCQEWNwcGByMiJwkBBiYvASY2NwE2Mh8BNTQ2OwEyFh0BFxYUAxIWDtaP1g8UAQFBAUEBfCIFBwIHBf5+/n4HDQUjBAIFAZESMBOICghrCAp6BgEo/vUPFAHW1hYOAQ8BCP74ASQpBQEDAUL+vgQCBSkGDgUBTg8PcWwICgoI42YEEAAAAAMAAP+6A5gDSQAcADsAXACaQBo6AQkFV0cCAAQTCwIBBwNHVisCCUYGAgcCRkuwClBYQDAABQMJBAVlAAEHAgABZQAJAAAHCQBgAAQABwEEB2EAAgAGAgZcAAMDCFgACAgMA0kbQDIABQMJAwUJbQABBwIHAQJtAAkAAAcJAGAABAAHAQQHYQACAAYCBlwAAwMIWAAICAwDSVlADllYFxccKBcYGhgUCgUdKyU0LwEmIgcXHgEfARQGByIuAS8BBhQfARYyPwE2ATQvASYiDwEGFB8BFjI3Jy4CNTQ2FzIWHwEWHwE2ARQPAQYiLwEmNDcnBiIvASY0PwE2Mh8BFhQHFzYyHwEWAy0QdBAuEBYDDAECIBYIDg4EFhMQcw8tEFIQ/ncPcxAsEFIQEHQPLhEXAwoEHhcJDgcLBAgKEgH0MFIuhy5zLjExMIcvdC8vUi+GL3MuMTEwhy90L6sXD3QQEhYDEAYPFx4BBAoEFhEuD3QPD1EQAZ8WEHMQD1IPLBB0DxEXAw4OCRYgAQQFCAMJCxH+jkIvUS8wcy+HMDExL3Qvhi5SLi90LogwMTEvdC8AAAACAAD/sQNaAwsACABqAEVAQmVZTEEEAAQ7CgIBADQoGxAEAwEDRwAFBAVvBgEEAARvAAABAG8AAQMBbwADAgNvAAICZlxbU1FJSCsqIiATEgcFFisBNCYiDgEWMjYlFRQGDwEGBxYXFhQHDgEnIi8BBgcGBwYrASImNScmJwcGIicmJyY0Nz4BNyYvAS4BJzU0Nj8BNjcmJyY0Nz4BMzIfATY3Njc2OwEyFh8BFhc3NjIXFhcWFAcOAQcWHwEeAQI7UnhSAlZ0VgEcCAdoCgsTKAYFD1ANBwdNGRoJBwQQfAgMEBsXTwYQBkYWBAUIKAoPCGYHCAEKBWgIDhclBgUPUA0HCE0YGgkIAxF8BwwBDxwXTwUPB0gUBAQJKAoPCGYHCgFeO1RUdlRUeHwHDAEQHhUbMgYOBhVQAQU8DQhMHBAKB2cJDDwFBkAeBQ4GDDIPHBsPAQwHfAcMARAZGiAtBwwHFFAFPA0ITBwQCgdnCQs7BQVDHAUOBgwyDxwaEAEMAAAAAQAA/7ECOwMLADoAOEA1EAEAAS4rDAMDAAJHGQEBRQADAAIAAwJtAAICbgABAAABVAABAQBYAAABAEw5NTQwYh4EBRYrFTc+Ajc2PwE2Ej0BLgInNxceATMyNj8BBgcOAQcGDwEOAQcGAg8CBhUXFhcGByIGIyImIyYjIgcKDCwkDxAHIyI6DSIsCgpDMEgfGzgoNgIIEVAUBQMFAgQCD0QJEgkEAQleAgcGGAYQQg9NJhwzTjAECgwHEyWingEiFA4IBgICOgQDAgIDBBYcBhQJCg0XCh4JUv7QLlMuFgoKAw8YHwIMAQUAAAADAAD/sQMTAwsAFAAqAF8ATUBKKSMCAgNRAQECDgEAASwBBgAERwAFBAVvAAQAAwIEA2AAAgABAAIBYAAABgYAVAAAAAZYBwEGAAZMKysrXytZRkVEPygpNyEIBRgrJRYzMjU0Jy4EIyIHFRQHFRQWAxYzMj4CJzQuAiciBxQWBxUUBxQBNz4BNz4DJjc1ECcuBCMnNiQ3MhY3Mh4DFRQOAwceAQcUDgMHIiYHIgcBNikl0hcPJiY0KiAoEAEEAxcmLkQ2HgEgOj4mHC0GAQH+0wEJThQEBgIGBAIMAhQeGhwDAjcBDkkNMg0nSkYyIBIaLiQdVnQBKEBaXDQZYhk7cAESu0AlGCISCgIGWDsdXBU0AZYEDiRALyc6Ig4BBxxwHS0eDhr+AzUCDggHEBYOHAUkAiQYBQYGAgQuAQoBAgEOIixKJx0yHiIQDhRuUzhaNioMAgQBBgAAAAAC//3/sQNZA1IAKAA0ACJAHwACAwEDAgFtAAEAAAEAXAADAwwDSTMyLSwaGRQEBRUrARQOAiIuAjc0Njc2FhcWBgcOARUUHgIyPgI3NCYnLgE+ARceAQERFAYiJjcRNDYyFgNZRHKgrKJuSgNaURg8EBIIGDY8LkxqdGhQKgE8NhcKJDwXUVr+myo6LAEqPCgBXleedEREdJ5XZrI+EggYFzwRKXhDOmpMLi5MajpEdioSOjAIEj20AUj+mh0qKh0BZh0qKgAAAAv///9qBC8DCwAPAB8ALwA/AE8AXwBvAH8AjwCfAK8AeUB2kEACCQiIgGAgBAUEeDgCAwJQMAADAQAERwAVEgwCCAkVCGATAQkQAQQFCQRgEQ0CBQ4GAgIDBQJgDwEDCgEAAQMAYAsHAgEBFFgAFBQNFEmuq6ajnpuWlI6MhoR+fHZzbmtmZF5bVlROSzU1NSY1JjU1MxYFHSsXNTQmByMiBh0BFBY7ATI2JzU0JisBIgYdARQWNzMyNic1NCYnIyIGHQEUFhczMjYBETQmIyEiBhcRFBYzITI2ATU0JgcjIgYdARQWOwEyNgE1NCYHIyIGBxUUFjsBMjYDETQmByEiBhcRFBYXITI2FzU0JisBIgYHFRQWNzMyNjc1NCYnIyIGBxUUFhczMjY3NTQmByMiBgcVFBY7ATI2NxEUBiMhIiY3ETQ2NyEyFtYUD0gOFhYOSA4WARQPSA4WFg5IDhYBFA9IDhYWDkgOFgI7Fg7+Uw4WARQPAa0PFP3FFA9IDhYWDkgOFgMRFg5HDxQBFg5HDxTVFg7+Uw4WARQPAa0PFNcWDkcPFAEWDkcPFAEWDkcPFAEWDkcPFAEWDkcPFAEWDkcPFEg0JfyDJDYBNCUDfSU0K0gOFgEUD0gOFhbkSA4WFg5IDhYBFOZHDxQBFg5HDxQBFv5hAR4OFhYO/uIOFhYCkUcPFgEUEEcOFhb9i0gOFgEUD0gOFhYBuwEdDxYBFBD+4w8UARbJSA4WFg5IDhYBFOZHDxQBFg5HDxQBFuRHDxYBFBBHDhYWZ/0SJTQ0JQLuJTQBNgAAAAAEAAD/sQOhAy4ACAARACkAQABGQEM1AQcGCQACAgACRwAJBglvCAEGBwZvAAcDB28ABAACBFQFAQMBAQACAwBgAAQEAlgAAgQCTD08IzMjIjIlORgSCgUdKyU0Jg4CHgE2NzQmDgIeATY3FRQGIyEiJic1NDYXMx4BOwEyNjczMhYDBisBFRQGByMiJic1IyImPwE2Mh8BFgLKFB4UAhgaGI0UIBICFhwYRiAW/MsXHgEgFu4MNiOPIjYN7hYgtgkYjxQPjw8UAY8XExH6Ch4K+hIdDhYCEiASBBoMDhYCEiASBBqJsxYgIBazFiABHygoHx4BUhb6DxQBFg76LBH6Cgr6EQAAAAABAAAAAAMSAe0ADwAYQBUAAQAAAVQAAQEAWAAAAQBMNTMCBRYrARUUBichIiYnNTQ2NyEyFgMSIBb9WhceASAWAqYXHgG3axYgAR4XaxceASAAAAACAAAAAADDAm8ACAARACJAHwAAAAECAAFgAAIDAwJUAAICA1gAAwIDTBMUExIEBRgrETQ2MhYUBiImETQ2MhYUBiImOVE5OVE5OVE5OVE5Ag4oOTlROTn+ySk5OlE4OAABAAAAAALZAn0AJgAxQC4XEAIEBQwFAgACAkcAAwUDbwAFBAVvAAQCBG8AAgACbwEBAABmEhIUFhoYBgUaKwEWFAcGBxEUDgEmNREmJyYnERQOASY1EQYHBiImNDc2MhcWMjc2MgLKDw86Ux4sHlQ5HiYeKiAlHg8sHg9Q4lAyjDEPLAItDywPOhD+3BYeAiIUASQQOh4L/mkWHgIiFAGXCx4PHiwPUFAyMg8ACP////kD6AMLAA8AHwAvAD8ATwBfAG8AfwB0QHFoYQIFDFlRKSEEBAVJQRkRBAIDOTEJCAEFAAFpAQ0ABUcADwAMBQ8MYAsBBQoBBAMFBF4JAQMIAQIBAwJgBwEBBgEADQEAYAANDg4NVAANDQ5YAA4NDkx+e3ZzbWtlY11cVVRNTCYmJhcXFyYmIxAFHSs3FRQGKwEiJjc1NDY7ATIWNRUUBisBIiY3NTQ2FzMyFjUVFAYHIyImNzU0NhczMhYBFRQGIyEiJj0BNDYzITIWNRUUBiMhIiY9ATQ2FyEyFjUVFAYHISImPQE0NhchMhYTETQmIyEiBgcRFBYXITI2ExEUBiMhIiY3ETQ2NyEyFtYKCCMHDAEKCCMICgoIIwcMAQoIIwgKCggjBwwBCggjCAoCgwoI/ekICgoIAhcICgoI/ekICgoIAhcICgoI/ekICgoIAhcICkgMBvzKBwoBDAYDNgcKSDQl/MokNgE0JQM2JTS9IwgKCggjCAoKhyQHCgoHJAcMAQqHJAcKAQwGJAcMAQr+2iMICgoIIwgKCockBwoKByQHDAEKhyQHCgEMBiQHDAEK/m8B0AgKCgj+MAcKAQwCZf2hJTQ0JQJfJTQBNgAAAQAA/2oBigNSACAAKEAlBAEABQEFAAFtAwEBAgUBAmsABQUMSAACAg0CSRUhJRUhEwYFGisBFAYnIxEzMh4BDwEGIi8BJjQ2OwERIyImNj8BNjIfARYBiRYOR0cPFAIMjwoeCo8KFA9ISA4WAgmPCxwLjwsCnw4WAf3EFB4KjwsLjwoeFAI8FB4LjgsLjgsAAQAA/4kDqQMyABIAJ0AkDgUCA0QAAQACAAECYAAAAwMAVAAAAANYAAMAA0wiIyIiBAUYKxE0NhczFxMhMhYUBicjAwEjIiYwIdekWQEyIjAwIqqI/ql9ITAB2B8uAfkCByxALgH88AIELAAAAQAA/2oD6ANSAEQAUEBNCwEJCgcKCQdtDQEHCAoHCGsGAQABAgEAAm0EAQIDAQIDawwBCAUBAQAIAV4ACgoMSAADAw0DSUFAPTw7OTQzLiwTFxMRJRUhExQOBR0rARQPAQYiJj0BIxUzMhYUDwEGIi8BJjQ2OwE1IxUUBiIvASY0PwE2MhYdATM1IyImND8BNjIfARYUBisBFTM1NDYyHwEWA+gLjgseFNdIDhYLjwoeCo8LFg5I1xQeCo8LC48KHhTXSA4WC48LHAuPCxYOSNcUHguOCwFeDguPCxYOSNcUHgqPCwuPCh4U10gOFguPCxwLjwsWDkjXFB4LjgsLjgseFNdIDhYLjwoAAAIAAP/5A+gDUgAnAD8AREBBKAEBBhEBAgE3LgIEAiEBBQQERwAEAgUCBAVtAAUDAgUDawABAAIEAQJgAAMAAAMAXAAGBgwGSTobJTU2JTMHBRsrARUUBiMhIiY1ETQ2NyEyFh0BFAYjISIGBxEUFhchMjY9ATQ2OwEyFhMRFA4BLwEBBiIvASY0NwEnJjQ2MyEyFgMSXkP+MENeXkMBiQcKCgf+dyU0ATYkAdAlNAoIJAgK1hYcC2L+lAUQBEAGBgFsYgsWDgEdDxQBTLJDXl5DAdBCXgEKCCQICjQl/jAlNAE2JLIICgoB2v7jDxQCDGL+lAYGQAUOBgFsYgscFhYAAAAAAgAA/74CygMLAAUAIgAyQC8UBQMCBAIAAUcDAQIAAnAEAQEAAAFUBAEBAQBWAAABAEoHBhgWEhAGIgchEAUFFSsBIREBHwETMhceARcRFAYHBiMiLwEHBiMiJy4BNRE0Njc2MwKD/cQBHjLsBwwMExQBFhIKDhsU9vYUGg0MEhYWEgwNAsP9SwESL+MC/QUIHhT9MRMgBwQS7OwTBQcgEwLPEyAHBQAABgAA/9QD6QLnAAgAEQAhACoAOgBKAF9AXEQ8OwMKCzQsAggJGxMCBAUDRwALAAoGCwpeAAcABgMHBmAACQAIAgkIYAADAAIBAwJgAAEFAAFUAAUABAAFBF4AAQEAWAAAAQBMSEZAPzg2JRMVFxYTFBMSDAUdKzcUBi4BND4BFjUUBiImNDYyFgEVFAYnISImPQE0NjchMhYBFAYiJjQ2MhYBFRQGIyEiJj0BNDYzITIWAxUUBgchIiY9ATQ2MyEyFtY+Wj4+Wj4+Wj4+Wj4DEgoI/VoICgoIAqYHDPztPlo+Plo+AxIKCP1aCAoKCAKmBwwBCgj9WggKCggCpgcMQCxAAjxcPAJA8i0+Plo+Pv7rawcMAQoIawcKAQwCAC0+Plo+Pv7rbAcKCgdsBwoKARZrBwoBDAZrCAoKAAYAAP9qA+kDTQAfAD0ATQBdAG0AfQIDQDdaWVUDFA93bgIOFG8BDQ4wAQcIZy8qAwoSRxwCAwU/HQ4DCwQGAQECBQEAAQlHXwEKFxMCAwJGS7AMUFhAXhUBChIRCQplAAQDCwMEZQACCwEDAmUAFA4NFFQWEAIOEwENCA4NXgAIAAcSCAdgABIAEQkSEWAACQAGBQkGXwADBAUDVAwBBQALAgULXgAPDwxIAAEBAFgAAAANAEkbS7AlUFhAXxUBChIRCQplAAQDCwMEZQACCwELAgFtABQODRRUFhACDhMBDQgODV4ACAAHEggHYAASABEJEhFgAAkABgUJBl8AAwQFA1QMAQUACwIFC14ADw8MSAABAQBYAAAADQBJG0uwKlBYQGAVAQoSERIKEW0ABAMLAwRlAAILAQsCAW0AFA4NFFQWEAIOEwENCA4NXgAIAAcSCAdgABIAEQkSEWAACQAGBQkGXwADBAUDVAwBBQALAgULXgAPDwxIAAEBAFgAAAANAEkbQGEVAQoSERIKEW0ABAMLAwQLbQACCwELAgFtABQODRRUFhACDhMBDQgODV4ACAAHEggHYAASABEJEhFgAAkABgUJBl8AAwQFA1QMAQUACwIFC14ADw8MSAABAQBYAAAADQBJWVlZQCxOTiAge3lzcmtpY2FOXU5dXFtSUVBPS0lDQiA9ID08OyQbFhESGBMjIhcFHSsXFAYHIic3FjMyNjU0Byc2PwE2NzUiBicVIzUzFQceARMVIyY1ND4DNzQmByIHJz4BMzIWFRQOAgczNQUVFAYnISImPQE0NjMhMhYBFSM1MzU0NzUjBgcnNzMVBRUUBiMhIiY9ATQ2MyEyFgMVFAYHISImPQE0NjMhMhbVPiw8JB8cIBAYOw4EDhgKCgkkCTu6NRwiAcoEHCIoFgMSDRkULw02ICg4Ji4mAUcDTQoI/VoICgoIAqYHDPztuzwBAQUXKEw7A04KCP1aCAoKCAKmBwwBCgj9WggKCggCpgcMNi0yASUxGRAQIwQfBhIfDQgBAgEeVTFBBioBQlkUCh0uHhgYDQ4QASAhHCAuKBwuGh4PIrJrBwwBCghrCAoMAfA4OEMtFwcKFCpH4dhsBwoKB2wHCgoBFmsHCgEMBmsICgoAAgAA/7EDWQMLAFwAbAFaS7AJUFhAGTQQAgUBEQEABS4tAgQAZl4CCgkERzkBAUUbS7AKUFhAGTQQAgUCEQEABS4tAgQAZl4CCgkERzkBAUUbQBk0EAIFAREBAAUuLQIEAGZeAgoJBEc5AQFFWVlLsAlQWEAuAAkICggJZQAKCm4ABQABBVQGAgIBBwMLAwAEAQBgAAQICARUAAQECFgACAQITBtLsApQWEAzAAkICggJZQAKCm4AAQIAAVQABQACBVQGAQIHAwsDAAQCAGAABAgIBFQABAQIWAAIBAhMG0uwElBYQC4ACQgKCAllAAoKbgAFAAEFVAYCAgEHAwsDAAQBAGAABAgIBFQABAQIWAAIBAhMG0AvAAkICggJCm0ACgpuAAUAAQVUBgICAQcDCwMABAEAYAAECAgEVAAEBAhYAAgECExZWVlAHQEAamhiYFNRQD84NTMxIB4UEg8HBgMAXAFcDAUUKxMmLwE2MzIXFjMyNzY3MjcHFwYjIgcGFR8BFhcWFxYzMjc2NzY3Njc2NTQuAS8BJicmDwEnNzMXFjcXFhUUBwYHBgcGHQEUFxYXFgcGBwYHDgEjIi4BJyY9ATQnJgE1NCYjISIGHQEUFjMhMjYbFQQCBw8iHUoTLy5BER8RAQEhJCELBwEIAxkUIjExOzAfGBsKFAkMBAgEAgMKExg4CAEvcitDCgMCGRYpAwgBBQgDDAgPFSkqeVFdhEMNCQkOAvoKCPzLCAoKCAM1CAoC1gEBMQEDBAICAQEIKQUOB0KgnUUrIRMaEAoSFBAfIClXLDhQMSElDBQBAQIwBgIIARYHBA0HAQYDCA8PCwYL0m09KhokIR8lNFRDLVe6aQ4U/O8kCAoKCCQICgoACv////kDoQMLAA8AHwAvAD8ATwBfAG8AfwCPAJ8AekB3iYFpYTk4MQcHBnlxSUEZEQYDAllRKSEJCAEHAQADRwATEAwCBgcTBmARDQIHDggCAgMHAmAPCQIDCgQCAAEDAGALBQIBEhIBVAsFAgEBElgAEgESTJ6blpONi4WDfXt1c21rZWNdW1VTTUsmJiYmJiYmJiMUBR0rJTU0JisBIgYHFRQWOwEyNj0BNCYrASIGBxUUFjsBMjYFNTQmKwEiBh0BFBY7ATI2ATU0JisBIgYHFRQWOwEyNgU1NCYrASIGHQEUFjsBMjYFNTQmKwEiBh0BFBY7ATI2ATU0JisBIgYdARQWOwEyNgU1NCYrASIGHQEUFjsBMjY9ATQmKwEiBh0BFBY7ATI2NxEUBiMhIiY3ETQ2NyEyFgEeCgizBwoBDAazCAoKCLMHCgEMBrMICgEdCgezCAoKCLMHCv7jCgizBwoBDAazCAoBHQoHswgKCgizBwoBHgoIsggKCgiyCAr+4goHswgKCgizBwoBHgoIsggKCgiyCAoKCLIICgoIsggKSDYk/RIkNgE0JQLuJTRSawgKCghrCAoK3mwHCgoHbAcKCs9rCAoKCGsICgoBtWsICgoIawgKCs9sBwoKB2wHCgrPawgKCghrCAoKAbVrCAoKCGsICgrPbAcKCgdsBwoK3msICgoIawgKCrr9oSU0NCUCXyU0ATYABgAA/2oDWQNSABMAGgAjADMAQwBTAHJAbxQBAgQsJAIHBkA4AggJUEgCCgsERwACAAMGAgNgAAYABwkGB2ANAQkACAsJCGAOAQsACgULCmAABAQBWAABAQxIDAEFBQBYAAAADQBJREQ0NBsbRFNEUkxKNEM0Qjw6MC4oJhsjGyMTJhQ1Ng8FGSsBHgEVERQGByEiJicRNDY3ITIWFwcVMyYvASYTESMiJic1IRETNDYzITIWHQEUBiMhIiY1BTIWHQEUBiMhIiY9ATQ2MwUyFh0BFAYjISImPQE0NjMDMxAWHhf9EhceASAWAfQWNg9K0gUHrwbG6BceAf5TjwoIAYkICgoI/ncICgGbCAoKCP53CAoKCAGJCAoKCP53CAoKCAJ+EDQY/X4XHgEgFgN8Fx4BFhAm0hEGrwf8sAI8IBXp/KYB4wcKCgckCAoKCFkKCCQICgoIJAgKjwoIJAgKCggkCAoAAAAAAv////kDoQMLACEAQwA2QDM0EgICAQFHBwEDAgACAwBtBAEAAG4FAQECAgFUBQEBAQJYBgECAQJMNTU3NTU1NzMIBRwrARUUBisBIiY3ETQ+AjsBMhYHFRQGJyMiBh0BFBY7ATIWBRUUBisBIiY3ETQ+AjsBMhYHFRQGJyMiBh0BFBY7ATIWAa1ALNYsQAEuTGo6Iw8WARQQIztUIBV9LT4B9UAs1ixAAS5MajojDxYBFBAjO1QgFX0tPgE61i0+Pi0BiTpqTC4WDkgOFgFUOxIWID4t1i0+Pi0BiTpqTC4WDkgOFgFUOxIWID4AAAAAAgAA//kDoQMLABcALAAsQCkABAABBQQBYAAFAAACBQBgAAIDAwJUAAICA1gAAwIDTCM1NTU1MwYFGislETQmByEiJic1NCYHIyIGFREUFjMhMjYTERQGIyEiJjURNDY7ATIWHQEhMhYDWR4X/ncXHgEeF7MWICAWAqcWIEdKM/1ZM0pKM7MzSgF3M0p2AYkWIAEgFiQWIAEeF/3oFiAgAZ/+dzNKSjMCGDNKSjMSSgADAAD/uQQWAroAFAAkADkAHkAbLhECAAEBRwMBAQABbwIBAABmNTQoJxcSBAUWKyUHBiInASY0NwE2Mh8BFhQPARcWFAEDDgEvAS4BNxM+AR8BHgEJAQYiLwEmND8BJyY0PwE2MhcBFhQBWBwFDgb+/AYGAQQFEAQcBgbb2wYBRNACDgYiCAYB0QIMByMHCAFs/vwGDgYcBQXb2wUFHAYOBgEEBUUcBQUBBQUOBgEEBgYcBRAE3NsGDgJO/S8HCAMJAwwIAtAIBgEKAg7+j/77BQUcBg4G29wFDgYcBgb+/AUQAAACAAD/+QNYAv8AIwBIALJAFTgBCQo3AQQJJwEIAyIaEQYEAggER0uwHFBYQDcOAQwECwsMZQAKAAkECglgBQEEBgEDCAQDXgALAAgCCwhfDQcCAgAAAlINBwICAgBWAQEAAgBKG0A4DgEMBAsEDAttAAoACQQKCWAFAQQGAQMIBANeAAsACAILCF8NBwICAAACUg0HAgICAFYBAQACAEpZQB4kJAAAJEgkSEdGPTs0MiYlACMAIxEZERIRGxEPBRsrJRUjLwEmJyMUDwEGDwEjNTM3JyM1Mx8BFhczNj8CMxUjBxcBFSEnJjU0PgQ1NCYHIgcGByc2NzYzMhYVFA4EBzM1AfWLWQ0EAgIBBAUJVpBHbmdMmk0NBAICAQUOTo9FZ3IBoP7hAQMeKjQqHiIWHRkIDDsPFC86PUwaLC4uHAOCVl2NFwUHAgIICw6LXaKYXn8YBQYFBhh/XpWlAXtzDxAKJDomJBgkEhUcARUGEDQUESRCOB80IiAYIBQtAAAC/////gPpAr4AAgBFACBAHQIBAAMBAAFHAgEAAQBvAAEBZgcDJyQDRQdDAwUUKy0CNzIWHwIeBRceAhceAR0BFgcOAQ8BDgMPAQYjJyYvAS4CJy4CJy4BPQEmNz4BPwE+Aj8BNhY2FTYBjQEO/vJnXq4pKQkJCBIOEhAIBAoWBAQGAQsEFAgIBxIQEAgjjNLJVhkwFBQmDAQKFgQEBgELBBQICAgQEgcQCQgSjN2LjckGAgIBAQICCAYQBwMONB4kUBhiUVEeMgoJCA4GCAEFCwQCAgUDBBQNAw40HiRQGGJRUR8wCwkJDAoCBQMCBgIKAAAAAQAA/7EDxQMLAH4ATkBLWVQ0AwYFFwECAQgBAAIDRwgBBAkHAgUGBAVgAAYAAQIGAWAKAQIAAAJUCgECAgBYAwEAAgBMenlwb2tlYF9YVU9OSkR0Fj1gCwUYKwUiJiIGIyImNzQ+Ajc2PQE0JyYjISIPARQXHgEyFhcUBgciJiIGIyImNTQ+Ajc2NScRNzYmNC8BLgEnLgEGJjc0NjcyFjI2MzIWFRQGIgYHBhUXFjMhMjc2PQE0Jy4CNTQ2NzIWMjYzMhYVFAYiBgcGFRMUFx4BMhYXFAYDqxliMmIZDRABEhogCRIBBxX+iBYHARUJIh4UAQwPGmgxXhgNDhIWHgkSAQEBAgIEAggFCCIYFgEMDhpoMGAWDg4SGhwKFAEHDwGGDgcBEwouHA4OGGQvYBgODhQYIgcUARMJIBwSAQxPBAQYDRIQAgYGC0PaDAUDA+BPDAYEEBIOGAEEBBgNERAEBAcNQx8Bxg8NDhwKFAoQAgUEAhASDhgBBAQaDREQBAUMTsQCAgYMsk4MBgIMFg4YAQQEGg0REAQFDU398kIMBgQSEA4YAAEAAP+xAskDCwArAC5AKx0IAgECAUcAAgABAAJlAwEBAW4ABAAABFQABAQAWAAABABMPTMTOCMFBRkrARUUBiMiBwYHBhURFAYrASImNREjERQGKwEiJjURJicmJyY1NDY3NjMhMhYCyRQOHAIOBAEWDTwOFFASEDwOFFI3RiUkY0M+qwELDhQC6SkQJAEDDgYe/X0OFBQOAqj9WA4UFA4BFQcaIUNBUFyFFxUUAAAAAQAAAAEAAH4/3FVfDzz1AAsD6AAAAADY/aFhAAAAANj9oWH//f9qBC8DUgAAAAgAAgAAAAAAAAABAAADUv9qAAAEL//9//4ELwABAAAAAAAAAAAAAAAAAAAAIQPoAAAEL///AxEAAAMRAAADoAAAA6AAAANZAAACOwAAAxEAAANZ//0EL///A6AAAAMRAAAAwwAAAtkAAAPo//8BrAAAA6kAAAPoAAAD6AAAAsoAAAPoAAAD6AAAA1kAAAOg//8DWQAAA6D//wOgAAAELwAAA1kAAAPo//8D6AAAAsoAAAAAAAAAegDEAQwBdAJOAxADhgQ0BJgFxgZKBnQGpAb6B+QILAhiCOgJagm+ClwMCg1UDmIPFg+OD+YQWBEYEZISZhK9AAEAAAAhALAACwAAAAAAAgAuAD4AcwAAANgLcAAAAAAAAAASAN4AAQAAAAAAAAA1AAAAAQAAAAAAAQAIADUAAQAAAAAAAgAHAD0AAQAAAAAAAwAIAEQAAQAAAAAABAAIAEwAAQAAAAAABQALAFQAAQAAAAAABgAIAF8AAQAAAAAACgArAGcAAQAAAAAACwATAJIAAwABBAkAAABqAKUAAwABBAkAAQAQAQ8AAwABBAkAAgAOAR8AAwABBAkAAwAQAS0AAwABBAkABAAQAT0AAwABBAkABQAWAU0AAwABBAkABgAQAWMAAwABBAkACgBWAXMAAwABBAkACwAmAclDb3B5cmlnaHQgKEMpIDIwMTkgYnkgb3JpZ2luYWwgYXV0aG9ycyBAIGZvbnRlbGxvLmNvbWZvbnRlbGxvUmVndWxhcmZvbnRlbGxvZm9udGVsbG9WZXJzaW9uIDEuMGZvbnRlbGxvR2VuZXJhdGVkIGJ5IHN2ZzJ0dGYgZnJvbSBGb250ZWxsbyBwcm9qZWN0Lmh0dHA6Ly9mb250ZWxsby5jb20AQwBvAHAAeQByAGkAZwBoAHQAIAAoAEMAKQAgADIAMAAxADkAIABiAHkAIABvAHIAaQBnAGkAbgBhAGwAIABhAHUAdABoAG8AcgBzACAAQAAgAGYAbwBuAHQAZQBsAGwAbwAuAGMAbwBtAGYAbwBuAHQAZQBsAGwAbwBSAGUAZwB1AGwAYQByAGYAbwBuAHQAZQBsAGwAbwBmAG8AbgB0AGUAbABsAG8AVgBlAHIAcwBpAG8AbgAgADEALgAwAGYAbwBuAHQAZQBsAGwAbwBHAGUAbgBlAHIAYQB0AGUAZAAgAGIAeQAgAHMAdgBnADIAdAB0AGYAIABmAHIAbwBtACAARgBvAG4AdABlAGwAbABvACAAcAByAG8AagBlAGMAdAAuAGgAdAB0AHAAOgAvAC8AZgBvAG4AdABlAGwAbABvAC4AYwBvAG0AAAAAAgAAAAAAAAAKAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAhAQIBAwEEAQUBBgEHAQgBCQEKAQsBDAENAQ4BDwEQAREBEgETARQBFQEWARcBGAEZARoBGwEcAR0BHgEfASABIQEiAAdwaWN0dXJlBmNhbmNlbARwbHVzBGhvbWUEbGluawNjb2cGaXRhbGljBGJvbGQDb2ZmBXZpZGVvBnVwbG9hZAVtaW51cwVjb2xvbgJwaQhsaXN0LWFsdA9yZXNpemUtdmVydGljYWwEbWF0aARtb3ZlCGxpbmstZXh0DmJvb2ttYXJrLWVtcHR5C2xpc3QtYnVsbGV0DWxpc3QtbnVtYmVyZWQJdW5kZXJsaW5lBXRhYmxlCGRvYy10ZXh0CnF1b3RlLWxlZnQMZm9sZGVyLWVtcHR5BGNvZGULc3VwZXJzY3JpcHQMeW91dHViZS1wbGF5BmhlYWRlcglwYXJhZ3JhcGgAAAAAAAEAAf//AA8AAAAAAAAAAAAAAAAAAAAAABgAGAAYABgDUv9qA1L/arAALCCwAFVYRVkgIEu4AA5RS7AGU1pYsDQbsChZYGYgilVYsAIlYbkIAAgAY2MjYhshIbAAWbAAQyNEsgABAENgQi2wASywIGBmLbACLCBkILDAULAEJlqyKAEKQ0VjRVJbWCEjIRuKWCCwUFBYIbBAWRsgsDhQWCGwOFlZILEBCkNFY0VhZLAoUFghsQEKQ0VjRSCwMFBYIbAwWRsgsMBQWCBmIIqKYSCwClBYYBsgsCBQWCGwCmAbILA2UFghsDZgG2BZWVkbsAErWVkjsABQWGVZWS2wAywgRSCwBCVhZCCwBUNQWLAFI0KwBiNCGyEhWbABYC2wBCwjISMhIGSxBWJCILAGI0KxAQpDRWOxAQpDsAFgRWOwAyohILAGQyCKIIqwASuxMAUlsAQmUVhgUBthUllYI1khILBAU1iwASsbIbBAWSOwAFBYZVktsAUssAdDK7IAAgBDYEItsAYssAcjQiMgsAAjQmGwAmJmsAFjsAFgsAUqLbAHLCAgRSCwC0NjuAQAYiCwAFBYsEBgWWawAWNgRLABYC2wCCyyBwsAQ0VCKiGyAAEAQ2BCLbAJLLAAQyNEsgABAENgQi2wCiwgIEUgsAErI7AAQ7AEJWAgRYojYSBkILAgUFghsAAbsDBQWLAgG7BAWVkjsABQWGVZsAMlI2FERLABYC2wCywgIEUgsAErI7AAQ7AEJWAgRYojYSBksCRQWLAAG7BAWSOwAFBYZVmwAyUjYUREsAFgLbAMLCCwACNCsgsKA0VYIRsjIVkqIS2wDSyxAgJFsGRhRC2wDiywAWAgILAMQ0qwAFBYILAMI0JZsA1DSrAAUlggsA0jQlktsA8sILAQYmawAWMguAQAY4ojYbAOQ2AgimAgsA4jQiMtsBAsS1RYsQRkRFkksA1lI3gtsBEsS1FYS1NYsQRkRFkbIVkksBNlI3gtsBIssQAPQ1VYsQ8PQ7ABYUKwDytZsABDsAIlQrEMAiVCsQ0CJUKwARYjILADJVBYsQEAQ2CwBCVCioogiiNhsA4qISOwAWEgiiNhsA4qIRuxAQBDYLACJUKwAiVhsA4qIVmwDENHsA1DR2CwAmIgsABQWLBAYFlmsAFjILALQ2O4BABiILAAUFiwQGBZZrABY2CxAAATI0SwAUOwAD6yAQEBQ2BCLbATLACxAAJFVFiwDyNCIEWwCyNCsAojsAFgQiBgsAFhtRAQAQAOAEJCimCxEgYrsHIrGyJZLbAULLEAEystsBUssQETKy2wFiyxAhMrLbAXLLEDEystsBgssQQTKy2wGSyxBRMrLbAaLLEGEystsBsssQcTKy2wHCyxCBMrLbAdLLEJEystsB4sALANK7EAAkVUWLAPI0IgRbALI0KwCiOwAWBCIGCwAWG1EBABAA4AQkKKYLESBiuwcisbIlktsB8ssQAeKy2wICyxAR4rLbAhLLECHistsCIssQMeKy2wIyyxBB4rLbAkLLEFHistsCUssQYeKy2wJiyxBx4rLbAnLLEIHistsCgssQkeKy2wKSwgPLABYC2wKiwgYLAQYCBDI7ABYEOwAiVhsAFgsCkqIS2wKyywKiuwKiotsCwsICBHICCwC0NjuAQAYiCwAFBYsEBgWWawAWNgI2E4IyCKVVggRyAgsAtDY7gEAGIgsABQWLBAYFlmsAFjYCNhOBshWS2wLSwAsQACRVRYsAEWsCwqsAEVMBsiWS2wLiwAsA0rsQACRVRYsAEWsCwqsAEVMBsiWS2wLywgNbABYC2wMCwAsAFFY7gEAGIgsABQWLBAYFlmsAFjsAErsAtDY7gEAGIgsABQWLBAYFlmsAFjsAErsAAWtAAAAAAARD4jOLEvARUqLbAxLCA8IEcgsAtDY7gEAGIgsABQWLBAYFlmsAFjYLAAQ2E4LbAyLC4XPC2wMywgPCBHILALQ2O4BABiILAAUFiwQGBZZrABY2CwAENhsAFDYzgtsDQssQIAFiUgLiBHsAAjQrACJUmKikcjRyNhIFhiGyFZsAEjQrIzAQEVFCotsDUssAAWsAQlsAQlRyNHI2GwCUMrZYouIyAgPIo4LbA2LLAAFrAEJbAEJSAuRyNHI2EgsAQjQrAJQysgsGBQWCCwQFFYswIgAyAbswImAxpZQkIjILAIQyCKI0cjRyNhI0ZgsARDsAJiILAAUFiwQGBZZrABY2AgsAErIIqKYSCwAkNgZCOwA0NhZFBYsAJDYRuwA0NgWbADJbACYiCwAFBYsEBgWWawAWNhIyAgsAQmI0ZhOBsjsAhDRrACJbAIQ0cjRyNhYCCwBEOwAmIgsABQWLBAYFlmsAFjYCMgsAErI7AEQ2CwASuwBSVhsAUlsAJiILAAUFiwQGBZZrABY7AEJmEgsAQlYGQjsAMlYGRQWCEbIyFZIyAgsAQmI0ZhOFktsDcssAAWICAgsAUmIC5HI0cjYSM8OC2wOCywABYgsAgjQiAgIEYjR7ABKyNhOC2wOSywABawAyWwAiVHI0cjYbAAVFguIDwjIRuwAiWwAiVHI0cjYSCwBSWwBCVHI0cjYbAGJbAFJUmwAiVhuQgACABjYyMgWGIbIVljuAQAYiCwAFBYsEBgWWawAWNgIy4jICA8ijgjIVktsDossAAWILAIQyAuRyNHI2EgYLAgYGawAmIgsABQWLBAYFlmsAFjIyAgPIo4LbA7LCMgLkawAiVGUlggPFkusSsBFCstsDwsIyAuRrACJUZQWCA8WS6xKwEUKy2wPSwjIC5GsAIlRlJYIDxZIyAuRrACJUZQWCA8WS6xKwEUKy2wPiywNSsjIC5GsAIlRlJYIDxZLrErARQrLbA/LLA2K4ogIDywBCNCijgjIC5GsAIlRlJYIDxZLrErARQrsARDLrArKy2wQCywABawBCWwBCYgLkcjRyNhsAlDKyMgPCAuIzixKwEUKy2wQSyxCAQlQrAAFrAEJbAEJSAuRyNHI2EgsAQjQrAJQysgsGBQWCCwQFFYswIgAyAbswImAxpZQkIjIEewBEOwAmIgsABQWLBAYFlmsAFjYCCwASsgiophILACQ2BkI7ADQ2FkUFiwAkNhG7ADQ2BZsAMlsAJiILAAUFiwQGBZZrABY2GwAiVGYTgjIDwjOBshICBGI0ewASsjYTghWbErARQrLbBCLLA1Ky6xKwEUKy2wQyywNishIyAgPLAEI0IjOLErARQrsARDLrArKy2wRCywABUgR7AAI0KyAAEBFRQTLrAxKi2wRSywABUgR7AAI0KyAAEBFRQTLrAxKi2wRiyxAAEUE7AyKi2wRyywNCotsEgssAAWRSMgLiBGiiNhOLErARQrLbBJLLAII0KwSCstsEossgAAQSstsEsssgABQSstsEwssgEAQSstsE0ssgEBQSstsE4ssgAAQistsE8ssgABQistsFAssgEAQistsFEssgEBQistsFIssgAAPistsFMssgABPistsFQssgEAPistsFUssgEBPistsFYssgAAQCstsFcssgABQCstsFgssgEAQCstsFkssgEBQCstsFossgAAQystsFsssgABQystsFwssgEAQystsF0ssgEBQystsF4ssgAAPystsF8ssgABPystsGAssgEAPystsGEssgEBPystsGIssDcrLrErARQrLbBjLLA3K7A7Ky2wZCywNyuwPCstsGUssAAWsDcrsD0rLbBmLLA4Ky6xKwEUKy2wZyywOCuwOystsGgssDgrsDwrLbBpLLA4K7A9Ky2waiywOSsusSsBFCstsGsssDkrsDsrLbBsLLA5K7A8Ky2wbSywOSuwPSstsG4ssDorLrErARQrLbBvLLA6K7A7Ky2wcCywOiuwPCstsHEssDorsD0rLbByLLMJBAIDRVghGyMhWUIrsAhlsAMkUHiwARUwLQBLuADIUlixAQGOWbABuQgACABjcLEABUKyAAEAKrEABUKzCgIBCCqxAAVCsw4AAQgqsQAGQroCwAABAAkqsQAHQroAQAABAAkqsQMARLEkAYhRWLBAiFixA2REsSYBiFFYugiAAAEEQIhjVFixAwBEWVlZWbMMAgEMKrgB/4WwBI2xAgBEAAA=') format('truetype'); +} +/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ +/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ +/* +@media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: 'fontello'; + src: url('../font/fontello.svg?71992743#fontello') format('svg'); + } +} +*/ + + [class^="icon-"]:before, [class*=" icon-"]:before { + font-family: "fontello"; + font-style: normal; + font-weight: normal; + speak: none; + + display: inline-block; + text-decoration: inherit; + width: 1em; + margin-right: .2em; + text-align: center; + /* opacity: .8; */ + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + + /* Animation center compensation - margins should be symmetric */ + /* remove if not needed */ + margin-left: .2em; + + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ + + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ +} +.icon-picture:before { content: '\e800'; } /* '' */ +.icon-cancel:before { content: '\e801'; } /* '' */ +.icon-plus:before { content: '\e802'; } /* '' */ +.icon-home:before { content: '\e803'; } /* '' */ +.icon-link:before { content: '\e804'; } /* '' */ +.icon-cog:before { content: '\e805'; } /* '' */ +.icon-italic:before { content: '\e806'; } /* '' */ +.icon-bold:before { content: '\e807'; } /* '' */ +.icon-off:before { content: '\e808'; } /* '' */ +.icon-video:before { content: '\e809'; } /* '' */ +.icon-upload:before { content: '\e80a'; } /* '' */ +.icon-minus:before { content: '\e80b'; } /* '' */ +.icon-colon:before { content: '\e80c'; } /* '' */ +.icon-pi:before { content: '\e80d'; } /* '' */ +.icon-list-alt:before { content: '\e80e'; } /* '' */ +.icon-resize-vertical:before { content: '\e80f'; } /* '' */ +.icon-math:before { content: '\f01a'; } /* '' */ +.icon-move:before { content: '\f047'; } /* '' */ +.icon-link-ext:before { content: '\f08e'; } /* '' */ +.icon-bookmark-empty:before { content: '\f097'; } /* '' */ +.icon-list-bullet:before { content: '\f0ca'; } /* '' */ +.icon-list-numbered:before { content: '\f0cb'; } /* '' */ +.icon-underline:before { content: '\f0cd'; } /* '' */ +.icon-table:before { content: '\f0ce'; } /* '' */ +.icon-doc-text:before { content: '\f0f6'; } /* '' */ +.icon-quote-left:before { content: '\f10d'; } /* '' */ +.icon-folder-empty:before { content: '\f114'; } /* '' */ +.icon-code:before { content: '\f121'; } /* '' */ +.icon-superscript:before { content: '\f12b'; } /* '' */ +.icon-youtube-play:before { content: '\f16a'; } /* '' */ +.icon-header:before { content: '\f1dc'; } /* '' */ .icon-paragraph:before { content: '\f1dd'; } /* '' */ \ No newline at end of file diff --git a/system/author/css/fontello/css/fontello-ie7-codes.css b/system/author/css/fontello/css/fontello-ie7-codes.css index ab92fbb..851b299 100644 --- a/system/author/css/fontello/css/fontello-ie7-codes.css +++ b/system/author/css/fontello/css/fontello-ie7-codes.css @@ -1,33 +1,33 @@ - -.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-link { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-italic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-bold { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-upload { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-minus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-colon { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-pi { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-list-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-resize-vertical { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-math { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-bookmark-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-list-bullet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-list-numbered { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-underline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-table { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-quote-left { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-code { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-superscript { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-youtube-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-header { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } + +.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-link { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-italic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-bold { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-upload { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-minus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-colon { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-pi { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-list-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-resize-vertical { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-math { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-bookmark-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-list-bullet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-list-numbered { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-underline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-table { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-quote-left { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-code { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-superscript { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-youtube-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-header { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-paragraph { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/system/author/css/fontello/css/fontello-ie7.css b/system/author/css/fontello/css/fontello-ie7.css index 1149754..7a5842e 100644 --- a/system/author/css/fontello/css/fontello-ie7.css +++ b/system/author/css/fontello/css/fontello-ie7.css @@ -1,44 +1,44 @@ -[class^="icon-"], [class*=" icon-"] { - font-family: 'fontello'; - font-style: normal; - font-weight: normal; - - /* fix buttons height */ - line-height: 1em; - - /* you can be more comfortable with increased icons size */ - /* font-size: 120%; */ -} - -.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-link { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-italic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-bold { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-upload { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-minus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-colon { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-pi { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-list-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-resize-vertical { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-math { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-bookmark-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-list-bullet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-list-numbered { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-underline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-table { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-quote-left { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-code { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-superscript { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-youtube-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } -.icon-header { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +[class^="icon-"], [class*=" icon-"] { + font-family: 'fontello'; + font-style: normal; + font-weight: normal; + + /* fix buttons height */ + line-height: 1em; + + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ +} + +.icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-link { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-italic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-bold { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-upload { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-minus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-colon { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-pi { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-list-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-resize-vertical { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-math { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-bookmark-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-list-bullet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-list-numbered { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-underline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-table { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-quote-left { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-folder-empty { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-code { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-superscript { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-youtube-play { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } +.icon-header { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } .icon-paragraph { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = ' '); } \ No newline at end of file diff --git a/system/author/css/fontello/css/fontello.css b/system/author/css/fontello/css/fontello.css index 5209d3a..0ae4eb0 100644 --- a/system/author/css/fontello/css/fontello.css +++ b/system/author/css/fontello/css/fontello.css @@ -1,89 +1,89 @@ -@font-face { - font-family: 'fontello'; - src: url('../font/fontello.eot?35517051'); - src: url('../font/fontello.eot?35517051#iefix') format('embedded-opentype'), - url('../font/fontello.woff2?35517051') format('woff2'), - url('../font/fontello.woff?35517051') format('woff'), - url('../font/fontello.ttf?35517051') format('truetype'), - url('../font/fontello.svg?35517051#fontello') format('svg'); - font-weight: normal; - font-style: normal; -} -/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ -/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ -/* -@media screen and (-webkit-min-device-pixel-ratio:0) { - @font-face { - font-family: 'fontello'; - src: url('../font/fontello.svg?35517051#fontello') format('svg'); - } -} -*/ - - [class^="icon-"]:before, [class*=" icon-"]:before { - font-family: "fontello"; - font-style: normal; - font-weight: normal; - speak: none; - - display: inline-block; - text-decoration: inherit; - width: 1em; - margin-right: .2em; - text-align: center; - /* opacity: .8; */ - - /* For safety - reset parent styles, that can break glyph codes*/ - font-variant: normal; - text-transform: none; - - /* fix buttons height, for twitter bootstrap */ - line-height: 1em; - - /* Animation center compensation - margins should be symmetric */ - /* remove if not needed */ - margin-left: .2em; - - /* you can be more comfortable with increased icons size */ - /* font-size: 120%; */ - - /* Font smoothing. That was taken from TWBS */ - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - - /* Uncomment for 3D effect */ - /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ -} - -.icon-picture:before { content: '\e800'; } /* '' */ -.icon-cancel:before { content: '\e801'; } /* '' */ -.icon-plus:before { content: '\e802'; } /* '' */ -.icon-home:before { content: '\e803'; } /* '' */ -.icon-link:before { content: '\e804'; } /* '' */ -.icon-cog:before { content: '\e805'; } /* '' */ -.icon-italic:before { content: '\e806'; } /* '' */ -.icon-bold:before { content: '\e807'; } /* '' */ -.icon-off:before { content: '\e808'; } /* '' */ -.icon-video:before { content: '\e809'; } /* '' */ -.icon-upload:before { content: '\e80a'; } /* '' */ -.icon-minus:before { content: '\e80b'; } /* '' */ -.icon-colon:before { content: '\e80c'; } /* '' */ -.icon-pi:before { content: '\e80d'; } /* '' */ -.icon-list-alt:before { content: '\e80e'; } /* '' */ -.icon-resize-vertical:before { content: '\e80f'; } /* '' */ -.icon-math:before { content: '\f01a'; } /* '' */ -.icon-move:before { content: '\f047'; } /* '' */ -.icon-link-ext:before { content: '\f08e'; } /* '' */ -.icon-bookmark-empty:before { content: '\f097'; } /* '' */ -.icon-list-bullet:before { content: '\f0ca'; } /* '' */ -.icon-list-numbered:before { content: '\f0cb'; } /* '' */ -.icon-underline:before { content: '\f0cd'; } /* '' */ -.icon-table:before { content: '\f0ce'; } /* '' */ -.icon-doc-text:before { content: '\f0f6'; } /* '' */ -.icon-quote-left:before { content: '\f10d'; } /* '' */ -.icon-folder-empty:before { content: '\f114'; } /* '' */ -.icon-code:before { content: '\f121'; } /* '' */ -.icon-superscript:before { content: '\f12b'; } /* '' */ -.icon-youtube-play:before { content: '\f16a'; } /* '' */ -.icon-header:before { content: '\f1dc'; } /* '' */ +@font-face { + font-family: 'fontello'; + src: url('../font/fontello.eot?35517051'); + src: url('../font/fontello.eot?35517051#iefix') format('embedded-opentype'), + url('../font/fontello.woff2?35517051') format('woff2'), + url('../font/fontello.woff?35517051') format('woff'), + url('../font/fontello.ttf?35517051') format('truetype'), + url('../font/fontello.svg?35517051#fontello') format('svg'); + font-weight: normal; + font-style: normal; +} +/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ +/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ +/* +@media screen and (-webkit-min-device-pixel-ratio:0) { + @font-face { + font-family: 'fontello'; + src: url('../font/fontello.svg?35517051#fontello') format('svg'); + } +} +*/ + + [class^="icon-"]:before, [class*=" icon-"]:before { + font-family: "fontello"; + font-style: normal; + font-weight: normal; + speak: none; + + display: inline-block; + text-decoration: inherit; + width: 1em; + margin-right: .2em; + text-align: center; + /* opacity: .8; */ + + /* For safety - reset parent styles, that can break glyph codes*/ + font-variant: normal; + text-transform: none; + + /* fix buttons height, for twitter bootstrap */ + line-height: 1em; + + /* Animation center compensation - margins should be symmetric */ + /* remove if not needed */ + margin-left: .2em; + + /* you can be more comfortable with increased icons size */ + /* font-size: 120%; */ + + /* Font smoothing. That was taken from TWBS */ + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + + /* Uncomment for 3D effect */ + /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ +} + +.icon-picture:before { content: '\e800'; } /* '' */ +.icon-cancel:before { content: '\e801'; } /* '' */ +.icon-plus:before { content: '\e802'; } /* '' */ +.icon-home:before { content: '\e803'; } /* '' */ +.icon-link:before { content: '\e804'; } /* '' */ +.icon-cog:before { content: '\e805'; } /* '' */ +.icon-italic:before { content: '\e806'; } /* '' */ +.icon-bold:before { content: '\e807'; } /* '' */ +.icon-off:before { content: '\e808'; } /* '' */ +.icon-video:before { content: '\e809'; } /* '' */ +.icon-upload:before { content: '\e80a'; } /* '' */ +.icon-minus:before { content: '\e80b'; } /* '' */ +.icon-colon:before { content: '\e80c'; } /* '' */ +.icon-pi:before { content: '\e80d'; } /* '' */ +.icon-list-alt:before { content: '\e80e'; } /* '' */ +.icon-resize-vertical:before { content: '\e80f'; } /* '' */ +.icon-math:before { content: '\f01a'; } /* '' */ +.icon-move:before { content: '\f047'; } /* '' */ +.icon-link-ext:before { content: '\f08e'; } /* '' */ +.icon-bookmark-empty:before { content: '\f097'; } /* '' */ +.icon-list-bullet:before { content: '\f0ca'; } /* '' */ +.icon-list-numbered:before { content: '\f0cb'; } /* '' */ +.icon-underline:before { content: '\f0cd'; } /* '' */ +.icon-table:before { content: '\f0ce'; } /* '' */ +.icon-doc-text:before { content: '\f0f6'; } /* '' */ +.icon-quote-left:before { content: '\f10d'; } /* '' */ +.icon-folder-empty:before { content: '\f114'; } /* '' */ +.icon-code:before { content: '\f121'; } /* '' */ +.icon-superscript:before { content: '\f12b'; } /* '' */ +.icon-youtube-play:before { content: '\f16a'; } /* '' */ +.icon-header:before { content: '\f1dc'; } /* '' */ .icon-paragraph:before { content: '\f1dd'; } /* '' */ \ No newline at end of file diff --git a/system/author/css/fontello/demo.html b/system/author/css/fontello/demo.html index 77b3e1e..481761c 100644 --- a/system/author/css/fontello/demo.html +++ b/system/author/css/fontello/demo.html @@ -1,351 +1,351 @@ - - - - - - - - -
-

fontello font demo

- -
-
-
-
icon-picture0xe800
-
icon-cancel0xe801
-
icon-plus0xe802
-
icon-home0xe803
-
-
-
icon-link0xe804
-
icon-cog0xe805
-
icon-italic0xe806
-
icon-bold0xe807
-
-
-
icon-off0xe808
-
icon-video0xe809
-
icon-upload0xe80a
-
icon-minus0xe80b
-
-
-
icon-colon0xe80c
-
icon-pi0xe80d
-
icon-list-alt0xe80e
-
icon-resize-vertical0xe80f
-
-
-
icon-math0xf01a
-
icon-move0xf047
-
icon-link-ext0xf08e
-
icon-bookmark-empty0xf097
-
-
-
icon-list-bullet0xf0ca
-
icon-list-numbered0xf0cb
-
icon-underline0xf0cd
-
icon-table0xf0ce
-
-
-
icon-doc-text0xf0f6
-
icon-quote-left0xf10d
-
icon-folder-empty0xf114
-
icon-code0xf121
-
-
-
icon-superscript0xf12b
-
icon-youtube-play0xf16a
-
icon-header0xf1dc
-
icon-paragraph0xf1dd
-
-
- - + + + + + + + + +
+

fontello font demo

+ +
+
+
+
icon-picture0xe800
+
icon-cancel0xe801
+
icon-plus0xe802
+
icon-home0xe803
+
+
+
icon-link0xe804
+
icon-cog0xe805
+
icon-italic0xe806
+
icon-bold0xe807
+
+
+
icon-off0xe808
+
icon-video0xe809
+
icon-upload0xe80a
+
icon-minus0xe80b
+
+
+
icon-colon0xe80c
+
icon-pi0xe80d
+
icon-list-alt0xe80e
+
icon-resize-vertical0xe80f
+
+
+
icon-math0xf01a
+
icon-move0xf047
+
icon-link-ext0xf08e
+
icon-bookmark-empty0xf097
+
+
+
icon-list-bullet0xf0ca
+
icon-list-numbered0xf0cb
+
icon-underline0xf0cd
+
icon-table0xf0ce
+
+
+
icon-doc-text0xf0f6
+
icon-quote-left0xf10d
+
icon-folder-empty0xf114
+
icon-code0xf121
+
+
+
icon-superscript0xf12b
+
icon-youtube-play0xf16a
+
icon-header0xf1dc
+
icon-paragraph0xf1dd
+
+
+ + \ No newline at end of file diff --git a/system/author/css/fontello/font/fontello.svg b/system/author/css/fontello/font/fontello.svg index ef3f2f6..d782771 100644 --- a/system/author/css/fontello/font/fontello.svg +++ b/system/author/css/fontello/font/fontello.svg @@ -1,74 +1,74 @@ - - - -Copyright (C) 2019 by original authors @ fontello.com - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + +Copyright (C) 2019 by original authors @ fontello.com + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/system/author/css/normalize.css b/system/author/css/normalize.css index 9b77e0e..b672540 100644 --- a/system/author/css/normalize.css +++ b/system/author/css/normalize.css @@ -1,461 +1,461 @@ -/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ - -/** - * 1. Change the default font family in all browsers (opinionated). - * 2. Correct the line height in all browsers. - * 3. Prevent adjustments of font size after orientation changes in - * IE on Windows Phone and in iOS. - */ - -/* Document - ========================================================================== */ - -html { - font-family: sans-serif; /* 1 */ - line-height: 1.15; /* 2 */ - -ms-text-size-adjust: 100%; /* 3 */ - -webkit-text-size-adjust: 100%; /* 3 */ -} - -/* Sections - ========================================================================== */ - -/** - * Remove the margin in all browsers (opinionated). - */ - -body { - margin: 0; -} - -/** - * Add the correct display in IE 9-. - */ - -article, -aside, -footer, -header, -nav, -section { - display: block; -} - -/** - * Correct the font size and margin on `h1` elements within `section` and - * `article` contexts in Chrome, Firefox, and Safari. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -/* Grouping content - ========================================================================== */ - -/** - * Add the correct display in IE 9-. - * 1. Add the correct display in IE. - */ - -figcaption, -figure, -main { /* 1 */ - display: block; -} - -/** - * Add the correct margin in IE 8. - */ - -figure { - margin: 1em 40px; -} - -/** - * 1. Add the correct box sizing in Firefox. - * 2. Show the overflow in Edge and IE. - */ - -hr { - box-sizing: content-box; /* 1 */ - height: 0; /* 1 */ - overflow: visible; /* 2 */ -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -pre { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/* Text-level semantics - ========================================================================== */ - -/** - * 1. Remove the gray background on active links in IE 10. - * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. - */ - -a { - background-color: transparent; /* 1 */ - -webkit-text-decoration-skip: objects; /* 2 */ -} - -/** - * Remove the outline on focused links when they are also active or hovered - * in all browsers (opinionated). - */ - -a:active, -a:hover { - outline-width: 0; -} - -/** - * 1. Remove the bottom border in Firefox 39-. - * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. - */ - -abbr[title] { - border-bottom: none; /* 1 */ - text-decoration: underline; /* 2 */ - text-decoration: underline dotted; /* 2 */ -} - -/** - * Prevent the duplicate application of `bolder` by the next rule in Safari 6. - */ - -b, -strong { - font-weight: inherit; -} - -/** - * Add the correct font weight in Chrome, Edge, and Safari. - */ - -b, -strong { - font-weight: bolder; -} - -/** - * 1. Correct the inheritance and scaling of font size in all browsers. - * 2. Correct the odd `em` font sizing in all browsers. - */ - -code, -kbd, -samp { - font-family: monospace, monospace; /* 1 */ - font-size: 1em; /* 2 */ -} - -/** - * Add the correct font style in Android 4.3-. - */ - -dfn { - font-style: italic; -} - -/** - * Add the correct background and color in IE 9-. - */ - -mark { - background-color: #ff0; - color: #000; -} - -/** - * Add the correct font size in all browsers. - */ - -small { - font-size: 80%; -} - -/** - * Prevent `sub` and `sup` elements from affecting the line height in - * all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sub { - bottom: -0.25em; -} - -sup { - top: -0.5em; -} - -/* Embedded content - ========================================================================== */ - -/** - * Add the correct display in IE 9-. - */ - -audio, -video { - display: inline-block; -} - -/** - * Add the correct display in iOS 4-7. - */ - -audio:not([controls]) { - display: none; - height: 0; -} - -/** - * Remove the border on images inside links in IE 10-. - */ - -img { - border-style: none; -} - -/** - * Hide the overflow in IE. - */ - -svg:not(:root) { - overflow: hidden; -} - -/* Forms - ========================================================================== */ - -/** - * 1. Change the font styles in all browsers (opinionated). - * 2. Remove the margin in Firefox and Safari. - */ - -button, -input, -optgroup, -select, -textarea { - font-family: sans-serif; /* 1 */ - font-size: 100%; /* 1 */ - line-height: 1.15; /* 1 */ - margin: 0; /* 2 */ -} - -/** - * Show the overflow in IE. - * 1. Show the overflow in Edge. - */ - -button, -input { /* 1 */ - overflow: visible; -} - -/** - * Remove the inheritance of text transform in Edge, Firefox, and IE. - * 1. Remove the inheritance of text transform in Firefox. - */ - -button, -select { /* 1 */ - text-transform: none; -} - -/** - * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` - * controls in Android 4. - * 2. Correct the inability to style clickable types in iOS and Safari. - */ - -button, -html [type="button"], /* 1 */ -[type="reset"], -[type="submit"] { - -webkit-appearance: button; /* 2 */ -} - -/** - * Remove the inner border and padding in Firefox. - */ - -button::-moz-focus-inner, -[type="button"]::-moz-focus-inner, -[type="reset"]::-moz-focus-inner, -[type="submit"]::-moz-focus-inner { - border-style: none; - padding: 0; -} - -/** - * Restore the focus styles unset by the previous rule. - */ - -button:-moz-focusring, -[type="button"]:-moz-focusring, -[type="reset"]:-moz-focusring, -[type="submit"]:-moz-focusring { - outline: 1px dotted ButtonText; -} - -/** - * Change the border, margin, and padding in all browsers (opinionated). - */ - -fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; -} - -/** - * 1. Correct the text wrapping in Edge and IE. - * 2. Correct the color inheritance from `fieldset` elements in IE. - * 3. Remove the padding so developers are not caught out when they zero out - * `fieldset` elements in all browsers. - */ - -legend { - box-sizing: border-box; /* 1 */ - color: inherit; /* 2 */ - display: table; /* 1 */ - max-width: 100%; /* 1 */ - padding: 0; /* 3 */ - white-space: normal; /* 1 */ -} - -/** - * 1. Add the correct display in IE 9-. - * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. - */ - -progress { - display: inline-block; /* 1 */ - vertical-align: baseline; /* 2 */ -} - -/** - * Remove the default vertical scrollbar in IE. - */ - -textarea { - overflow: auto; -} - -/** - * 1. Add the correct box sizing in IE 10-. - * 2. Remove the padding in IE 10-. - */ - -[type="checkbox"], -[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ -} - -/** - * Correct the cursor style of increment and decrement buttons in Chrome. - */ - -[type="number"]::-webkit-inner-spin-button, -[type="number"]::-webkit-outer-spin-button { - height: auto; -} - -/** - * 1. Correct the odd appearance in Chrome and Safari. - * 2. Correct the outline style in Safari. - */ - -[type="search"] { - -webkit-appearance: textfield; /* 1 */ - outline-offset: -2px; /* 2 */ -} - -/** - * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. - */ - -[type="search"]::-webkit-search-cancel-button, -[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/** - * 1. Correct the inability to style clickable types in iOS and Safari. - * 2. Change font properties to `inherit` in Safari. - */ - -::-webkit-file-upload-button { - -webkit-appearance: button; /* 1 */ - font: inherit; /* 2 */ -} - -/* Interactive - ========================================================================== */ - -/* - * Add the correct display in IE 9-. - * 1. Add the correct display in Edge, IE, and Firefox. - */ - -details, /* 1 */ -menu { - display: block; -} - -/* - * Add the correct display in all browsers. - */ - -summary { - display: list-item; -} - -/* Scripting - ========================================================================== */ - -/** - * Add the correct display in IE 9-. - */ - -canvas { - display: inline-block; -} - -/** - * Add the correct display in IE. - */ - -template { - display: none; -} - -/* Hidden - ========================================================================== */ - -/** - * Add the correct display in IE 10-. - */ - -[hidden] { - display: none; -} +/*! normalize.css v5.0.0 | MIT License | github.com/necolas/normalize.css */ + +/** + * 1. Change the default font family in all browsers (opinionated). + * 2. Correct the line height in all browsers. + * 3. Prevent adjustments of font size after orientation changes in + * IE on Windows Phone and in iOS. + */ + +/* Document + ========================================================================== */ + +html { + font-family: sans-serif; /* 1 */ + line-height: 1.15; /* 2 */ + -ms-text-size-adjust: 100%; /* 3 */ + -webkit-text-size-adjust: 100%; /* 3 */ +} + +/* Sections + ========================================================================== */ + +/** + * Remove the margin in all browsers (opinionated). + */ + +body { + margin: 0; +} + +/** + * Add the correct display in IE 9-. + */ + +article, +aside, +footer, +header, +nav, +section { + display: block; +} + +/** + * Correct the font size and margin on `h1` elements within `section` and + * `article` contexts in Chrome, Firefox, and Safari. + */ + +h1 { + font-size: 2em; + margin: 0.67em 0; +} + +/* Grouping content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + * 1. Add the correct display in IE. + */ + +figcaption, +figure, +main { /* 1 */ + display: block; +} + +/** + * Add the correct margin in IE 8. + */ + +figure { + margin: 1em 40px; +} + +/** + * 1. Add the correct box sizing in Firefox. + * 2. Show the overflow in Edge and IE. + */ + +hr { + box-sizing: content-box; /* 1 */ + height: 0; /* 1 */ + overflow: visible; /* 2 */ +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +pre { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/* Text-level semantics + ========================================================================== */ + +/** + * 1. Remove the gray background on active links in IE 10. + * 2. Remove gaps in links underline in iOS 8+ and Safari 8+. + */ + +a { + background-color: transparent; /* 1 */ + -webkit-text-decoration-skip: objects; /* 2 */ +} + +/** + * Remove the outline on focused links when they are also active or hovered + * in all browsers (opinionated). + */ + +a:active, +a:hover { + outline-width: 0; +} + +/** + * 1. Remove the bottom border in Firefox 39-. + * 2. Add the correct text decoration in Chrome, Edge, IE, Opera, and Safari. + */ + +abbr[title] { + border-bottom: none; /* 1 */ + text-decoration: underline; /* 2 */ + text-decoration: underline dotted; /* 2 */ +} + +/** + * Prevent the duplicate application of `bolder` by the next rule in Safari 6. + */ + +b, +strong { + font-weight: inherit; +} + +/** + * Add the correct font weight in Chrome, Edge, and Safari. + */ + +b, +strong { + font-weight: bolder; +} + +/** + * 1. Correct the inheritance and scaling of font size in all browsers. + * 2. Correct the odd `em` font sizing in all browsers. + */ + +code, +kbd, +samp { + font-family: monospace, monospace; /* 1 */ + font-size: 1em; /* 2 */ +} + +/** + * Add the correct font style in Android 4.3-. + */ + +dfn { + font-style: italic; +} + +/** + * Add the correct background and color in IE 9-. + */ + +mark { + background-color: #ff0; + color: #000; +} + +/** + * Add the correct font size in all browsers. + */ + +small { + font-size: 80%; +} + +/** + * Prevent `sub` and `sup` elements from affecting the line height in + * all browsers. + */ + +sub, +sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +sub { + bottom: -0.25em; +} + +sup { + top: -0.5em; +} + +/* Embedded content + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +audio, +video { + display: inline-block; +} + +/** + * Add the correct display in iOS 4-7. + */ + +audio:not([controls]) { + display: none; + height: 0; +} + +/** + * Remove the border on images inside links in IE 10-. + */ + +img { + border-style: none; +} + +/** + * Hide the overflow in IE. + */ + +svg:not(:root) { + overflow: hidden; +} + +/* Forms + ========================================================================== */ + +/** + * 1. Change the font styles in all browsers (opinionated). + * 2. Remove the margin in Firefox and Safari. + */ + +button, +input, +optgroup, +select, +textarea { + font-family: sans-serif; /* 1 */ + font-size: 100%; /* 1 */ + line-height: 1.15; /* 1 */ + margin: 0; /* 2 */ +} + +/** + * Show the overflow in IE. + * 1. Show the overflow in Edge. + */ + +button, +input { /* 1 */ + overflow: visible; +} + +/** + * Remove the inheritance of text transform in Edge, Firefox, and IE. + * 1. Remove the inheritance of text transform in Firefox. + */ + +button, +select { /* 1 */ + text-transform: none; +} + +/** + * 1. Prevent a WebKit bug where (2) destroys native `audio` and `video` + * controls in Android 4. + * 2. Correct the inability to style clickable types in iOS and Safari. + */ + +button, +html [type="button"], /* 1 */ +[type="reset"], +[type="submit"] { + -webkit-appearance: button; /* 2 */ +} + +/** + * Remove the inner border and padding in Firefox. + */ + +button::-moz-focus-inner, +[type="button"]::-moz-focus-inner, +[type="reset"]::-moz-focus-inner, +[type="submit"]::-moz-focus-inner { + border-style: none; + padding: 0; +} + +/** + * Restore the focus styles unset by the previous rule. + */ + +button:-moz-focusring, +[type="button"]:-moz-focusring, +[type="reset"]:-moz-focusring, +[type="submit"]:-moz-focusring { + outline: 1px dotted ButtonText; +} + +/** + * Change the border, margin, and padding in all browsers (opinionated). + */ + +fieldset { + border: 1px solid #c0c0c0; + margin: 0 2px; + padding: 0.35em 0.625em 0.75em; +} + +/** + * 1. Correct the text wrapping in Edge and IE. + * 2. Correct the color inheritance from `fieldset` elements in IE. + * 3. Remove the padding so developers are not caught out when they zero out + * `fieldset` elements in all browsers. + */ + +legend { + box-sizing: border-box; /* 1 */ + color: inherit; /* 2 */ + display: table; /* 1 */ + max-width: 100%; /* 1 */ + padding: 0; /* 3 */ + white-space: normal; /* 1 */ +} + +/** + * 1. Add the correct display in IE 9-. + * 2. Add the correct vertical alignment in Chrome, Firefox, and Opera. + */ + +progress { + display: inline-block; /* 1 */ + vertical-align: baseline; /* 2 */ +} + +/** + * Remove the default vertical scrollbar in IE. + */ + +textarea { + overflow: auto; +} + +/** + * 1. Add the correct box sizing in IE 10-. + * 2. Remove the padding in IE 10-. + */ + +[type="checkbox"], +[type="radio"] { + box-sizing: border-box; /* 1 */ + padding: 0; /* 2 */ +} + +/** + * Correct the cursor style of increment and decrement buttons in Chrome. + */ + +[type="number"]::-webkit-inner-spin-button, +[type="number"]::-webkit-outer-spin-button { + height: auto; +} + +/** + * 1. Correct the odd appearance in Chrome and Safari. + * 2. Correct the outline style in Safari. + */ + +[type="search"] { + -webkit-appearance: textfield; /* 1 */ + outline-offset: -2px; /* 2 */ +} + +/** + * Remove the inner padding and cancel buttons in Chrome and Safari on macOS. + */ + +[type="search"]::-webkit-search-cancel-button, +[type="search"]::-webkit-search-decoration { + -webkit-appearance: none; +} + +/** + * 1. Correct the inability to style clickable types in iOS and Safari. + * 2. Change font properties to `inherit` in Safari. + */ + +::-webkit-file-upload-button { + -webkit-appearance: button; /* 1 */ + font: inherit; /* 2 */ +} + +/* Interactive + ========================================================================== */ + +/* + * Add the correct display in IE 9-. + * 1. Add the correct display in Edge, IE, and Firefox. + */ + +details, /* 1 */ +menu { + display: block; +} + +/* + * Add the correct display in all browsers. + */ + +summary { + display: list-item; +} + +/* Scripting + ========================================================================== */ + +/** + * Add the correct display in IE 9-. + */ + +canvas { + display: inline-block; +} + +/** + * Add the correct display in IE. + */ + +template { + display: none; +} + +/* Hidden + ========================================================================== */ + +/** + * Add the correct display in IE 10-. + */ + +[hidden] { + display: none; +} diff --git a/system/author/css/style.css b/system/author/css/style.css index 26518aa..5d159c6 100644 --- a/system/author/css/style.css +++ b/system/author/css/style.css @@ -1,2311 +1,2311 @@ -/********************** -* TRANSITION * -**********************/ - -a, a:link, a:visited, a:focus, a:hover, a:active, button, .button, input, .control-group, .sidebar-menu, .sidebar-menu--content, .menu-action, .button-arrow{ - -webkit-transition: color 0.2s ease; - -moz-transition: color 0.2s ease; - -o-transition: color 0.2s ease; - -ms-transition: color 0.2s ease; - transition: color 0.2s ease; - -webkit-transition: background-color 0.2s ease; - -moz-transition: background-color 0.2s ease; - -o-transition: background-color 0.2s ease; - -ms-transition: background-color 0.2s ease; - transition: border-color 0.2s ease; - -webkit-transition: border-color 0.2s ease; - -moz-transition: border-color 0.2s ease; - -o-transition: border-color 0.2s ease; - -ms-transition: border-color 0.2s ease; - transition: border-color 0.2s ease; -} -.navi-item a, .navi-item.folder a i, .navi-item.file a i{ - -webkit-transition: all 0.1s ease; - -moz-transition: all 0.1s ease; - -o-transition: all 0.1s ease; - -ms-transition: all 0.1s ease; - transition: all 0.1s ease; -} -/******************** -* COMMONS * -********************/ - -html,body{ - padding: 0; - margin:0; -} -body{ - background: #f9f8f6; - color: #444; - font-size: 16px; -} -body,input,select,textarea{ - font-family: Helvetica, Calibri, Arial, sans-serif; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; -} -header, nav, h1, h2, h3, h4, h5, h6{ - font-family: arial, sans-serif; -} -h1{ font-size: 1.8em; margin: 1em 0 0.8em; font-weight: 700; } -h2{ font-size: 1.6em; margin: 1em 0 0.8em; font-weight: 700; } -h3{ font-size: 1em; margin: 0.6em 0 0.6em; text-transform: uppercase; font-weight: 300; } -.main{ - margin: 0px auto; - box-sizing:border-box; - padding: 20px; -} -section{ - background: #FFFFFF; - box-shadow: 0 0 4px #ddd; - position: relative; - margin-bottom: 40px; - padding: 20px 5px 40px; - box-sizing: border-box; -} -article{ - width: 100%; -} -aside.sidebar{ - display: block; - width: 100%; - background: #fff; - margin-bottom: 10px; - box-sizing: border-box; -} -.right{ - float: right; -} - -/******************** -* NAVI * -********************/ - -.main-header{ - display: inline-block; - width: 100%; - border-bottom: 1px solid #eee; -} -.header-navi, .main{ - margin: auto; - max-width: 1200px; -} -.header-navi .logo{ - display: inline-block; - font-size: 1.6em; - font-weight: 700; - width: 18%; - box-sizing: border-box; -} -.header-navi .logo a{ - color: #000; - text-decoration: none; - line-height: 2em; - margin-left: 10px; -} -.header-navi ul{ - display: inline-block; - width: 80%; - box-sizing: border-box; - text-align: right; - list-style: none; - padding:0px; - margin:0px; - font-size: 1.2em; -} -.header-navi li{ - display: inline-block; - padding: 0px; - margin: 0px; -} -.navi-items a{ - color: #000; - font-size: 0.9em; - text-transform: uppercase; - text-decoration: none; - margin: 0px 5px; - padding: 20px 0px 10px 0px; - line-height: 2em; - border-bottom: 3px solid transparent; -} -.navi-items a:hover, .navi-items a:focus, .navi-items a:active, .navi-items a.active{ - border-bottom: 3px solid #66b0a3; -} -.navi-items i{ - color: #ddd; -} -.navi-items span{ - display: none; -} - -/************************ -* MENU * -************************/ - -.sidebar-menu,.sidebar-menu--content{ - max-height: 40px; - padding: 0px 20px; - overflow: hidden; - box-sizing: border-box; - font-size: 0.9em; - text-align: center; -} -.sidebar-menu.expand{ - max-height: 500px; -} -.sidebar-menu--content.expand{ - max-height: 3000px; -} -.menu-action{ - display: block; - width: 100%; - height: 40px; - text-align: center; - line-height: 40px; - text-transform: uppercase; - font-weight: 300; - cursor: pointer; -} -ul.menu-list{ - list-style: none; - margin: 5px 0 30px; - padding:0; -} -li.menu-item{ - padding: 0; - margin: 8px 0; -} -.menu-item a, .menu-item a:link, .menu-item a:visited{ - background: #f9f8f6; - width: 100%; - box-sizing: border-box; - display: inline-block; - text-decoration: none; - color: #444; - border: 1px solid #eee; - border-radius: 3px; - line-height: 2.3em; -} -.menu-item a:hover, .menu-item a:focus, .menu-item a:active, .menu-item a.active{ - color: #fff; - background: #70c1b3; -} - -/******************** -* CONTENT-NAVI * -********************/ -.content-navi{ - background: transparent; - padding: 0; - position: relative; - text-align: left; -} -.navi-list{ - padding: 0px; -} -.navi-item{ - list-style: none; - padding: 0px; - position: relative; -} -.navi-item i.icon-doc-text, -.navi-item i.icon-folder-empty, -.navi-item i.icon-home, -.navi-item i.icon-plus{ - display: inline-block; - position: absolute; - top: 0px; - background: transparent; - color: #ccc; - padding: 7px 2px 7px; -} -.navi-item .status{ - position: absolute; - width: 4px; - height: 100%; - max-height: 32px; - left: -10px; - border-top: 1px solid #f9f8f6; - border-bottom: 1px solid #f9f8f6; -} -.status.published{ - background:#66b0a3; -} -.status.modified{ -/* background: #FFD700; */ - background: #FFA500; -} -.status.unpublished{ - background:#cc4146; -} -.navi-item i.icon-resize-full-alt, -.navi-item i.icon-move { - position: absolute; - right: 5px; - top: 7px; - color: #f9f8f6; - color: #444; - background: transparent; -} -.navi-item a{ - display: block; - padding: 7px 0; - width: 100%; - margin-bottom: 1px; - margin-top: 1px; - text-decoration: none; - color: #444; - background: transparent; -} -.navi-item.folder a{ - font-weight: 700; -} -.navi-item.file a{ - font-weight: 300; -} -.navi-item a:focus, .navi-item a:focus i, -.navi-item a:hover, .navi-item a:hover i, -.navi-item a.active, .navi-item a.active i{ - background:#66b0a3; - color: #fff; -} -[class^="level-"]{ - padding-left: 80px; - display: block; - box-sizing: border-box; -} -span.level-1{ padding-left: 30px; } -span.level-2{ padding-left: 40px; } -span.level-3{ padding-left: 50px; } -span.level-4{ padding-left: 60px; } -span.level-5{ padding-left: 70px; } - -.addNaviItem{ - padding: 5px; - display: block; -} -.addNaviItem .hide{ - height: 0px; - overflow: hidden; -} -.addNaviItem a, .addNaviItem a:link, .addNaviItem a:visited{ - padding: 3px 0; - margin: 0; - width: auto; - background: transparent; - color: #e0474c; -} -.addNaviItem a:focus,.addNaviItem a:hover,.addNaviItem a:active{ - text-decoration: underline; - background: transparent; - color: #e0474c; -} -.addNaviForm input{ - min-height: 0px; - width: 100%; - background: #fff; - padding: 7px; - outline: 1px solid #efefef; -} -.addNaviForm button{ - display: inline-block; - box-sizing: border-box; - margin: 5px 0 10px; - padding: 5px; - background:#66b0a3; - color: #f9f8f6; - width: 50%; - border: 0px; - border-radius: 2px; -} -.addNaviForm button.fullWidth{ - width: 100%; -} -.addNaviForm button:hover{ - background: #4D978A; -} -.addNaviForm button.b-left{ - border-right: 1px solid #f9f8f6; -} -.addNaviForm button.b-right{ - border-left: 1px solid #f9f8f6; -} -.fade-enter-active { - transition: opacity 0.25s ease-out; -} -.fade-leave-active{ - transition: opacity 0.1s ease-out; -} -.fade-enter, .fade-leave-to { - opacity: 0; -} - -/******************** -* CONTENT * -********************/ - -header h1, header h2{ - display: inline-block; -} -header input[type="submit"]{ - margin-top: 1.5em; - margin-right: 10px; - float: right; - width: 150px; - box-sizing:border-box; - border: 0px; -} -h1 .version-number{ - text-transform: uppercase; - font-size: 0.5em; - font-weight: 300; -} -footer{ - text-align: center; - padding: 20px 0; -} -.math{ - white-space: pre; -} -/******************** -* SETUP FORM * -********************/ -.authformWrapper{ - width: 100%; - background: #fff; - padding: 40px; - box-shadow: 0px 0px 10px rgba(0,0,0,0.2); - border-radius: 3px; - box-sizing: border-box; -} -.setupContent{ - width: 100%; - text-align: center; -} -.setupWrapper{ - margin-top: 8%; - max-width: 350px; - margin-left: auto; - margin-right: auto; -} -.setupWrapper label{ - font-weight: 700; -} -.setupWrapper input[type="submit"] -{ - border-radius: 0px; - margin-bottom: 0px; -} -.setupWrapper input[type="submit"]:disabled{ - cursor: default; - background: #f9f8f6; - border: 2px solid #f9f8f6; -} -.setupWrapper .formElement{ - margin: 0 0 20px 0; -} - - -/************************ -* OPEN-CLOSE BUTTON * -************************/ - -.button-arrow{ - width: 0; - height: 0; - border-left: 5px solid transparent; - border-right: 5px solid transparent; - border-bottom: 5px solid transparent; - border-top: 5px solid #000; - display:inline-block; - margin-left: 5px; -} -.active .button-arrow{ - border-top: 5px solid #fff; -} -.expand .button-arrow{ - border-bottom: 5px solid #000; - border-top: 0px solid transparent; - margin-bottom: 5px; -} -.expand.active .button-arrow{ - border-bottom: 5px solid #fff; -} - -/************* -** MODAL ** -*************/ - -.modal, .modalWindow{ - position:fixed; - display: block; - z-index: 9999; - width: 100%; - height: 100%; - padding: 20px; - box-sizing: border-box; - top: 0; - bottom: 0; - left: 0; - right: 0; - background: #FFF; - background: rgba(255,255,255,0.9); -} -.modal{ - display: none; -} -.modal.show{ - display: block; -} -.modalInner{ - position: relative; - max-width: 350px; - margin: 10% auto; - background: #FFF; - padding: 50px 50px 20px 50px; - border-radius: 3px; - box-shadow: 0px 0px 10px rgba(0,0,0,0.2); -} -.modalInner h2{ - margin-top: 0px; -} -.modalInner.wide{ - max-width: 500px; -} -.closeModal{ - position: absolute; - top: 0px; - right: 0px; - font-weight: 700; - padding: 0.6em; - cursor: pointer; -} -.closeModal:focus, .closeModal:hover{ - background: #f9f8f6; -} -.modal .actionLink{ - display: block; - width: 100%; - text-align: center; - margin-top: -20px; - margin-bottom: 20px; -} -a.openModal:link,a.openModal:active,a.openModal:visited{ - color: #cc4146; - padding: 0px 20px 20px; - text-decoration: none; - display: block; - margin: auto; - text-align: center; - width: 100px; -} -a.openModal:focus,a.openModal:hover{ - text-decoration: underline; -} - -/***************** -** USER-LIST ** -*****************/ - -ul.userlist{ - padding: 0; - margin: 10px 20px 30px; - list-style: none; -} -li.row{ - display: inline-block; - width: 100%; - box-sizing: border-box; -} -li.row ul{ - background: #f9f8f6; - margin: 5px 0; - padding: 0px; - width: 100%; -} -li.col{ - display: inline-block; - padding: 15px 10px; - box-sizing: border-box; - margin: 0px; -} -li.col.username, li.col.email, li.col.userrole, li.col.edit{ - border: 1px solid #fff; -} -li.col.username{ - border-top: 2px solid #70c1b3; -} -.col.username,.col.email,.col.userrole, .col.edit{ - width: 100%; -} -.userlist a, .userlist a:link, .userlist a:visited{ - color: #e0474c; - text-decoration: none; -} -.userlist a:hover, .userlist a:focus, .userlist a:active{ - text-decoration: underline; -} - -/********************* -** WELCOME PAGE ** -*********************/ - -.formWrapper, .welcome{ - display: block; - position: relative; - max-width: 900px; - margin: auto; -} -.welcome{ - padding: 30px 50px; - box-sizing: border-box; - background: #FFFFFF; - box-shadow: 0px 0px 10px rgba(0,0,0,0.2); -} -.welcome .welcomeIntro a, .welcome .welcomeCard a:link, .welcome .welcomeCard a:visited{ - text-decoration: none; - color: #e0474c; -} -.welcome .welcomeIntro a:hover, .welcome .welcomeCard a:focus, .welcome .welcomeCard a:active{ - text-decoration: underline; -} -.welcome p{ - line-height: 1.5em; - padding: 0px; -} -.welcome a.button{ - margin: 40px auto; - display: block; - width: 200px; - text-align: center; - padding: 10px; - border-radius: 3px; - text-decoration: none; -} -.welcome .welcomeCard{ - position: relative; - margin-top: -1px; - border-top: 1px solid #efece7; - border-bottom: 1px solid #efece7; - width: 100%; -} -.welcome .welcomeCard h3{ - font-weight: 700; -} -.welcome .welcomeCard a, .welcome .welcomeCard a:link, .welcome .welcomeCard a:visited{ - color: #444; - text-decoration: none; - width: 100%; -} -.welcome .welcomeCard a:hover, .welcome .welcomeCard a:focus, .welcome .welcomeCard a:active{ - color: #e0474c; -} -.welcome .welcomeCard a:after{ - content: ''; - display: block; - position: absolute; - left: 100%; - top: 20px; - bottom: 0; - width: 0; - height: 0; - border-top: 50px solid transparent; - border-right: 0px solid transparent; - border-bottom: 50px solid transparent; - border-left: 20px solid #efece7; -} -.welcome .welcomeCard a:hover:after{ - border-left: 20px solid #e0474c; -} -.welcome .welcomeInner{ - margin-top: 20px; - width: 90%; - height: 120px; -} - -header.headline -{ - padding: 0px 0px; -} - -/******************** -* Forms * -********************/ - -.large, .medium, .small{ - padding: 18px 0px; - box-sizing:border-box; - vertical-align: top; -} -form .large, form .medium, form .small{ - width: 100%; - display: block; -} -.large img{ - width: 100%; -} -fieldset{ - display: block; - position: relative; - width: 100%; - margin: 0; - box-sizing: border-box; - border: none; - padding: 0; -} -label,.label{ - display: inline-block; - width: 100%; - font-size: 0.75em; - font-weight: 300; - padding: 0px; - line-height: 1.5em; - text-transform: uppercase; -} -input, select, textarea{ - display: inline-block; - box-shadow: none; - width: 100%; - font-size: 1em; - box-sizing: border-box; - min-height: 42px; - border: 1px solid #f9f8f6; - background: #f9f8f6; -} -input:focus, select:focus, textarea:focus{ - border: 1px solid #FFF; - outline: none; - box-shadow: 0 0 2px #000; -} -input, textarea{ - padding: 10px; -} -textarea{ - min-height: 84px; -} -select{ - padding: 14px 8px; -} -button,input[type="submit"]:hover{ - cursor: pointer; -} -input[type="submit"]{ - border-radius: 3px; - margin-bottom: 40px; - color: #f9f8f6; - border: 2px solid #70c1b3; - background: #70c1b3; -} -input[type="submit"]:hover{ - border: 2px solid #66b0a3; - background: #66b0a3; -} -input, select, button[type="button"]{ - min-height: 52px; -} -button[type="button"]{ - background: #f9f8f6; - color: #444; - border-top: 1px solid #ddd; - width: 100%; - text-align: center; -} -button[type="button"].active{ - background: #70c1b3; - color: #f9f8f6; - border-top: 1px solid #70c1b3; -} -button[type="button"].no-settings:hover{ - cursor: default; -} -button[type="button"].has-settings:hover{ - background: #efece7; -} -button[type="button"].has-settings.active:hover{ - background: #66b0a3; - border-top: 1px solid #66b0a3; -} -button[type="button"].plugin-button{ - border-left: 0px; - border-right: 0px; - border-bottom: 0px; - margin-top: 10px; - padding: 15px 0; -} -button[type="button"].theme-button{ - border: 0px; - margin: 5px 0 20px; - padding: 12px; - border-radius: 3px; - min-height: 52px; - background: #FFF; -} -button[type="button"].theme-button.active{ - background: #70c1b3; - color: #f9f8f6; -} -button[type="button"].theme-button:hover{ - border:0px; - border-top: 0px; -} -.button{ - color: #fff; - padding: 10px; - border-radius: 3px; -} -a.button, a.button:link,a.button:visited{ - text-decoration: none; - color: #fff; - border: 2px solid #70c1b3; - background: #70c1b3; -} -a.button:focus, a.button:hover, a.button:active{ - border: 2px solid #66b0a3; - background: #66b0a3; -} - -input[type="submit"].danger{ - border: 2px solid #e0474c; - background: #e0474c; -} -input[type="submit"].danger:hover{ -border: 2px solid #cc4146; -background: #cc4146; -} - - -.settings .medium a.button{ - width: 100%; - display: block; - box-sizing:border-box; - text-align: center; -} - -/***************** -** CARDS ** -*****************/ - -fieldset.card{ - width: 100%; - display: inline-block; - padding: 15px 0px; - box-sizing: border-box; - vertical-align: top; -} -.card .medium{ - padding: 0px 20px; -} -.card img{ - width: 100%; - border: 1px solid #f9f8f6; - background: #f9f8f6; -} -.cardInner{ - box-sizing: border-box; - position: relative; - border: 1px solid #ddd; - background-color: #f9f8f6; -} -.cardInner .button-box{ - margin: 20px; -} -.cardHead h2{ - padding: 5px 0px; - margin-bottom: 0px; -} -.cardHead header, .cardHead p, .cardHead ul{ - padding: 5px 0px; -} -.cardHead .cardActive{ - float: right; -} -.cardHead header{ - width: 100%; - height: 52px; - display: inline-block; - box-sizing: border-box; - padding: 20px; - background: #f9f8f6; - color: #444; -} -.cardHead header legend{ - font-size: 1.2em; - font-weight: 700; - float:left; -} -.cardHead header label{ - display: inline-block; - width: auto; - line-height: 20px; - margin: 0; - padding: 0 25px 0 0; -} -.cardHead header .control-group .checkmark{ - right:-5px; - left: auto; -} -.cardHead header input{ - width: auto; - min-height: 0px; - padding: 0; - margin: 0; - vertical-align: middle; -} -.cardContent{ - padding: 20px 40px 0; -} -.cardDescription{ - line-height: 1.5em; -} -.cardDescPlugin{ - padding: 0 20px; -} -ul.cardInfo{ - font-size: 0.8em; - padding: 0px; -} -.cardInfo a, .cardInfo a:link,.cardInfo a:visited{ - color: #70c1b3; - text-decoration: none; -} -.cardInfo a:focus,.cardInfo a:hover,.cardInfo a:active{ - text-decoration: underline; -} -.cardInfo li{ - display: inline-block; - box-sizing: border-box; - vertical-align: top; - padding: 0px 5px 0 0; - margin: 0px 5px 0 0; - color: grey; - border-right: 1px solid grey; -} -.cardInfo li:last-child{ - border-right: 0px; -} -.cardFields{ - max-height: 0; - transition: max-height 0.5s ease-out; - overflow: hidden; - padding: 0px 40px; - border: 0px; -} -.cardFields.open{ - max-height: 2800px; - transition: max-height 0.5s ease-in; - overflow: hidden; - border: 1px; -} -.cardFields .cardField:first-child{ - margin-top: 50px; -} -.cardFields input, .cardFields select, .cardFields textarea, .cardFields .onoffswitch{ - margin-bottom: 12px; -} -.cardFields label{ - margin-top: 12px; -} -.cardFields fieldset.subfield legend{ - float:none; - font-weight: bold; - font-size: 1.2em; -} -.cardField{ - position: relative; - width: 100%; - display: inline-block; - margin: 10px 0; -} -.cardField label{ - width: 100%; - margin-top: 0px; - margin-left: 0px; - padding: 0px; - background: transparent; -} -.cardField label.control-group{ - padding-left: 30px; - margin: 10px 0 0; -} -.cardField input, .cardField select, .cardField textarea{ - background-color: #FFF; -} -.cardFields fieldset.subfield{ - padding: 20px; - border: 1px solid #ddd; - margin: 20px 0 30px; -} - -/******************** -* ALERT/ERRROR * -********************/ - -.alert{ - color: #fff; - width: 100%; - line-height: 20px; - display: block; - margin:0px; - text-align: center; - padding: 5px 0px; -} -.alert-info{ - background: #70c1b3; -} -.alert-error{ - background: #e0474c; -} - -.error input, -.error select, -.error textarea{ - border: 1px solid #e0474c; -} -.error, -.error label, -.error small, -.error p{ - color: #e0474c; -} -.errors .cardHead header{ - background: #e0474c; - color: #fff; -} -span.error{ - font-size: 0.8em; - line-height: 1em; -} -.cardInner span.error{ - display: block; - margin-top: -8px; - margin-bottom: 8px; -} - - -/******************** -* UPDATE-BANNER * -********************/ - -.update-banner{ - display: none; - position: absolute; - top: 0; - left: 0; - width: 0; - height: 0; - border-top: 100px solid #e0474c; - border-bottom: 100px solid transparent; - border-right: 100px solid transparent; -} -.update-banner.show-banner{ - display: block; -} -.update-banner span{ - position: absolute; - top: -85px; - left: -20px; - text-align: center; - font-size: 0.9em; - transform: rotate(-45deg); - display: block; - color: #fff; - width: 100px; -} -.update-banner a{ - color: #fff; - text-decoration: none; -} - -/**************************************** -* SELECT BUTTON * -* https://codepen.io/vkjgr/pen/VYMeXp * -****************************************/ - -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, #f9f8f6, #f9f8f6); - 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; -} -.cardField select{ - background-image: - linear-gradient(45deg, transparent 50%, #444 50%), - linear-gradient(135deg, #444 50%, transparent 50%), - linear-gradient(to right, #fff, #fff); -} -/******************** -* COLOR PICKER * -********************/ - -.color-box{ - width: 100%; - height: 36px; - margin-top: 5px; - margin-bottom: 12px; - display: inline-block; - border: 1px solid #ddd; -} -.color-box:hover{ - cursor: pointer; -} -.color-picker-control{ - width: 172px; -} -.color-picker-control input{ - margin-top: 0px; -} - -/************************ -* CHECKBOX / RADIO * -************************/ - -.control-group input { - position: absolute; - opacity: 0; - cursor: pointer; - width: 0; - height: 0; - padding: 0; - margin: 0; - font-size: 0.1em; - min-height: 0px; -} -.control-group { - text-transform: none; - display: block; - position: relative; - padding-left: 30px; - margin-bottom: 7px; - margin-top: 5px; - cursor: pointer; - font-size: 1em; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; -} -.checkmark { - position: absolute; - top: 0; - left: 0; - height: 16px; - width: 16px; - border: 2px solid #ccc; - background-color: #fff; - border-radius: 2px; -} -.checkmark:after { - content: ""; - position: absolute; - display: none; -} -.control-group .checkmark:after { - left: 5px; - top: 0px; - width: 5px; - height: 10px; - border: solid #fff; - border-width: 0 2px 2px 0; - -webkit-transform: rotate(45deg); - -ms-transform: rotate(45deg); - transform: rotate(45deg); -} -.radiomark { - position: absolute; - top: 0; - left: 0; - height: 16px; - width: 16px; - border: 2px solid #ccc; - background-color: #fff; - border-radius: 50%; -} -.radiomark:after { - content: ""; - position: absolute; - display: none; -} -.control-group .radiomark:after { - top: 5px; - left: 5px; - width: 6px; - height: 6px; - background: #fff; - border-radius: 50%; -} -.control-group:hover input ~ .checkmark, -.control-group:hover input ~ .radiomark { - background-color: #ccc; - border: 2px solid #ccc; -} -.control-group input:checked ~ .checkmark, -.control-group input:checked ~ .radiomark { - border: 2px solid #70c1b3; - background-color: #70c1b3; -} -.control-group input:checked ~ .checkmark:after, -.control-group input:checked ~ .radiomark:after { - display: block; -} -.control-group input:disabled ~ .checkmark, -.control-group input:disabled ~ .radiomark { - border: 2px solid #eee; - background-color: #eee; -} - -/******************** -* TOOLTIP * -********************/ - -label .help, .label .help{ - float:right; - width: 15px; - height: 15px; - line-height: 15px; - border-radius: 50%; - text-align: center; - background: #fff; - cursor: pointer; -} - -.help { - position: relative; - display: inline-block; - border-bottom: 0px dotted black; -} -.help .tooltip { - visibility: hidden; - width: 200px; - top: -5px; - right: 25px; - text-align: center; - padding: 5px 0; - border-radius: 6px; - color: #fff; - background-color: #555; - - position: absolute; - z-index: 1; - opacity: 0; - transition: opacity .6s; - - text-transform: none; -} -.help .tooltip:after{ - content: ""; - position: absolute; - top: 15px; - left: 100%; - margin-top: -5px; - border-width: 5px; - border-color: transparent transparent transparent #999; - border-style: solid; -} -.help:hover .tooltip{ - visibility: visible; - opacity: 1; -} - -/******************** -* LOGIN * -********************/ - -.loginarea{ - position: relative; - text-align: center; - width: 100%; -} -.loginbutton{ - height: 34px; -} -.loginarea .counter{ - position: absolute; - width: 100%; - top: 16px; - color: #444; -} -.loginarea .forgotpw{ - margin-top: 20px; -} -.loginarea .forgotpw a,.loginarea .forgotpw a:link,.loginarea .forgotpw a:visited{ - font-size: 0.8em; - text-decoration: none; - color: #cc4146; -} -.loginarea .forgotpw a:hover,.loginarea .forgotpw a:focus,.loginarea .forgotpw a:active{ - text-decoration: underline; -} - -/******************** -* EDITOR * -********************/ - -.editor .large{ - position: relative; -} -.editor input[name="title"]{ - font-size: 1.8em; - padding: 10px 20px; -} -.editor textarea{ - font-size: 1em; - padding: 20px; - line-height: 1.4em; -} -.editor span.error{ - position: absolute; - left:20px; - bottom: 0px; -} -.editor .message{ - display: inline-block; - width: 70%; -} -.editor .message span.error{ - position: relative; - left: 0; - width: 100%; - padding: 15px 20px; - color: #e0474c; -} -.buttonset{ - position: fixed; - display: block; - bottom: 0; - left: 0; - right: 0; - box-sizing: border-box; - padding: 2px; - max-width: 900px; - background: #fff; - box-shadow: 0 0 4px #ddd; - z-index: 99; -} -.buttonset .message.error{ - position: absolute; - background: #e0474c; - color: #fff; - font-size: 0.9em; - width: 100%; - left: 0; - top: -25px; - padding: 5px 40px; - box-sizing: border-box; - box-shadow: 0 0 2px #ddd; -} - -.editor button, .editor a{ - position: relative; - display: inline-block; - min-width: 40px; - margin: 4px 2px; - padding: 10px; - border-radius: 0px; - font-size: 0.8em; - text-align: center; - text-decoration: none; - box-sizing: border-box; -} -.editor .secondary{ - display: inline-block; - float: right; -} -.editor button.danger:enabled, -.editor button.danger[enabled], -.editor a.button--secondary, -.editor a.button--secondary:visited{ - border: 1px solid #eee; - background: #fff; - color: #444; -} -.editor button:enabled, -.editor button[enabled], -.editor a.button--secondary:focus, -.editor a.button--secondary:hover, -.editor a.button--secondary:active{ - border: 1px solid #66b0a3; - background: #66b0a3; - color: #fff; -} -.editor button:enabled:hover, -.editor button[enabled]:hover{ - border: 1px solid #4D978A; - background: #4D978A; - color: #fff; -} -.editor button.danger:enabled:hover, -.editor button.danger[enabled]:hover{ - border: 1px solid #e0474c; - background:#e0474c; - color: #fff; -} -.editor button:disabled, -.editor button[disabled]{ - border: 1px solid #eee; - background: #eee; - color: #444; - cursor: default; -} - -[v-cloak]{ - display: none; -} -.mobile{ - display: block; -} -.desktop{ - display: none; -} - -/**************** -** BLOX EDITOR ** -****************/ - -.blox-body{ - position: relative; - padding: 18px 20px -} -.blox-overlay{ - position:absolute; - display: block; - z-index: 10; - box-sizing: border-box; - top: 0; - bottom: 0; - left: 0; - right: 0; - background: #FFF; - background: rgba(255,255,255,0.8); -} -.blox{ - padding: 1px 20px; - line-height: 1.5em; - font-size: 16px; -} -.blox p{ - margin-top: 12px; - margin-bottom: 12px; -} -.blox:hover{ - background: #f9f8f6; -} -.blox-editor{ - position: relative; -} -.blox-buttons{ - position: absolute; - bottom: -15px; - text-align: right; - right: 25px; - width: 200px; - z-index: 99; -} -.blox-buttons button{ - display: inline-block; - box-sizing: border-box; - margin: 2px; - padding: 3px 6px; - width: 80px; - text-align: center; - color: #444; - background: #f9f8f6; - border: 2px solid #fff; - border-radius: 2px; - font-size: 0.9em; -} -.blox-buttons button.edit:hover{ - background: #70c1b3; - color: #eee; -} -.blox-buttons button.cancel:hover{ - background: #e0474c; - color: #eee; -} -.blox-buttons button.edit:disabled, .blox-buttons button.cancel:disabled{ - background: #eee; - color: #444; - border: 1px solid #eee; -} - - -.sideaction{ - position: absolute; - top: 0px; - font-size: 0.8em; - right: -22px; -} -.sideaction button{ - display: block; - font-weight: 300; - font-size: 0.9em; - background: #fff; - color: #fff; - width: 20px; - height: 20px; - line-height: 20px; - text-align: center; - padding: 0px; - margin: 1px; - border: 0px; - border-radius: 1px; -} -.blox-wrapper{ - position: relative; -} -.editactive .sideaction button, -.blox-wrapper:hover button.add, -.blox-wrapper:hover button.delete{ - background-color: #f9f8f6; - color: #666; -} -.sideaction:hover ~ .background-helper { - background-color: #f9f8f6; -} -.editactive .background-helper{ - background-color: transparent!important; -} -.blox-wrapper button.add:hover{ - background: #66b0a3; - color: #fff; -} -.blox-wrapper button.delete:hover{ - background: #e0474c; - color: #fff; -} - - - -.blox-editor textarea{ - font-family: arial; - line-height: 1.5em; - font-size: 16px; - padding-left: 20px; - padding-right: 20px; - box-sizing: border-box; - min-height: 40px; -} -.blox-editor textarea:focus, .blox-editor input:focus{ - box-shadow: none; - outline: none; -} -.blox-editor input.mdcontent.h2,.blox-editor input.mdcontent.h3,.blox-editor input.mdcontent.h4,.blox-editor input.mdcontent.h5,.blox-editor input.mdcontent.h6{ - padding-left: 35px; -} -.blox-editor input.mdcontent.h2{ - font-size: 1.6em; - font-weight: 700; -} -.blox-editor input.mdcontent.h3{ - font-size: 1.3em; - font-weight: 700; -} -.blox-editor input.mdcontent.h4{ - font-size: 1.1em; - font-weight: 700; -} -.blox-editor input.mdcontent.h5{ - font-size: 1em; - font-weight: 700; -} -.blox-editor input.mdcontent.h6{ - font-size: 1em; - font-weight: 300; - font-style: italic; -} -button.hdown{ - position: absolute; - padding: 8px; - top: 1px; - bottom: 1px; - left: 0px; - font-size: 1em; - font-weight: 700; - background: #f9f8f6; - border: 0px solid #fff; - border-right: 1px solid #fff; - color: #66b0a3; -} -button.hdown:hover,button.hdown:focus,button.hdown:active{ - color: #f9f8f6; - background: #66b0a3; -} - -.blox-editor .contenttype { - position: absolute; - top: 15px; - left: -25px; - color: #666; -} -.visible{ - display: block; -} -.hidden{ - visibility: hidden; -} -.hidden .blox:hover{ - background: #fff; -} -.component{ - position: absolute; - width: 100%; - z-index:9; -} - - -/* .format-bar at the bottom of the page */ -.format-bar .hidden{ - display: none; -} -.format-bar .component{ - position: relative; -} -.format-bar{ - padding: 20px; - width:100%; - box-sizing: border-box; -} -.format-bar .editactive{ - position: relative; - margin-left: -20px; - margin-right: 20px; -} -.format-bar.blox{ - width: auto; - background: #f9f8f6; -} -.newblock{ - z-index: 20; -} -.newblock .sideaction{ - display: none; -} -.newblock-info{ - padding: 0 0 0 20px; - font-size: 0.9em; - line-height: 30px; - background: #66b0a1; - margin-bottom: 2px; - color: #fff; -} -.newblock-close{ - line-height: 30px; - border: 0px; - color: #fff; - background: #e0474c; - float: right; -} -/* format line for each block */ -.formatbuttons{ - margin: 5px 0px 2px; - font-size: 0.7em; - text-align: center; -} -.formatbuttons.hidden{ - display: none; -} -.formatbuttons button.format-item { - width: 30px; - height: 30px; -} -.fade-editor-enter-active{ - transition: opacity .5s; -} -.fade-editor-enter{ - opacity: 0.3; -} -button.format-item{ - margin: 2px 0; - padding: 5px; - background: #f9f8f6; - border: 1px solid #eee; - color: #444; - display: inline; - border-radius: 2px; - width: 40px; - height: 40px; - text-align: center; -} -button.format-item:hover{ - background: #66b0a3; - border: 1px solid #66b0a3; - color: #fff; - cursor: pointer; -} -button.format-item.disabled, button.format-item.close{ - width: 90px; -} -button.format-item.disabled{ - background: #f9f8f6; - color: #444; - border: 1px solid #eee; - cursor: initial; -} -button.format-item.close:hover{ - background: #cc4146; - border: 1px solid #cc4146; -} - -/************************ -** BLOX EDITOR CONTENT ** -************************/ - -.blox h1, .blox h2, .blox h3, .blox h4, .blox h5, .blox h6{ font-weight: 700; line-height: 1em; } -.blox h1{ font-size: 2.2em; margin: 0.6em 0 0.6em; } -.blox h2{ font-size: 1.6em; margin: 1.3em 0 0.6em; } -.blox h3{ font-size: 1.3em; text-transform: none; margin: 1.2em 0 0.6em; } -.blox h4{ font-size: 1.1em; margin: 1.2em 0 0.6em; } -.blox h5{ font-size: 1em; margin: 1.2em 0 0.6em; } -.blox h6{ font-size: 1em; font-style: italic; font-weight:300; margin: 1em 0 0.6em; } -.title input{ - font-size: 2.2em; - font-weight: 700; - padding: 20px; -} -.blox pre,.blox code{ - white-space: pre; - color: #333; - background: #f9f8f6; -} -.blox code{ - display: inline-block; - padding: 0 0.5em; - font-size: 0.8em; - line-height: 1.4em; - border-radius: 3px; -} -.blox code.hljs{ - background: transparent; -} -.blox pre{ - padding: 10px; - display: block; - max-width: 100%; - overflow-x: auto; - border-left: 4px solid #e0474c; -} -.blox table{ - width: 100%; - border-collapse: collapse; -} -.blox thead{ - border-bottom: 1px solid #e0474c; - border-top: 1px solid #e0474c; - background: #f9f8f6; - font-weight: 700; -} -.blox tbody{} -.blox th{ padding: 10px 0;} -.blox tr,.blox-editor tr{} -.blox tr:nth-child{ } -.blox tr:nth-child(even){ background-color:#f9f8f6; } -.blox td{ padding: 5px;} - -.blox-editor table{ - display: inline-table; - width: 100%; - border-collapse: collapse; - margin-bottom: 20px; -} -.blox-editor thead{} -.blox-editor tbody{} -.blox-editor tr{} -.blox-editor th{ border: 1px solid #ccc; padding: 5px; } -.blox-editor td{ border: 1px solid #ccc; padding: 5px; } -.blox-editor td.noteditable, .blox-editor th.noteditable{ - border: 1px solid #ccc; - text-align:center; - color: #ccc; - background: #f9f8f6; - font-weight: 300; - padding: 0; -} -.blox-editor .columnaction, .blox-editor .rowaction{ - position: absolute; - background: #fff; - width: 150px; - font-size: 0.9em; - color: #000; - text-align: left; - box-shadow: 0 0 2px #000; - margin: 5px; - z-index:999999; -} -.blox-editor .rowaction{ - margin-left: 53px; - margin-top: -3px; -} -.blox-editor .actionline{ - padding: 5px 10px; - cursor: pointer; -} -.blox-editor .actionline:hover{ - background: #70c1b3; - color: #fff; -} -.blox dl{ - border-top: 1px solid #e0474c; - border-bottom: 1px solid #e0474c; - padding: 0.5em 0; - box-sizing: border-box; -} -.blox dt, .blox dd{ - width: 100%; - margin: 0; - padding: 3px 5px; - box-sizing: border-box; - display: inline-block; - vertical-align: top; -} -.blox dt{ - font-weight: 700; -} -.blox dt::after{ - content: ":"; -} -.blox dd{ - padding-left: 40px; -} -.definitionList{ - background: #f9f8f6; -} -.definitionRow{ - width: 100%; - display: block; - padding: 20px 20px 0 20px; - box-sizing: border-box; -} -.definitionRow .icon-colon{ - display: inline-block; - margin-top: 15px; -} -input.definitionTerm{ - width: 29%; - display: inline-block; - vertical-align: top; - background: #fff; -} -textarea.definitionDescription{ - width: 60%; - display: inline-block; - vertical-align: top; - background: #fff; -} -button.addDL, -button.delDL{ - display: inline-block; - vertical-align: top; - font-weight: 300; - font-size: 0.8em; - height: 20px; - line-height: 20px; - border: 0px; - background: transparent; -} -button.addDL i, -button.delDL i{ - width: 20px; - border-radius: 1px; - text-align: center; - color:#f9f8f6; - padding: 2px 0 0; - line-height: 20px; - height: 20px; -} -button.addDL{ - margin: 20px; -} -button.addDL i{ - background: #70c1b3; -} -button.addDL i:hover{ - background:#66b0a3; -} -button.delDL i{ - background: #e0474c; -} -button.delDL i:hover{ - background: #cc4146; -} -i.icon-resize-vertical{ - color: #ddd; -} -.blox ul, .blox ol{ - padding-left: 0px; - margin-left: 18px; -} -.blox blockquote{ - border-left: 4px solid #e0474c; - background: #f9f8f6; - position: relative; - font-style: italic; - font-family: serif; - border-left: 4px solid #e0474c; - background: #f9f8f6; - margin: 12px 0; - padding: 5px; -} -.blox blockquote:before { - position: absolute; - left: 0px; - top: 20px; - color: #ccc; - content: open-quote; - font-size: 4em; -} -.blox blockquote p{ - margin-left: 50px; -} -.dropbox{ - min-height: 70px; - background: #f9f8f6; - padding: 0px; - box-sizing: border-box; - margin-bottom: 10px; -} -.dropbox p{ - position: relative; - cursor: pointer; - line-height: 70px; - width: 100%; - text-align: center; - box-sizing:border-box; - padding: 0; - margin: 0; -} -.dropbox input, .dropbox select{ - background-color: #fff; - width: 80%; - margin: 2px 0; - display: inline-block; -} -.video input{ - width: 75%; - margin: 15px 0; -} -.dropbox select{ - background-image: linear-gradient(45deg, transparent 50%, #444 50%), linear-gradient(135deg, #444 50%, transparent 50%), linear-gradient(to right, #fff, #fff); -} -.dropbox label{ - width: 20%; - display: inline-block; -} -.video label{ - text-align: right; - padding-right: 10px; - box-sizing: border-box; -} -.dropbox .imgmeta{ - padding: 30px; - box-sizing: border-box; -} -.dropbox .input-file{ - opacity: 0; - width: 100%; - height: 70px; - position: absolute; - cursor: pointer; - z-index: 1; -} -.blox img, img.uploadPreview{ - display: block; - margin: auto; - max-width: 100%; -} -.blox img.youtube{ - position: relative; -} -.blox .video-container{ - position: relative; - text-align: center; -} -.blox button.play-video { - position: absolute; - top: 50%; - margin-top: -50px; - margin-left: -50px; - height: 100px; - width: 100px; - background: #e0474c; - color: #FFFFFF; - border-radius: 50%; - border: 0px; - padding: 0; - text-align: center; -} -.blox button.play-video:hover { - background: #cc4146; -} -.blox button.play-video::after { - position: absolute; - top: 50%; - margin: -20px 0 0 -15px; - height: 0; - width: 0; - border-style: solid; - border-width: 20px 0 20px 40px; - border-color: transparent transparent transparent rgba(255, 255, 255, 0.75); - content: ' '; -} -sup{} -cite{} -abbr{} -hr{ - background: #ddd; - height: 2px; - margin: 20px 0; - border: 0px; -} -.blox ul.TOC,.blox .TOC ul{ - list-style: none; - padding-left: 0px; - margin-left: 0px; -} -.blox .TOC li:before{ - content: "\2192"; - margin-left: -7px; - margin-right: 7px; -} -.blox .TOC li.h1:before{ - content: ""; -} -.blox ul.TOC{ - background: #f9f8f6; - width: 100%; - padding: 20px; - box-sizing:border-box; -} -.blox li.h1{ - font-weight: 700; -} -.blox li.h2,.blox li.h3,.blox li.h4,.blox li.h5,.blox li.h6{ - font-weight: 400; - padding-left: 25px; -} -.blox a, .blox a:link, .blox a:visited, -footer a, footer a:link, footer a:visited -{ - text-decoration: none; - color: #e0474c; -} -.blox a:focus, .blox a:hover, .blox a:active, -footer a:focus, footer a:hover, footer a:active -{ - text-decoration: underline; -} -.setupContent a, .setupContent a:link, .setupContent a:visited -{ - text-decoration: none; - color: #444; -} -.setupContent a:focus, .setupContent a:hover, .setupContent a:active -{ - color: #e0474c; -} -.blox .TOC li:before{ color: #bbb; } - - -@media only screen and (min-width: 600px) { - section{ - padding: 20px 20px 40px; - } - header.headline{ - padding: 0px 20px; - } - .large, .medium, .small{ - padding: 18px 20px; - } - form .medium{ - width: 49.5%; - display: inline-block; - } - form .small{ - width: 33.3%; - display: inline-block; - } - fieldset.card{ - padding: 15px 20px; - } - .settings .medium a.button{ - display: inline-block; - width: auto; - } - .editor button, .editor a.button--secondary{ - min-width: 150px; - font-size: 0.9em; - } - - .blox-editor .loadwrapper{ - display: block; - position: relative; - width: 40px; - height: 40px; - margin: auto; - text-align: center; - } - .blox-editor .loadoverlay{ - display: block; - position: absolute; - z-index: 999; - left: 0px; - right: 0px; - top: 0px; - bottom: 0px; - background: rgba(255,255,255,0.4); - } - /* load design editor button */ - .editor button.load:after, .blox-editor .load:after, - .editor button.success:after, - .editor button.fail:after{ - position: absolute; - right: 8px; - top: 6px; - width: 8px; - height: 8px; - border-radius: 50%; - content: ''; - } - .editor button.load:after, .blox-editor .load:after{ - border: 8px solid #fff; - border-top: 8px solid #ccc; - background: #ccc; - animation: spin 2s linear infinite; - } - .blox-editor .loadoverlay .load:after{ - position: absolute; - left: 50%; - top: 50%; - margin-top: -10px; - margin-left: -10px; - } - @keyframes spin { - 0% { transform: rotate(0deg); } - 100% { transform: rotate(360deg); } - } - .editor button.success:after, - .editor button.fail:after{ - border: 8px solid #eee; - } - .editor button.success:after{ - background: #00cc00; - } - .editor button.fail:after{ - background: #e0474c; - } - - /* load design navi buttons */ - .navi-item.load:after - { - position: absolute; - right: 6px; - top: 6px; - width: 6px; - height: 6px; - border-radius: 50%; - content: ''; - border: 8px solid #eee; /* background */ - border-top: 8px solid #ccc; /* moving border */ - background: #ccc; /* dot in the middle */ - animation: spin 2s linear infinite; - } - .editor button.button--secondary, .editor a.button--secondary{ - width: auto; - min-width: auto; - max-width: inherit; - } - - .mobile{ - display: none; - } - .desktop{ - display: inline-block; - } -} -@media only screen and (min-width: 900px) { - .main{ - margin: 30px auto; - padding: 20px 30px; - } - .infoline{ - min-height: 30px; - width: 100%; - display: block; - color: #bbb; - font-size: 0.9em; - } - .infoline .help{ - float: right; - text-align: center; - width: 20px; - height: 20px; - line-height: 20px; - border-radius: 50%; - background: #fff; - cursor: pointer; - } - span.level-1{ padding-left: 5px; } - span.level-2{ padding-left: 20px; } - span.level-3{ padding-left: 35px; } - span.level-4{ padding-left: 50px; } - span.level-5{ padding-left: 65px; } - .navi-item i.icon-doc-text, .navi-item i.icon-folder-empty, .navi-item i.icon-home, .navi-item i.icon-plus{ - left: -24px; - } - fieldset.plugin{ - width: 49.5%; - } - .welcome .medium{ - width: 66%; - display: inline-block; - } - .welcome .small{ - width: 33.3%; - display: inline-block; - } - .header-navi ul{ - font-size: 1em; - } - article{ - width: 80%; - display: inline-block; - vertical-align: top; - } - aside.sidebar{ - width: 19%; - display: inline-block; - vertical-align: top; - background: transparent; - } - .sidebar-menu, .sidebar-menu--content{ - max-height: 2000px; - padding: 0 0 0 0; - overflow: visible; - box-sizing: border-box; - font-size: 1em; - text-align: left; - } - .sidebar-menu{ - max-height: 2000px; - } - .sidebar-menu--content{ - max-height: 3000px; - } - .menu-action{ - display: none; - width: 0px; - height: 0px; - } - ul.menu-list{ - margin: 5px 0 0 20px; - } - .navi-items a{ - padding: 20px 5px 10px 0px; - } - .navi-item .status{ - left: -30px; - } - .navi-item a i.icon-move, .navi-item a:link i.icon-move, .navi-item a:visited i.icon-move{ - color: #f9f8f6; - background: transparent; - } - .navi-item a:focus, .navi-item a:focus i, .navi-item a:focus i.icon-move, - .navi-item a:hover, .navi-item a:hover i, .navi-item a:hover i.icon-move, - .navi-item a.active, .navi-item a.active i, .navi-item a.active i.icon-move{ - background:#fff; - color: #444; - } - .navi-items span{ - display: inline; - } - .level-1 > ul.menu-list{ - margin-bottom: 30px; - } - .menu-list li{ - font-weight: 300; - } - .sidebar-menu, .sidebar-menu--content{ - font-size: 0.9em; - } - .sidebar-menu--content li.level-1, .sidebar-menu--content li.level-0 { - font-weight: 700; - } - .menu-list.margin-bottom{ - margin-bottom: 40px; - } - .menu-item a, .menu-item a:link, .menu-item a:visited{ - position: relative; - width: auto; - padding: 1px 0px; - line-height: 1.2em; - border: 0px; - } - .menu-item a:hover, .menu-item a:focus, .menu-item a:active, .menu-item a.active, .active > a{ - color: #fff; - color: #e0474c; - background: transparent; - } - .menu-item a::before{ - position: absolute; - left: -15px; - content:"\A"; - border-style: solid; - border-width: 8px 0px 8px 8px; - border-color: transparent transparent transparent #fff; - } - .menu-item a:hover:before, .menu-item a:focus:before, .menu-item a:active:before, .menu-item a.active:before{ - border-left-color: #e0474c; - } - .addNaviItem a:focus,.addNaviItem a:hover,.addNaviItem a:active{ - text-decoration: underline; - background: transparent; - color: #e0474c; - } - .card .medium{ - padding: 0px 20px; - } - .card .medium button[type="button"]{ - margin-top: 10px; - margin-bottom: 40px; - } - .card .medium input[type="submit"]{ - margin-top: 10px; - margin-bottom: 40px; - } - li.row ul{ - margin: 0px; - } - .col.username,.col.email,.col.userrole{ - width: 30%; - } - .col.edit{ - width: 10%; - } - li.col.username{ - border-top: 0px; - border-left: 2px solid #70c1b3; - } - .buttonset{ - width: 76%; - padding: 10px 40px; - left: auto; - right: auto; - max-width: 900px; - } - .editor button, .editor a{ - border-radius: 3px; - } -} +/********************** +* TRANSITION * +**********************/ + +a, a:link, a:visited, a:focus, a:hover, a:active, button, .button, input, .control-group, .sidebar-menu, .sidebar-menu--content, .menu-action, .button-arrow{ + -webkit-transition: color 0.2s ease; + -moz-transition: color 0.2s ease; + -o-transition: color 0.2s ease; + -ms-transition: color 0.2s ease; + transition: color 0.2s ease; + -webkit-transition: background-color 0.2s ease; + -moz-transition: background-color 0.2s ease; + -o-transition: background-color 0.2s ease; + -ms-transition: background-color 0.2s ease; + transition: border-color 0.2s ease; + -webkit-transition: border-color 0.2s ease; + -moz-transition: border-color 0.2s ease; + -o-transition: border-color 0.2s ease; + -ms-transition: border-color 0.2s ease; + transition: border-color 0.2s ease; +} +.navi-item a, .navi-item.folder a i, .navi-item.file a i{ + -webkit-transition: all 0.1s ease; + -moz-transition: all 0.1s ease; + -o-transition: all 0.1s ease; + -ms-transition: all 0.1s ease; + transition: all 0.1s ease; +} +/******************** +* COMMONS * +********************/ + +html,body{ + padding: 0; + margin:0; +} +body{ + background: #f9f8f6; + color: #444; + font-size: 16px; +} +body,input,select,textarea{ + font-family: Helvetica, Calibri, Arial, sans-serif; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} +header, nav, h1, h2, h3, h4, h5, h6{ + font-family: arial, sans-serif; +} +h1{ font-size: 1.8em; margin: 1em 0 0.8em; font-weight: 700; } +h2{ font-size: 1.6em; margin: 1em 0 0.8em; font-weight: 700; } +h3{ font-size: 1em; margin: 0.6em 0 0.6em; text-transform: uppercase; font-weight: 300; } +.main{ + margin: 0px auto; + box-sizing:border-box; + padding: 20px; +} +section{ + background: #FFFFFF; + box-shadow: 0 0 4px #ddd; + position: relative; + margin-bottom: 40px; + padding: 20px 5px 40px; + box-sizing: border-box; +} +article{ + width: 100%; +} +aside.sidebar{ + display: block; + width: 100%; + background: #fff; + margin-bottom: 10px; + box-sizing: border-box; +} +.right{ + float: right; +} + +/******************** +* NAVI * +********************/ + +.main-header{ + display: inline-block; + width: 100%; + border-bottom: 1px solid #eee; +} +.header-navi, .main{ + margin: auto; + max-width: 1200px; +} +.header-navi .logo{ + display: inline-block; + font-size: 1.6em; + font-weight: 700; + width: 18%; + box-sizing: border-box; +} +.header-navi .logo a{ + color: #000; + text-decoration: none; + line-height: 2em; + margin-left: 10px; +} +.header-navi ul{ + display: inline-block; + width: 80%; + box-sizing: border-box; + text-align: right; + list-style: none; + padding:0px; + margin:0px; + font-size: 1.2em; +} +.header-navi li{ + display: inline-block; + padding: 0px; + margin: 0px; +} +.navi-items a{ + color: #000; + font-size: 0.9em; + text-transform: uppercase; + text-decoration: none; + margin: 0px 5px; + padding: 20px 0px 10px 0px; + line-height: 2em; + border-bottom: 3px solid transparent; +} +.navi-items a:hover, .navi-items a:focus, .navi-items a:active, .navi-items a.active{ + border-bottom: 3px solid #66b0a3; +} +.navi-items i{ + color: #ddd; +} +.navi-items span{ + display: none; +} + +/************************ +* MENU * +************************/ + +.sidebar-menu,.sidebar-menu--content{ + max-height: 40px; + padding: 0px 20px; + overflow: hidden; + box-sizing: border-box; + font-size: 0.9em; + text-align: center; +} +.sidebar-menu.expand{ + max-height: 500px; +} +.sidebar-menu--content.expand{ + max-height: 3000px; +} +.menu-action{ + display: block; + width: 100%; + height: 40px; + text-align: center; + line-height: 40px; + text-transform: uppercase; + font-weight: 300; + cursor: pointer; +} +ul.menu-list{ + list-style: none; + margin: 5px 0 30px; + padding:0; +} +li.menu-item{ + padding: 0; + margin: 8px 0; +} +.menu-item a, .menu-item a:link, .menu-item a:visited{ + background: #f9f8f6; + width: 100%; + box-sizing: border-box; + display: inline-block; + text-decoration: none; + color: #444; + border: 1px solid #eee; + border-radius: 3px; + line-height: 2.3em; +} +.menu-item a:hover, .menu-item a:focus, .menu-item a:active, .menu-item a.active{ + color: #fff; + background: #70c1b3; +} + +/******************** +* CONTENT-NAVI * +********************/ +.content-navi{ + background: transparent; + padding: 0; + position: relative; + text-align: left; +} +.navi-list{ + padding: 0px; +} +.navi-item{ + list-style: none; + padding: 0px; + position: relative; +} +.navi-item i.icon-doc-text, +.navi-item i.icon-folder-empty, +.navi-item i.icon-home, +.navi-item i.icon-plus{ + display: inline-block; + position: absolute; + top: 0px; + background: transparent; + color: #ccc; + padding: 7px 2px 7px; +} +.navi-item .status{ + position: absolute; + width: 4px; + height: 100%; + max-height: 32px; + left: -10px; + border-top: 1px solid #f9f8f6; + border-bottom: 1px solid #f9f8f6; +} +.status.published{ + background:#66b0a3; +} +.status.modified{ +/* background: #FFD700; */ + background: #FFA500; +} +.status.unpublished{ + background:#cc4146; +} +.navi-item i.icon-resize-full-alt, +.navi-item i.icon-move { + position: absolute; + right: 5px; + top: 7px; + color: #f9f8f6; + color: #444; + background: transparent; +} +.navi-item a{ + display: block; + padding: 7px 0; + width: 100%; + margin-bottom: 1px; + margin-top: 1px; + text-decoration: none; + color: #444; + background: transparent; +} +.navi-item.folder a{ + font-weight: 700; +} +.navi-item.file a{ + font-weight: 300; +} +.navi-item a:focus, .navi-item a:focus i, +.navi-item a:hover, .navi-item a:hover i, +.navi-item a.active, .navi-item a.active i{ + background:#66b0a3; + color: #fff; +} +[class^="level-"]{ + padding-left: 80px; + display: block; + box-sizing: border-box; +} +span.level-1{ padding-left: 30px; } +span.level-2{ padding-left: 40px; } +span.level-3{ padding-left: 50px; } +span.level-4{ padding-left: 60px; } +span.level-5{ padding-left: 70px; } + +.addNaviItem{ + padding: 5px; + display: block; +} +.addNaviItem .hide{ + height: 0px; + overflow: hidden; +} +.addNaviItem a, .addNaviItem a:link, .addNaviItem a:visited{ + padding: 3px 0; + margin: 0; + width: auto; + background: transparent; + color: #e0474c; +} +.addNaviItem a:focus,.addNaviItem a:hover,.addNaviItem a:active{ + text-decoration: underline; + background: transparent; + color: #e0474c; +} +.addNaviForm input{ + min-height: 0px; + width: 100%; + background: #fff; + padding: 7px; + outline: 1px solid #efefef; +} +.addNaviForm button{ + display: inline-block; + box-sizing: border-box; + margin: 5px 0 10px; + padding: 5px; + background:#66b0a3; + color: #f9f8f6; + width: 50%; + border: 0px; + border-radius: 2px; +} +.addNaviForm button.fullWidth{ + width: 100%; +} +.addNaviForm button:hover{ + background: #4D978A; +} +.addNaviForm button.b-left{ + border-right: 1px solid #f9f8f6; +} +.addNaviForm button.b-right{ + border-left: 1px solid #f9f8f6; +} +.fade-enter-active { + transition: opacity 0.25s ease-out; +} +.fade-leave-active{ + transition: opacity 0.1s ease-out; +} +.fade-enter, .fade-leave-to { + opacity: 0; +} + +/******************** +* CONTENT * +********************/ + +header h1, header h2{ + display: inline-block; +} +header input[type="submit"]{ + margin-top: 1.5em; + margin-right: 10px; + float: right; + width: 150px; + box-sizing:border-box; + border: 0px; +} +h1 .version-number{ + text-transform: uppercase; + font-size: 0.5em; + font-weight: 300; +} +footer{ + text-align: center; + padding: 20px 0; +} +.math{ + white-space: pre; +} +/******************** +* SETUP FORM * +********************/ +.authformWrapper{ + width: 100%; + background: #fff; + padding: 40px; + box-shadow: 0px 0px 10px rgba(0,0,0,0.2); + border-radius: 3px; + box-sizing: border-box; +} +.setupContent{ + width: 100%; + text-align: center; +} +.setupWrapper{ + margin-top: 8%; + max-width: 350px; + margin-left: auto; + margin-right: auto; +} +.setupWrapper label{ + font-weight: 700; +} +.setupWrapper input[type="submit"] +{ + border-radius: 0px; + margin-bottom: 0px; +} +.setupWrapper input[type="submit"]:disabled{ + cursor: default; + background: #f9f8f6; + border: 2px solid #f9f8f6; +} +.setupWrapper .formElement{ + margin: 0 0 20px 0; +} + + +/************************ +* OPEN-CLOSE BUTTON * +************************/ + +.button-arrow{ + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + border-bottom: 5px solid transparent; + border-top: 5px solid #000; + display:inline-block; + margin-left: 5px; +} +.active .button-arrow{ + border-top: 5px solid #fff; +} +.expand .button-arrow{ + border-bottom: 5px solid #000; + border-top: 0px solid transparent; + margin-bottom: 5px; +} +.expand.active .button-arrow{ + border-bottom: 5px solid #fff; +} + +/************* +** MODAL ** +*************/ + +.modal, .modalWindow{ + position:fixed; + display: block; + z-index: 9999; + width: 100%; + height: 100%; + padding: 20px; + box-sizing: border-box; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: #FFF; + background: rgba(255,255,255,0.9); +} +.modal{ + display: none; +} +.modal.show{ + display: block; +} +.modalInner{ + position: relative; + max-width: 350px; + margin: 10% auto; + background: #FFF; + padding: 50px 50px 20px 50px; + border-radius: 3px; + box-shadow: 0px 0px 10px rgba(0,0,0,0.2); +} +.modalInner h2{ + margin-top: 0px; +} +.modalInner.wide{ + max-width: 500px; +} +.closeModal{ + position: absolute; + top: 0px; + right: 0px; + font-weight: 700; + padding: 0.6em; + cursor: pointer; +} +.closeModal:focus, .closeModal:hover{ + background: #f9f8f6; +} +.modal .actionLink{ + display: block; + width: 100%; + text-align: center; + margin-top: -20px; + margin-bottom: 20px; +} +a.openModal:link,a.openModal:active,a.openModal:visited{ + color: #cc4146; + padding: 0px 20px 20px; + text-decoration: none; + display: block; + margin: auto; + text-align: center; + width: 100px; +} +a.openModal:focus,a.openModal:hover{ + text-decoration: underline; +} + +/***************** +** USER-LIST ** +*****************/ + +ul.userlist{ + padding: 0; + margin: 10px 20px 30px; + list-style: none; +} +li.row{ + display: inline-block; + width: 100%; + box-sizing: border-box; +} +li.row ul{ + background: #f9f8f6; + margin: 5px 0; + padding: 0px; + width: 100%; +} +li.col{ + display: inline-block; + padding: 15px 10px; + box-sizing: border-box; + margin: 0px; +} +li.col.username, li.col.email, li.col.userrole, li.col.edit{ + border: 1px solid #fff; +} +li.col.username{ + border-top: 2px solid #70c1b3; +} +.col.username,.col.email,.col.userrole, .col.edit{ + width: 100%; +} +.userlist a, .userlist a:link, .userlist a:visited{ + color: #e0474c; + text-decoration: none; +} +.userlist a:hover, .userlist a:focus, .userlist a:active{ + text-decoration: underline; +} + +/********************* +** WELCOME PAGE ** +*********************/ + +.formWrapper, .welcome{ + display: block; + position: relative; + max-width: 900px; + margin: auto; +} +.welcome{ + padding: 30px 50px; + box-sizing: border-box; + background: #FFFFFF; + box-shadow: 0px 0px 10px rgba(0,0,0,0.2); +} +.welcome .welcomeIntro a, .welcome .welcomeCard a:link, .welcome .welcomeCard a:visited{ + text-decoration: none; + color: #e0474c; +} +.welcome .welcomeIntro a:hover, .welcome .welcomeCard a:focus, .welcome .welcomeCard a:active{ + text-decoration: underline; +} +.welcome p{ + line-height: 1.5em; + padding: 0px; +} +.welcome a.button{ + margin: 40px auto; + display: block; + width: 200px; + text-align: center; + padding: 10px; + border-radius: 3px; + text-decoration: none; +} +.welcome .welcomeCard{ + position: relative; + margin-top: -1px; + border-top: 1px solid #efece7; + border-bottom: 1px solid #efece7; + width: 100%; +} +.welcome .welcomeCard h3{ + font-weight: 700; +} +.welcome .welcomeCard a, .welcome .welcomeCard a:link, .welcome .welcomeCard a:visited{ + color: #444; + text-decoration: none; + width: 100%; +} +.welcome .welcomeCard a:hover, .welcome .welcomeCard a:focus, .welcome .welcomeCard a:active{ + color: #e0474c; +} +.welcome .welcomeCard a:after{ + content: ''; + display: block; + position: absolute; + left: 100%; + top: 20px; + bottom: 0; + width: 0; + height: 0; + border-top: 50px solid transparent; + border-right: 0px solid transparent; + border-bottom: 50px solid transparent; + border-left: 20px solid #efece7; +} +.welcome .welcomeCard a:hover:after{ + border-left: 20px solid #e0474c; +} +.welcome .welcomeInner{ + margin-top: 20px; + width: 90%; + height: 120px; +} + +header.headline +{ + padding: 0px 0px; +} + +/******************** +* Forms * +********************/ + +.large, .medium, .small{ + padding: 18px 0px; + box-sizing:border-box; + vertical-align: top; +} +form .large, form .medium, form .small{ + width: 100%; + display: block; +} +.large img{ + width: 100%; +} +fieldset{ + display: block; + position: relative; + width: 100%; + margin: 0; + box-sizing: border-box; + border: none; + padding: 0; +} +label,.label{ + display: inline-block; + width: 100%; + font-size: 0.75em; + font-weight: 300; + padding: 0px; + line-height: 1.5em; + text-transform: uppercase; +} +input, select, textarea{ + display: inline-block; + box-shadow: none; + width: 100%; + font-size: 1em; + box-sizing: border-box; + min-height: 42px; + border: 1px solid #f9f8f6; + background: #f9f8f6; +} +input:focus, select:focus, textarea:focus{ + border: 1px solid #FFF; + outline: none; + box-shadow: 0 0 2px #000; +} +input, textarea{ + padding: 10px; +} +textarea{ + min-height: 84px; +} +select{ + padding: 14px 8px; +} +button,input[type="submit"]:hover{ + cursor: pointer; +} +input[type="submit"]{ + border-radius: 3px; + margin-bottom: 40px; + color: #f9f8f6; + border: 2px solid #70c1b3; + background: #70c1b3; +} +input[type="submit"]:hover{ + border: 2px solid #66b0a3; + background: #66b0a3; +} +input, select, button[type="button"]{ + min-height: 52px; +} +button[type="button"]{ + background: #f9f8f6; + color: #444; + border-top: 1px solid #ddd; + width: 100%; + text-align: center; +} +button[type="button"].active{ + background: #70c1b3; + color: #f9f8f6; + border-top: 1px solid #70c1b3; +} +button[type="button"].no-settings:hover{ + cursor: default; +} +button[type="button"].has-settings:hover{ + background: #efece7; +} +button[type="button"].has-settings.active:hover{ + background: #66b0a3; + border-top: 1px solid #66b0a3; +} +button[type="button"].plugin-button{ + border-left: 0px; + border-right: 0px; + border-bottom: 0px; + margin-top: 10px; + padding: 15px 0; +} +button[type="button"].theme-button{ + border: 0px; + margin: 5px 0 20px; + padding: 12px; + border-radius: 3px; + min-height: 52px; + background: #FFF; +} +button[type="button"].theme-button.active{ + background: #70c1b3; + color: #f9f8f6; +} +button[type="button"].theme-button:hover{ + border:0px; + border-top: 0px; +} +.button{ + color: #fff; + padding: 10px; + border-radius: 3px; +} +a.button, a.button:link,a.button:visited{ + text-decoration: none; + color: #fff; + border: 2px solid #70c1b3; + background: #70c1b3; +} +a.button:focus, a.button:hover, a.button:active{ + border: 2px solid #66b0a3; + background: #66b0a3; +} + +input[type="submit"].danger{ + border: 2px solid #e0474c; + background: #e0474c; +} +input[type="submit"].danger:hover{ +border: 2px solid #cc4146; +background: #cc4146; +} + + +.settings .medium a.button{ + width: 100%; + display: block; + box-sizing:border-box; + text-align: center; +} + +/***************** +** CARDS ** +*****************/ + +fieldset.card{ + width: 100%; + display: inline-block; + padding: 15px 0px; + box-sizing: border-box; + vertical-align: top; +} +.card .medium{ + padding: 0px 20px; +} +.card img{ + width: 100%; + border: 1px solid #f9f8f6; + background: #f9f8f6; +} +.cardInner{ + box-sizing: border-box; + position: relative; + border: 1px solid #ddd; + background-color: #f9f8f6; +} +.cardInner .button-box{ + margin: 20px; +} +.cardHead h2{ + padding: 5px 0px; + margin-bottom: 0px; +} +.cardHead header, .cardHead p, .cardHead ul{ + padding: 5px 0px; +} +.cardHead .cardActive{ + float: right; +} +.cardHead header{ + width: 100%; + height: 52px; + display: inline-block; + box-sizing: border-box; + padding: 20px; + background: #f9f8f6; + color: #444; +} +.cardHead header legend{ + font-size: 1.2em; + font-weight: 700; + float:left; +} +.cardHead header label{ + display: inline-block; + width: auto; + line-height: 20px; + margin: 0; + padding: 0 25px 0 0; +} +.cardHead header .control-group .checkmark{ + right:-5px; + left: auto; +} +.cardHead header input{ + width: auto; + min-height: 0px; + padding: 0; + margin: 0; + vertical-align: middle; +} +.cardContent{ + padding: 20px 40px 0; +} +.cardDescription{ + line-height: 1.5em; +} +.cardDescPlugin{ + padding: 0 20px; +} +ul.cardInfo{ + font-size: 0.8em; + padding: 0px; +} +.cardInfo a, .cardInfo a:link,.cardInfo a:visited{ + color: #70c1b3; + text-decoration: none; +} +.cardInfo a:focus,.cardInfo a:hover,.cardInfo a:active{ + text-decoration: underline; +} +.cardInfo li{ + display: inline-block; + box-sizing: border-box; + vertical-align: top; + padding: 0px 5px 0 0; + margin: 0px 5px 0 0; + color: grey; + border-right: 1px solid grey; +} +.cardInfo li:last-child{ + border-right: 0px; +} +.cardFields{ + max-height: 0; + transition: max-height 0.5s ease-out; + overflow: hidden; + padding: 0px 40px; + border: 0px; +} +.cardFields.open{ + max-height: 2800px; + transition: max-height 0.5s ease-in; + overflow: hidden; + border: 1px; +} +.cardFields .cardField:first-child{ + margin-top: 50px; +} +.cardFields input, .cardFields select, .cardFields textarea, .cardFields .onoffswitch{ + margin-bottom: 12px; +} +.cardFields label{ + margin-top: 12px; +} +.cardFields fieldset.subfield legend{ + float:none; + font-weight: bold; + font-size: 1.2em; +} +.cardField{ + position: relative; + width: 100%; + display: inline-block; + margin: 10px 0; +} +.cardField label{ + width: 100%; + margin-top: 0px; + margin-left: 0px; + padding: 0px; + background: transparent; +} +.cardField label.control-group{ + padding-left: 30px; + margin: 10px 0 0; +} +.cardField input, .cardField select, .cardField textarea{ + background-color: #FFF; +} +.cardFields fieldset.subfield{ + padding: 20px; + border: 1px solid #ddd; + margin: 20px 0 30px; +} + +/******************** +* ALERT/ERRROR * +********************/ + +.alert{ + color: #fff; + width: 100%; + line-height: 20px; + display: block; + margin:0px; + text-align: center; + padding: 5px 0px; +} +.alert-info{ + background: #70c1b3; +} +.alert-error{ + background: #e0474c; +} + +.error input, +.error select, +.error textarea{ + border: 1px solid #e0474c; +} +.error, +.error label, +.error small, +.error p{ + color: #e0474c; +} +.errors .cardHead header{ + background: #e0474c; + color: #fff; +} +span.error{ + font-size: 0.8em; + line-height: 1em; +} +.cardInner span.error{ + display: block; + margin-top: -8px; + margin-bottom: 8px; +} + + +/******************** +* UPDATE-BANNER * +********************/ + +.update-banner{ + display: none; + position: absolute; + top: 0; + left: 0; + width: 0; + height: 0; + border-top: 100px solid #e0474c; + border-bottom: 100px solid transparent; + border-right: 100px solid transparent; +} +.update-banner.show-banner{ + display: block; +} +.update-banner span{ + position: absolute; + top: -85px; + left: -20px; + text-align: center; + font-size: 0.9em; + transform: rotate(-45deg); + display: block; + color: #fff; + width: 100px; +} +.update-banner a{ + color: #fff; + text-decoration: none; +} + +/**************************************** +* SELECT BUTTON * +* https://codepen.io/vkjgr/pen/VYMeXp * +****************************************/ + +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, #f9f8f6, #f9f8f6); + 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; +} +.cardField select{ + background-image: + linear-gradient(45deg, transparent 50%, #444 50%), + linear-gradient(135deg, #444 50%, transparent 50%), + linear-gradient(to right, #fff, #fff); +} +/******************** +* COLOR PICKER * +********************/ + +.color-box{ + width: 100%; + height: 36px; + margin-top: 5px; + margin-bottom: 12px; + display: inline-block; + border: 1px solid #ddd; +} +.color-box:hover{ + cursor: pointer; +} +.color-picker-control{ + width: 172px; +} +.color-picker-control input{ + margin-top: 0px; +} + +/************************ +* CHECKBOX / RADIO * +************************/ + +.control-group input { + position: absolute; + opacity: 0; + cursor: pointer; + width: 0; + height: 0; + padding: 0; + margin: 0; + font-size: 0.1em; + min-height: 0px; +} +.control-group { + text-transform: none; + display: block; + position: relative; + padding-left: 30px; + margin-bottom: 7px; + margin-top: 5px; + cursor: pointer; + font-size: 1em; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.checkmark { + position: absolute; + top: 0; + left: 0; + height: 16px; + width: 16px; + border: 2px solid #ccc; + background-color: #fff; + border-radius: 2px; +} +.checkmark:after { + content: ""; + position: absolute; + display: none; +} +.control-group .checkmark:after { + left: 5px; + top: 0px; + width: 5px; + height: 10px; + border: solid #fff; + border-width: 0 2px 2px 0; + -webkit-transform: rotate(45deg); + -ms-transform: rotate(45deg); + transform: rotate(45deg); +} +.radiomark { + position: absolute; + top: 0; + left: 0; + height: 16px; + width: 16px; + border: 2px solid #ccc; + background-color: #fff; + border-radius: 50%; +} +.radiomark:after { + content: ""; + position: absolute; + display: none; +} +.control-group .radiomark:after { + top: 5px; + left: 5px; + width: 6px; + height: 6px; + background: #fff; + border-radius: 50%; +} +.control-group:hover input ~ .checkmark, +.control-group:hover input ~ .radiomark { + background-color: #ccc; + border: 2px solid #ccc; +} +.control-group input:checked ~ .checkmark, +.control-group input:checked ~ .radiomark { + border: 2px solid #70c1b3; + background-color: #70c1b3; +} +.control-group input:checked ~ .checkmark:after, +.control-group input:checked ~ .radiomark:after { + display: block; +} +.control-group input:disabled ~ .checkmark, +.control-group input:disabled ~ .radiomark { + border: 2px solid #eee; + background-color: #eee; +} + +/******************** +* TOOLTIP * +********************/ + +label .help, .label .help{ + float:right; + width: 15px; + height: 15px; + line-height: 15px; + border-radius: 50%; + text-align: center; + background: #fff; + cursor: pointer; +} + +.help { + position: relative; + display: inline-block; + border-bottom: 0px dotted black; +} +.help .tooltip { + visibility: hidden; + width: 200px; + top: -5px; + right: 25px; + text-align: center; + padding: 5px 0; + border-radius: 6px; + color: #fff; + background-color: #555; + + position: absolute; + z-index: 1; + opacity: 0; + transition: opacity .6s; + + text-transform: none; +} +.help .tooltip:after{ + content: ""; + position: absolute; + top: 15px; + left: 100%; + margin-top: -5px; + border-width: 5px; + border-color: transparent transparent transparent #999; + border-style: solid; +} +.help:hover .tooltip{ + visibility: visible; + opacity: 1; +} + +/******************** +* LOGIN * +********************/ + +.loginarea{ + position: relative; + text-align: center; + width: 100%; +} +.loginbutton{ + height: 34px; +} +.loginarea .counter{ + position: absolute; + width: 100%; + top: 16px; + color: #444; +} +.loginarea .forgotpw{ + margin-top: 20px; +} +.loginarea .forgotpw a,.loginarea .forgotpw a:link,.loginarea .forgotpw a:visited{ + font-size: 0.8em; + text-decoration: none; + color: #cc4146; +} +.loginarea .forgotpw a:hover,.loginarea .forgotpw a:focus,.loginarea .forgotpw a:active{ + text-decoration: underline; +} + +/******************** +* EDITOR * +********************/ + +.editor .large{ + position: relative; +} +.editor input[name="title"]{ + font-size: 1.8em; + padding: 10px 20px; +} +.editor textarea{ + font-size: 1em; + padding: 20px; + line-height: 1.4em; +} +.editor span.error{ + position: absolute; + left:20px; + bottom: 0px; +} +.editor .message{ + display: inline-block; + width: 70%; +} +.editor .message span.error{ + position: relative; + left: 0; + width: 100%; + padding: 15px 20px; + color: #e0474c; +} +.buttonset{ + position: fixed; + display: block; + bottom: 0; + left: 0; + right: 0; + box-sizing: border-box; + padding: 2px; + max-width: 900px; + background: #fff; + box-shadow: 0 0 4px #ddd; + z-index: 99; +} +.buttonset .message.error{ + position: absolute; + background: #e0474c; + color: #fff; + font-size: 0.9em; + width: 100%; + left: 0; + top: -25px; + padding: 5px 40px; + box-sizing: border-box; + box-shadow: 0 0 2px #ddd; +} + +.editor button, .editor a{ + position: relative; + display: inline-block; + min-width: 40px; + margin: 4px 2px; + padding: 10px; + border-radius: 0px; + font-size: 0.8em; + text-align: center; + text-decoration: none; + box-sizing: border-box; +} +.editor .secondary{ + display: inline-block; + float: right; +} +.editor button.danger:enabled, +.editor button.danger[enabled], +.editor a.button--secondary, +.editor a.button--secondary:visited{ + border: 1px solid #eee; + background: #fff; + color: #444; +} +.editor button:enabled, +.editor button[enabled], +.editor a.button--secondary:focus, +.editor a.button--secondary:hover, +.editor a.button--secondary:active{ + border: 1px solid #66b0a3; + background: #66b0a3; + color: #fff; +} +.editor button:enabled:hover, +.editor button[enabled]:hover{ + border: 1px solid #4D978A; + background: #4D978A; + color: #fff; +} +.editor button.danger:enabled:hover, +.editor button.danger[enabled]:hover{ + border: 1px solid #e0474c; + background:#e0474c; + color: #fff; +} +.editor button:disabled, +.editor button[disabled]{ + border: 1px solid #eee; + background: #eee; + color: #444; + cursor: default; +} + +[v-cloak]{ + display: none; +} +.mobile{ + display: block; +} +.desktop{ + display: none; +} + +/**************** +** BLOX EDITOR ** +****************/ + +.blox-body{ + position: relative; + padding: 18px 20px +} +.blox-overlay{ + position:absolute; + display: block; + z-index: 10; + box-sizing: border-box; + top: 0; + bottom: 0; + left: 0; + right: 0; + background: #FFF; + background: rgba(255,255,255,0.8); +} +.blox{ + padding: 1px 20px; + line-height: 1.5em; + font-size: 16px; +} +.blox p{ + margin-top: 12px; + margin-bottom: 12px; +} +.blox:hover{ + background: #f9f8f6; +} +.blox-editor{ + position: relative; +} +.blox-buttons{ + position: absolute; + bottom: -15px; + text-align: right; + right: 25px; + width: 200px; + z-index: 99; +} +.blox-buttons button{ + display: inline-block; + box-sizing: border-box; + margin: 2px; + padding: 3px 6px; + width: 80px; + text-align: center; + color: #444; + background: #f9f8f6; + border: 2px solid #fff; + border-radius: 2px; + font-size: 0.9em; +} +.blox-buttons button.edit:hover{ + background: #70c1b3; + color: #eee; +} +.blox-buttons button.cancel:hover{ + background: #e0474c; + color: #eee; +} +.blox-buttons button.edit:disabled, .blox-buttons button.cancel:disabled{ + background: #eee; + color: #444; + border: 1px solid #eee; +} + + +.sideaction{ + position: absolute; + top: 0px; + font-size: 0.8em; + right: -22px; +} +.sideaction button{ + display: block; + font-weight: 300; + font-size: 0.9em; + background: #fff; + color: #fff; + width: 20px; + height: 20px; + line-height: 20px; + text-align: center; + padding: 0px; + margin: 1px; + border: 0px; + border-radius: 1px; +} +.blox-wrapper{ + position: relative; +} +.editactive .sideaction button, +.blox-wrapper:hover button.add, +.blox-wrapper:hover button.delete{ + background-color: #f9f8f6; + color: #666; +} +.sideaction:hover ~ .background-helper { + background-color: #f9f8f6; +} +.editactive .background-helper{ + background-color: transparent!important; +} +.blox-wrapper button.add:hover{ + background: #66b0a3; + color: #fff; +} +.blox-wrapper button.delete:hover{ + background: #e0474c; + color: #fff; +} + + + +.blox-editor textarea{ + font-family: arial; + line-height: 1.5em; + font-size: 16px; + padding-left: 20px; + padding-right: 20px; + box-sizing: border-box; + min-height: 40px; +} +.blox-editor textarea:focus, .blox-editor input:focus{ + box-shadow: none; + outline: none; +} +.blox-editor input.mdcontent.h2,.blox-editor input.mdcontent.h3,.blox-editor input.mdcontent.h4,.blox-editor input.mdcontent.h5,.blox-editor input.mdcontent.h6{ + padding-left: 35px; +} +.blox-editor input.mdcontent.h2{ + font-size: 1.6em; + font-weight: 700; +} +.blox-editor input.mdcontent.h3{ + font-size: 1.3em; + font-weight: 700; +} +.blox-editor input.mdcontent.h4{ + font-size: 1.1em; + font-weight: 700; +} +.blox-editor input.mdcontent.h5{ + font-size: 1em; + font-weight: 700; +} +.blox-editor input.mdcontent.h6{ + font-size: 1em; + font-weight: 300; + font-style: italic; +} +button.hdown{ + position: absolute; + padding: 8px; + top: 1px; + bottom: 1px; + left: 0px; + font-size: 1em; + font-weight: 700; + background: #f9f8f6; + border: 0px solid #fff; + border-right: 1px solid #fff; + color: #66b0a3; +} +button.hdown:hover,button.hdown:focus,button.hdown:active{ + color: #f9f8f6; + background: #66b0a3; +} + +.blox-editor .contenttype { + position: absolute; + top: 15px; + left: -25px; + color: #666; +} +.visible{ + display: block; +} +.hidden{ + visibility: hidden; +} +.hidden .blox:hover{ + background: #fff; +} +.component{ + position: absolute; + width: 100%; + z-index:9; +} + + +/* .format-bar at the bottom of the page */ +.format-bar .hidden{ + display: none; +} +.format-bar .component{ + position: relative; +} +.format-bar{ + padding: 20px; + width:100%; + box-sizing: border-box; +} +.format-bar .editactive{ + position: relative; + margin-left: -20px; + margin-right: 20px; +} +.format-bar.blox{ + width: auto; + background: #f9f8f6; +} +.newblock{ + z-index: 20; +} +.newblock .sideaction{ + display: none; +} +.newblock-info{ + padding: 0 0 0 20px; + font-size: 0.9em; + line-height: 30px; + background: #66b0a1; + margin-bottom: 2px; + color: #fff; +} +.newblock-close{ + line-height: 30px; + border: 0px; + color: #fff; + background: #e0474c; + float: right; +} +/* format line for each block */ +.formatbuttons{ + margin: 5px 0px 2px; + font-size: 0.7em; + text-align: center; +} +.formatbuttons.hidden{ + display: none; +} +.formatbuttons button.format-item { + width: 30px; + height: 30px; +} +.fade-editor-enter-active{ + transition: opacity .5s; +} +.fade-editor-enter{ + opacity: 0.3; +} +button.format-item{ + margin: 2px 0; + padding: 5px; + background: #f9f8f6; + border: 1px solid #eee; + color: #444; + display: inline; + border-radius: 2px; + width: 40px; + height: 40px; + text-align: center; +} +button.format-item:hover{ + background: #66b0a3; + border: 1px solid #66b0a3; + color: #fff; + cursor: pointer; +} +button.format-item.disabled, button.format-item.close{ + width: 90px; +} +button.format-item.disabled{ + background: #f9f8f6; + color: #444; + border: 1px solid #eee; + cursor: initial; +} +button.format-item.close:hover{ + background: #cc4146; + border: 1px solid #cc4146; +} + +/************************ +** BLOX EDITOR CONTENT ** +************************/ + +.blox h1, .blox h2, .blox h3, .blox h4, .blox h5, .blox h6{ font-weight: 700; line-height: 1em; } +.blox h1{ font-size: 2.2em; margin: 0.6em 0 0.6em; } +.blox h2{ font-size: 1.6em; margin: 1.3em 0 0.6em; } +.blox h3{ font-size: 1.3em; text-transform: none; margin: 1.2em 0 0.6em; } +.blox h4{ font-size: 1.1em; margin: 1.2em 0 0.6em; } +.blox h5{ font-size: 1em; margin: 1.2em 0 0.6em; } +.blox h6{ font-size: 1em; font-style: italic; font-weight:300; margin: 1em 0 0.6em; } +.title input{ + font-size: 2.2em; + font-weight: 700; + padding: 20px; +} +.blox pre,.blox code{ + white-space: pre; + color: #333; + background: #f9f8f6; +} +.blox code{ + display: inline-block; + padding: 0 0.5em; + font-size: 0.8em; + line-height: 1.4em; + border-radius: 3px; +} +.blox code.hljs{ + background: transparent; +} +.blox pre{ + padding: 10px; + display: block; + max-width: 100%; + overflow-x: auto; + border-left: 4px solid #e0474c; +} +.blox table{ + width: 100%; + border-collapse: collapse; +} +.blox thead{ + border-bottom: 1px solid #e0474c; + border-top: 1px solid #e0474c; + background: #f9f8f6; + font-weight: 700; +} +.blox tbody{} +.blox th{ padding: 10px 0;} +.blox tr,.blox-editor tr{} +.blox tr:nth-child{ } +.blox tr:nth-child(even){ background-color:#f9f8f6; } +.blox td{ padding: 5px;} + +.blox-editor table{ + display: inline-table; + width: 100%; + border-collapse: collapse; + margin-bottom: 20px; +} +.blox-editor thead{} +.blox-editor tbody{} +.blox-editor tr{} +.blox-editor th{ border: 1px solid #ccc; padding: 5px; } +.blox-editor td{ border: 1px solid #ccc; padding: 5px; } +.blox-editor td.noteditable, .blox-editor th.noteditable{ + border: 1px solid #ccc; + text-align:center; + color: #ccc; + background: #f9f8f6; + font-weight: 300; + padding: 0; +} +.blox-editor .columnaction, .blox-editor .rowaction{ + position: absolute; + background: #fff; + width: 150px; + font-size: 0.9em; + color: #000; + text-align: left; + box-shadow: 0 0 2px #000; + margin: 5px; + z-index:999999; +} +.blox-editor .rowaction{ + margin-left: 53px; + margin-top: -3px; +} +.blox-editor .actionline{ + padding: 5px 10px; + cursor: pointer; +} +.blox-editor .actionline:hover{ + background: #70c1b3; + color: #fff; +} +.blox dl{ + border-top: 1px solid #e0474c; + border-bottom: 1px solid #e0474c; + padding: 0.5em 0; + box-sizing: border-box; +} +.blox dt, .blox dd{ + width: 100%; + margin: 0; + padding: 3px 5px; + box-sizing: border-box; + display: inline-block; + vertical-align: top; +} +.blox dt{ + font-weight: 700; +} +.blox dt::after{ + content: ":"; +} +.blox dd{ + padding-left: 40px; +} +.definitionList{ + background: #f9f8f6; +} +.definitionRow{ + width: 100%; + display: block; + padding: 20px 20px 0 20px; + box-sizing: border-box; +} +.definitionRow .icon-colon{ + display: inline-block; + margin-top: 15px; +} +input.definitionTerm{ + width: 29%; + display: inline-block; + vertical-align: top; + background: #fff; +} +textarea.definitionDescription{ + width: 60%; + display: inline-block; + vertical-align: top; + background: #fff; +} +button.addDL, +button.delDL{ + display: inline-block; + vertical-align: top; + font-weight: 300; + font-size: 0.8em; + height: 20px; + line-height: 20px; + border: 0px; + background: transparent; +} +button.addDL i, +button.delDL i{ + width: 20px; + border-radius: 1px; + text-align: center; + color:#f9f8f6; + padding: 2px 0 0; + line-height: 20px; + height: 20px; +} +button.addDL{ + margin: 20px; +} +button.addDL i{ + background: #70c1b3; +} +button.addDL i:hover{ + background:#66b0a3; +} +button.delDL i{ + background: #e0474c; +} +button.delDL i:hover{ + background: #cc4146; +} +i.icon-resize-vertical{ + color: #ddd; +} +.blox ul, .blox ol{ + padding-left: 0px; + margin-left: 18px; +} +.blox blockquote{ + border-left: 4px solid #e0474c; + background: #f9f8f6; + position: relative; + font-style: italic; + font-family: serif; + border-left: 4px solid #e0474c; + background: #f9f8f6; + margin: 12px 0; + padding: 5px; +} +.blox blockquote:before { + position: absolute; + left: 0px; + top: 20px; + color: #ccc; + content: open-quote; + font-size: 4em; +} +.blox blockquote p{ + margin-left: 50px; +} +.dropbox{ + min-height: 70px; + background: #f9f8f6; + padding: 0px; + box-sizing: border-box; + margin-bottom: 10px; +} +.dropbox p{ + position: relative; + cursor: pointer; + line-height: 70px; + width: 100%; + text-align: center; + box-sizing:border-box; + padding: 0; + margin: 0; +} +.dropbox input, .dropbox select{ + background-color: #fff; + width: 80%; + margin: 2px 0; + display: inline-block; +} +.video input{ + width: 75%; + margin: 15px 0; +} +.dropbox select{ + background-image: linear-gradient(45deg, transparent 50%, #444 50%), linear-gradient(135deg, #444 50%, transparent 50%), linear-gradient(to right, #fff, #fff); +} +.dropbox label{ + width: 20%; + display: inline-block; +} +.video label{ + text-align: right; + padding-right: 10px; + box-sizing: border-box; +} +.dropbox .imgmeta{ + padding: 30px; + box-sizing: border-box; +} +.dropbox .input-file{ + opacity: 0; + width: 100%; + height: 70px; + position: absolute; + cursor: pointer; + z-index: 1; +} +.blox img, img.uploadPreview{ + display: block; + margin: auto; + max-width: 100%; +} +.blox img.youtube{ + position: relative; +} +.blox .video-container{ + position: relative; + text-align: center; +} +.blox button.play-video { + position: absolute; + top: 50%; + margin-top: -50px; + margin-left: -50px; + height: 100px; + width: 100px; + background: #e0474c; + color: #FFFFFF; + border-radius: 50%; + border: 0px; + padding: 0; + text-align: center; +} +.blox button.play-video:hover { + background: #cc4146; +} +.blox button.play-video::after { + position: absolute; + top: 50%; + margin: -20px 0 0 -15px; + height: 0; + width: 0; + border-style: solid; + border-width: 20px 0 20px 40px; + border-color: transparent transparent transparent rgba(255, 255, 255, 0.75); + content: ' '; +} +sup{} +cite{} +abbr{} +hr{ + background: #ddd; + height: 2px; + margin: 20px 0; + border: 0px; +} +.blox ul.TOC,.blox .TOC ul{ + list-style: none; + padding-left: 0px; + margin-left: 0px; +} +.blox .TOC li:before{ + content: "\2192"; + margin-left: -7px; + margin-right: 7px; +} +.blox .TOC li.h1:before{ + content: ""; +} +.blox ul.TOC{ + background: #f9f8f6; + width: 100%; + padding: 20px; + box-sizing:border-box; +} +.blox li.h1{ + font-weight: 700; +} +.blox li.h2,.blox li.h3,.blox li.h4,.blox li.h5,.blox li.h6{ + font-weight: 400; + padding-left: 25px; +} +.blox a, .blox a:link, .blox a:visited, +footer a, footer a:link, footer a:visited +{ + text-decoration: none; + color: #e0474c; +} +.blox a:focus, .blox a:hover, .blox a:active, +footer a:focus, footer a:hover, footer a:active +{ + text-decoration: underline; +} +.setupContent a, .setupContent a:link, .setupContent a:visited +{ + text-decoration: none; + color: #444; +} +.setupContent a:focus, .setupContent a:hover, .setupContent a:active +{ + color: #e0474c; +} +.blox .TOC li:before{ color: #bbb; } + + +@media only screen and (min-width: 600px) { + section{ + padding: 20px 20px 40px; + } + header.headline{ + padding: 0px 20px; + } + .large, .medium, .small{ + padding: 18px 20px; + } + form .medium{ + width: 49.5%; + display: inline-block; + } + form .small{ + width: 33.3%; + display: inline-block; + } + fieldset.card{ + padding: 15px 20px; + } + .settings .medium a.button{ + display: inline-block; + width: auto; + } + .editor button, .editor a.button--secondary{ + min-width: 150px; + font-size: 0.9em; + } + + .blox-editor .loadwrapper{ + display: block; + position: relative; + width: 40px; + height: 40px; + margin: auto; + text-align: center; + } + .blox-editor .loadoverlay{ + display: block; + position: absolute; + z-index: 999; + left: 0px; + right: 0px; + top: 0px; + bottom: 0px; + background: rgba(255,255,255,0.4); + } + /* load design editor button */ + .editor button.load:after, .blox-editor .load:after, + .editor button.success:after, + .editor button.fail:after{ + position: absolute; + right: 8px; + top: 6px; + width: 8px; + height: 8px; + border-radius: 50%; + content: ''; + } + .editor button.load:after, .blox-editor .load:after{ + border: 8px solid #fff; + border-top: 8px solid #ccc; + background: #ccc; + animation: spin 2s linear infinite; + } + .blox-editor .loadoverlay .load:after{ + position: absolute; + left: 50%; + top: 50%; + margin-top: -10px; + margin-left: -10px; + } + @keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + .editor button.success:after, + .editor button.fail:after{ + border: 8px solid #eee; + } + .editor button.success:after{ + background: #00cc00; + } + .editor button.fail:after{ + background: #e0474c; + } + + /* load design navi buttons */ + .navi-item.load:after + { + position: absolute; + right: 6px; + top: 6px; + width: 6px; + height: 6px; + border-radius: 50%; + content: ''; + border: 8px solid #eee; /* background */ + border-top: 8px solid #ccc; /* moving border */ + background: #ccc; /* dot in the middle */ + animation: spin 2s linear infinite; + } + .editor button.button--secondary, .editor a.button--secondary{ + width: auto; + min-width: auto; + max-width: inherit; + } + + .mobile{ + display: none; + } + .desktop{ + display: inline-block; + } +} +@media only screen and (min-width: 900px) { + .main{ + margin: 30px auto; + padding: 20px 30px; + } + .infoline{ + min-height: 30px; + width: 100%; + display: block; + color: #bbb; + font-size: 0.9em; + } + .infoline .help{ + float: right; + text-align: center; + width: 20px; + height: 20px; + line-height: 20px; + border-radius: 50%; + background: #fff; + cursor: pointer; + } + span.level-1{ padding-left: 5px; } + span.level-2{ padding-left: 20px; } + span.level-3{ padding-left: 35px; } + span.level-4{ padding-left: 50px; } + span.level-5{ padding-left: 65px; } + .navi-item i.icon-doc-text, .navi-item i.icon-folder-empty, .navi-item i.icon-home, .navi-item i.icon-plus{ + left: -24px; + } + fieldset.plugin{ + width: 49.5%; + } + .welcome .medium{ + width: 66%; + display: inline-block; + } + .welcome .small{ + width: 33.3%; + display: inline-block; + } + .header-navi ul{ + font-size: 1em; + } + article{ + width: 80%; + display: inline-block; + vertical-align: top; + } + aside.sidebar{ + width: 19%; + display: inline-block; + vertical-align: top; + background: transparent; + } + .sidebar-menu, .sidebar-menu--content{ + max-height: 2000px; + padding: 0 0 0 0; + overflow: visible; + box-sizing: border-box; + font-size: 1em; + text-align: left; + } + .sidebar-menu{ + max-height: 2000px; + } + .sidebar-menu--content{ + max-height: 3000px; + } + .menu-action{ + display: none; + width: 0px; + height: 0px; + } + ul.menu-list{ + margin: 5px 0 0 20px; + } + .navi-items a{ + padding: 20px 5px 10px 0px; + } + .navi-item .status{ + left: -30px; + } + .navi-item a i.icon-move, .navi-item a:link i.icon-move, .navi-item a:visited i.icon-move{ + color: #f9f8f6; + background: transparent; + } + .navi-item a:focus, .navi-item a:focus i, .navi-item a:focus i.icon-move, + .navi-item a:hover, .navi-item a:hover i, .navi-item a:hover i.icon-move, + .navi-item a.active, .navi-item a.active i, .navi-item a.active i.icon-move{ + background:#fff; + color: #444; + } + .navi-items span{ + display: inline; + } + .level-1 > ul.menu-list{ + margin-bottom: 30px; + } + .menu-list li{ + font-weight: 300; + } + .sidebar-menu, .sidebar-menu--content{ + font-size: 0.9em; + } + .sidebar-menu--content li.level-1, .sidebar-menu--content li.level-0 { + font-weight: 700; + } + .menu-list.margin-bottom{ + margin-bottom: 40px; + } + .menu-item a, .menu-item a:link, .menu-item a:visited{ + position: relative; + width: auto; + padding: 1px 0px; + line-height: 1.2em; + border: 0px; + } + .menu-item a:hover, .menu-item a:focus, .menu-item a:active, .menu-item a.active, .active > a{ + color: #fff; + color: #e0474c; + background: transparent; + } + .menu-item a::before{ + position: absolute; + left: -15px; + content:"\A"; + border-style: solid; + border-width: 8px 0px 8px 8px; + border-color: transparent transparent transparent #fff; + } + .menu-item a:hover:before, .menu-item a:focus:before, .menu-item a:active:before, .menu-item a.active:before{ + border-left-color: #e0474c; + } + .addNaviItem a:focus,.addNaviItem a:hover,.addNaviItem a:active{ + text-decoration: underline; + background: transparent; + color: #e0474c; + } + .card .medium{ + padding: 0px 20px; + } + .card .medium button[type="button"]{ + margin-top: 10px; + margin-bottom: 40px; + } + .card .medium input[type="submit"]{ + margin-top: 10px; + margin-bottom: 40px; + } + li.row ul{ + margin: 0px; + } + .col.username,.col.email,.col.userrole{ + width: 30%; + } + .col.edit{ + width: 10%; + } + li.col.username{ + border-top: 0px; + border-left: 2px solid #70c1b3; + } + .buttonset{ + width: 76%; + padding: 10px 40px; + left: auto; + right: auto; + max-width: 900px; + } + .editor button, .editor a{ + border-radius: 3px; + } +} diff --git a/system/author/css/tachyons.min.css b/system/author/css/tachyons.min.css new file mode 100644 index 0000000..c4970b6 --- /dev/null +++ b/system/author/css/tachyons.min.css @@ -0,0 +1,3 @@ +/*! TACHYONS v4.11.2 | http://tachyons.io */ +/*! normalize.css v8.0.0 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}h1{font-size:2em;margin:.67em 0}hr{box-sizing:content-box;height:0;overflow:visible}pre{font-family:monospace,monospace;font-size:1em}a{background-color:transparent}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,samp{font-family:monospace,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}img{border-style:none}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,input{overflow:visible}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:1px dotted ButtonText}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{vertical-align:baseline}textarea{overflow:auto}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}details{display:block}summary{display:list-item}[hidden],template{display:none}.border-box,a,article,aside,blockquote,body,code,dd,div,dl,dt,fieldset,figcaption,figure,footer,form,h1,h2,h3,h4,h5,h6,header,html,input[type=email],input[type=number],input[type=password],input[type=tel],input[type=text],input[type=url],legend,li,main,nav,ol,p,pre,section,table,td,textarea,th,tr,ul{box-sizing:border-box}.aspect-ratio{height:0;position:relative}.aspect-ratio--16x9{padding-bottom:56.25%}.aspect-ratio--9x16{padding-bottom:177.77%}.aspect-ratio--4x3{padding-bottom:75%}.aspect-ratio--3x4{padding-bottom:133.33%}.aspect-ratio--6x4{padding-bottom:66.6%}.aspect-ratio--4x6{padding-bottom:150%}.aspect-ratio--8x5{padding-bottom:62.5%}.aspect-ratio--5x8{padding-bottom:160%}.aspect-ratio--7x5{padding-bottom:71.42%}.aspect-ratio--5x7{padding-bottom:140%}.aspect-ratio--1x1{padding-bottom:100%}.aspect-ratio--object{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}img{max-width:100%}.cover{background-size:cover!important}.contain{background-size:contain!important}.bg-center{background-position:50%}.bg-center,.bg-top{background-repeat:no-repeat}.bg-top{background-position:top}.bg-right{background-position:100%}.bg-bottom,.bg-right{background-repeat:no-repeat}.bg-bottom{background-position:bottom}.bg-left{background-repeat:no-repeat;background-position:0}.outline{outline:1px solid}.outline-transparent{outline:1px solid transparent}.outline-0{outline:0}.ba{border-style:solid;border-width:1px}.bt{border-top-style:solid;border-top-width:1px}.br{border-right-style:solid;border-right-width:1px}.bb{border-bottom-style:solid;border-bottom-width:1px}.bl{border-left-style:solid;border-left-width:1px}.bn{border-style:none;border-width:0}.b--black{border-color:#000}.b--near-black{border-color:#111}.b--dark-gray{border-color:#333}.b--mid-gray{border-color:#555}.b--gray{border-color:#777}.b--silver{border-color:#999}.b--light-silver{border-color:#aaa}.b--moon-gray{border-color:#ccc}.b--light-gray{border-color:#eee}.b--near-white{border-color:#f4f4f4}.b--white{border-color:#fff}.b--white-90{border-color:hsla(0,0%,100%,.9)}.b--white-80{border-color:hsla(0,0%,100%,.8)}.b--white-70{border-color:hsla(0,0%,100%,.7)}.b--white-60{border-color:hsla(0,0%,100%,.6)}.b--white-50{border-color:hsla(0,0%,100%,.5)}.b--white-40{border-color:hsla(0,0%,100%,.4)}.b--white-30{border-color:hsla(0,0%,100%,.3)}.b--white-20{border-color:hsla(0,0%,100%,.2)}.b--white-10{border-color:hsla(0,0%,100%,.1)}.b--white-05{border-color:hsla(0,0%,100%,.05)}.b--white-025{border-color:hsla(0,0%,100%,.025)}.b--white-0125{border-color:hsla(0,0%,100%,.0125)}.b--black-90{border-color:rgba(0,0,0,.9)}.b--black-80{border-color:rgba(0,0,0,.8)}.b--black-70{border-color:rgba(0,0,0,.7)}.b--black-60{border-color:rgba(0,0,0,.6)}.b--black-50{border-color:rgba(0,0,0,.5)}.b--black-40{border-color:rgba(0,0,0,.4)}.b--black-30{border-color:rgba(0,0,0,.3)}.b--black-20{border-color:rgba(0,0,0,.2)}.b--black-10{border-color:rgba(0,0,0,.1)}.b--black-05{border-color:rgba(0,0,0,.05)}.b--black-025{border-color:rgba(0,0,0,.025)}.b--black-0125{border-color:rgba(0,0,0,.0125)}.b--dark-red{border-color:#e7040f}.b--red{border-color:#ff4136}.b--light-red{border-color:#ff725c}.b--orange{border-color:#ff6300}.b--gold{border-color:#ffb700}.b--yellow{border-color:gold}.b--light-yellow{border-color:#fbf1a9}.b--purple{border-color:#5e2ca5}.b--light-purple{border-color:#a463f2}.b--dark-pink{border-color:#d5008f}.b--hot-pink{border-color:#ff41b4}.b--pink{border-color:#ff80cc}.b--light-pink{border-color:#ffa3d7}.b--dark-green{border-color:#137752}.b--green{border-color:#19a974}.b--light-green{border-color:#9eebcf}.b--navy{border-color:#001b44}.b--dark-blue{border-color:#00449e}.b--blue{border-color:#357edd}.b--light-blue{border-color:#96ccff}.b--lightest-blue{border-color:#cdecff}.b--washed-blue{border-color:#f6fffe}.b--washed-green{border-color:#e8fdf5}.b--washed-yellow{border-color:#fffceb}.b--washed-red{border-color:#ffdfdf}.b--transparent{border-color:transparent}.b--inherit{border-color:inherit}.br0{border-radius:0}.br1{border-radius:.125rem}.br2{border-radius:.25rem}.br3{border-radius:.5rem}.br4{border-radius:1rem}.br-100{border-radius:100%}.br-pill{border-radius:9999px}.br--bottom{border-top-left-radius:0;border-top-right-radius:0}.br--top{border-bottom-right-radius:0}.br--right,.br--top{border-bottom-left-radius:0}.br--right{border-top-left-radius:0}.br--left{border-top-right-radius:0;border-bottom-right-radius:0}.b--dotted{border-style:dotted}.b--dashed{border-style:dashed}.b--solid{border-style:solid}.b--none{border-style:none}.bw0{border-width:0}.bw1{border-width:.125rem}.bw2{border-width:.25rem}.bw3{border-width:.5rem}.bw4{border-width:1rem}.bw5{border-width:2rem}.bt-0{border-top-width:0}.br-0{border-right-width:0}.bb-0{border-bottom-width:0}.bl-0{border-left-width:0}.shadow-1{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.shadow-2{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.shadow-3{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.shadow-4{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.shadow-5{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}.pre{overflow-x:auto;overflow-y:hidden;overflow:scroll}.top-0{top:0}.right-0{right:0}.bottom-0{bottom:0}.left-0{left:0}.top-1{top:1rem}.right-1{right:1rem}.bottom-1{bottom:1rem}.left-1{left:1rem}.top-2{top:2rem}.right-2{right:2rem}.bottom-2{bottom:2rem}.left-2{left:2rem}.top--1{top:-1rem}.right--1{right:-1rem}.bottom--1{bottom:-1rem}.left--1{left:-1rem}.top--2{top:-2rem}.right--2{right:-2rem}.bottom--2{bottom:-2rem}.left--2{left:-2rem}.absolute--fill{top:0;right:0;bottom:0;left:0}.cf:after,.cf:before{content:" ";display:table}.cf:after{clear:both}.cf{*zoom:1}.cl{clear:left}.cr{clear:right}.cb{clear:both}.cn{clear:none}.dn{display:none}.di{display:inline}.db{display:block}.dib{display:inline-block}.dit{display:inline-table}.dt{display:table}.dtc{display:table-cell}.dt-row{display:table-row}.dt-row-group{display:table-row-group}.dt-column{display:table-column}.dt-column-group{display:table-column-group}.dt--fixed{table-layout:fixed;width:100%}.flex{display:flex}.inline-flex{display:inline-flex}.flex-auto{flex:1 1 auto;min-width:0;min-height:0}.flex-none{flex:none}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.flex-wrap{flex-wrap:wrap}.flex-nowrap{flex-wrap:nowrap}.flex-wrap-reverse{flex-wrap:wrap-reverse}.flex-column-reverse{flex-direction:column-reverse}.flex-row-reverse{flex-direction:row-reverse}.items-start{align-items:flex-start}.items-end{align-items:flex-end}.items-center{align-items:center}.items-baseline{align-items:baseline}.items-stretch{align-items:stretch}.self-start{align-self:flex-start}.self-end{align-self:flex-end}.self-center{align-self:center}.self-baseline{align-self:baseline}.self-stretch{align-self:stretch}.justify-start{justify-content:flex-start}.justify-end{justify-content:flex-end}.justify-center{justify-content:center}.justify-between{justify-content:space-between}.justify-around{justify-content:space-around}.content-start{align-content:flex-start}.content-end{align-content:flex-end}.content-center{align-content:center}.content-between{align-content:space-between}.content-around{align-content:space-around}.content-stretch{align-content:stretch}.order-0{order:0}.order-1{order:1}.order-2{order:2}.order-3{order:3}.order-4{order:4}.order-5{order:5}.order-6{order:6}.order-7{order:7}.order-8{order:8}.order-last{order:99999}.flex-grow-0{flex-grow:0}.flex-grow-1{flex-grow:1}.flex-shrink-0{flex-shrink:0}.flex-shrink-1{flex-shrink:1}.fl{float:left}.fl,.fr{_display:inline}.fr{float:right}.fn{float:none}.sans-serif{font-family:-apple-system,BlinkMacSystemFont,avenir next,avenir,helvetica neue,helvetica,ubuntu,roboto,noto,segoe ui,arial,sans-serif}.serif{font-family:georgia,times,serif}.system-sans-serif{font-family:sans-serif}.system-serif{font-family:serif}.code,code{font-family:Consolas,monaco,monospace}.courier{font-family:Courier Next,courier,monospace}.helvetica{font-family:helvetica neue,helvetica,sans-serif}.avenir{font-family:avenir next,avenir,sans-serif}.athelas{font-family:athelas,georgia,serif}.georgia{font-family:georgia,serif}.times{font-family:times,serif}.bodoni{font-family:Bodoni MT,serif}.calisto{font-family:Calisto MT,serif}.garamond{font-family:garamond,serif}.baskerville{font-family:baskerville,serif}.i{font-style:italic}.fs-normal{font-style:normal}.normal{font-weight:400}.b{font-weight:700}.fw1{font-weight:100}.fw2{font-weight:200}.fw3{font-weight:300}.fw4{font-weight:400}.fw5{font-weight:500}.fw6{font-weight:600}.fw7{font-weight:700}.fw8{font-weight:800}.fw9{font-weight:900}.input-reset{-webkit-appearance:none;-moz-appearance:none}.button-reset::-moz-focus-inner,.input-reset::-moz-focus-inner{border:0;padding:0}.h1{height:1rem}.h2{height:2rem}.h3{height:4rem}.h4{height:8rem}.h5{height:16rem}.h-25{height:25%}.h-50{height:50%}.h-75{height:75%}.h-100{height:100%}.min-h-100{min-height:100%}.vh-25{height:25vh}.vh-50{height:50vh}.vh-75{height:75vh}.vh-100{height:100vh}.min-vh-100{min-height:100vh}.h-auto{height:auto}.h-inherit{height:inherit}.tracked{letter-spacing:.1em}.tracked-tight{letter-spacing:-.05em}.tracked-mega{letter-spacing:.25em}.lh-solid{line-height:1}.lh-title{line-height:1.25}.lh-copy{line-height:1.5}.link{text-decoration:none}.link,.link:active,.link:focus,.link:hover,.link:link,.link:visited{transition:color .15s ease-in}.link:focus{outline:1px dotted currentColor}.list{list-style-type:none}.mw-100{max-width:100%}.mw1{max-width:1rem}.mw2{max-width:2rem}.mw3{max-width:4rem}.mw4{max-width:8rem}.mw5{max-width:16rem}.mw6{max-width:32rem}.mw7{max-width:48rem}.mw8{max-width:64rem}.mw9{max-width:96rem}.mw-none{max-width:none}.w1{width:1rem}.w2{width:2rem}.w3{width:4rem}.w4{width:8rem}.w5{width:16rem}.w-10{width:10%}.w-20{width:20%}.w-25{width:25%}.w-30{width:30%}.w-33{width:33%}.w-34{width:34%}.w-40{width:40%}.w-50{width:50%}.w-60{width:60%}.w-70{width:70%}.w-75{width:75%}.w-80{width:80%}.w-90{width:90%}.w-100{width:100%}.w-third{width:33.33333%}.w-two-thirds{width:66.66667%}.w-auto{width:auto}.overflow-visible{overflow:visible}.overflow-hidden{overflow:hidden}.overflow-scroll{overflow:scroll}.overflow-auto{overflow:auto}.overflow-x-visible{overflow-x:visible}.overflow-x-hidden{overflow-x:hidden}.overflow-x-scroll{overflow-x:scroll}.overflow-x-auto{overflow-x:auto}.overflow-y-visible{overflow-y:visible}.overflow-y-hidden{overflow-y:hidden}.overflow-y-scroll{overflow-y:scroll}.overflow-y-auto{overflow-y:auto}.static{position:static}.relative{position:relative}.absolute{position:absolute}.fixed{position:fixed}.o-100{opacity:1}.o-90{opacity:.9}.o-80{opacity:.8}.o-70{opacity:.7}.o-60{opacity:.6}.o-50{opacity:.5}.o-40{opacity:.4}.o-30{opacity:.3}.o-20{opacity:.2}.o-10{opacity:.1}.o-05{opacity:.05}.o-025{opacity:.025}.o-0{opacity:0}.rotate-45{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.rotate-90{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.rotate-135{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.rotate-180{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.rotate-225{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.rotate-270{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.rotate-315{-webkit-transform:rotate(315deg);transform:rotate(315deg)}.black-90{color:rgba(0,0,0,.9)}.black-80{color:rgba(0,0,0,.8)}.black-70{color:rgba(0,0,0,.7)}.black-60{color:rgba(0,0,0,.6)}.black-50{color:rgba(0,0,0,.5)}.black-40{color:rgba(0,0,0,.4)}.black-30{color:rgba(0,0,0,.3)}.black-20{color:rgba(0,0,0,.2)}.black-10{color:rgba(0,0,0,.1)}.black-05{color:rgba(0,0,0,.05)}.white-90{color:hsla(0,0%,100%,.9)}.white-80{color:hsla(0,0%,100%,.8)}.white-70{color:hsla(0,0%,100%,.7)}.white-60{color:hsla(0,0%,100%,.6)}.white-50{color:hsla(0,0%,100%,.5)}.white-40{color:hsla(0,0%,100%,.4)}.white-30{color:hsla(0,0%,100%,.3)}.white-20{color:hsla(0,0%,100%,.2)}.white-10{color:hsla(0,0%,100%,.1)}.black{color:#000}.near-black{color:#111}.dark-gray{color:#333}.mid-gray{color:#555}.gray{color:#777}.silver{color:#999}.light-silver{color:#aaa}.moon-gray{color:#ccc}.light-gray{color:#eee}.near-white{color:#f4f4f4}.white{color:#fff}.dark-red{color:#e7040f}.red{color:#ff4136}.light-red{color:#ff725c}.orange{color:#ff6300}.gold{color:#ffb700}.yellow{color:gold}.light-yellow{color:#fbf1a9}.purple{color:#5e2ca5}.light-purple{color:#a463f2}.dark-pink{color:#d5008f}.hot-pink{color:#ff41b4}.pink{color:#ff80cc}.light-pink{color:#ffa3d7}.dark-green{color:#137752}.green{color:#19a974}.light-green{color:#9eebcf}.navy{color:#001b44}.dark-blue{color:#00449e}.blue{color:#357edd}.light-blue{color:#96ccff}.lightest-blue{color:#cdecff}.washed-blue{color:#f6fffe}.washed-green{color:#e8fdf5}.washed-yellow{color:#fffceb}.washed-red{color:#ffdfdf}.color-inherit{color:inherit}.bg-black-90{background-color:rgba(0,0,0,.9)}.bg-black-80{background-color:rgba(0,0,0,.8)}.bg-black-70{background-color:rgba(0,0,0,.7)}.bg-black-60{background-color:rgba(0,0,0,.6)}.bg-black-50{background-color:rgba(0,0,0,.5)}.bg-black-40{background-color:rgba(0,0,0,.4)}.bg-black-30{background-color:rgba(0,0,0,.3)}.bg-black-20{background-color:rgba(0,0,0,.2)}.bg-black-10{background-color:rgba(0,0,0,.1)}.bg-black-05{background-color:rgba(0,0,0,.05)}.bg-white-90{background-color:hsla(0,0%,100%,.9)}.bg-white-80{background-color:hsla(0,0%,100%,.8)}.bg-white-70{background-color:hsla(0,0%,100%,.7)}.bg-white-60{background-color:hsla(0,0%,100%,.6)}.bg-white-50{background-color:hsla(0,0%,100%,.5)}.bg-white-40{background-color:hsla(0,0%,100%,.4)}.bg-white-30{background-color:hsla(0,0%,100%,.3)}.bg-white-20{background-color:hsla(0,0%,100%,.2)}.bg-white-10{background-color:hsla(0,0%,100%,.1)}.bg-black{background-color:#000}.bg-near-black{background-color:#111}.bg-dark-gray{background-color:#333}.bg-mid-gray{background-color:#555}.bg-gray{background-color:#777}.bg-silver{background-color:#999}.bg-light-silver{background-color:#aaa}.bg-moon-gray{background-color:#ccc}.bg-light-gray{background-color:#eee}.bg-near-white{background-color:#f4f4f4}.bg-white{background-color:#fff}.bg-transparent{background-color:transparent}.bg-dark-red{background-color:#e7040f}.bg-red{background-color:#ff4136}.bg-light-red{background-color:#ff725c}.bg-orange{background-color:#ff6300}.bg-gold{background-color:#ffb700}.bg-yellow{background-color:gold}.bg-light-yellow{background-color:#fbf1a9}.bg-purple{background-color:#5e2ca5}.bg-light-purple{background-color:#a463f2}.bg-dark-pink{background-color:#d5008f}.bg-hot-pink{background-color:#ff41b4}.bg-pink{background-color:#ff80cc}.bg-light-pink{background-color:#ffa3d7}.bg-dark-green{background-color:#137752}.bg-green{background-color:#19a974}.bg-light-green{background-color:#9eebcf}.bg-navy{background-color:#001b44}.bg-dark-blue{background-color:#00449e}.bg-blue{background-color:#357edd}.bg-light-blue{background-color:#96ccff}.bg-lightest-blue{background-color:#cdecff}.bg-washed-blue{background-color:#f6fffe}.bg-washed-green{background-color:#e8fdf5}.bg-washed-yellow{background-color:#fffceb}.bg-washed-red{background-color:#ffdfdf}.bg-inherit{background-color:inherit}.hover-black:focus,.hover-black:hover{color:#000}.hover-near-black:focus,.hover-near-black:hover{color:#111}.hover-dark-gray:focus,.hover-dark-gray:hover{color:#333}.hover-mid-gray:focus,.hover-mid-gray:hover{color:#555}.hover-gray:focus,.hover-gray:hover{color:#777}.hover-silver:focus,.hover-silver:hover{color:#999}.hover-light-silver:focus,.hover-light-silver:hover{color:#aaa}.hover-moon-gray:focus,.hover-moon-gray:hover{color:#ccc}.hover-light-gray:focus,.hover-light-gray:hover{color:#eee}.hover-near-white:focus,.hover-near-white:hover{color:#f4f4f4}.hover-white:focus,.hover-white:hover{color:#fff}.hover-black-90:focus,.hover-black-90:hover{color:rgba(0,0,0,.9)}.hover-black-80:focus,.hover-black-80:hover{color:rgba(0,0,0,.8)}.hover-black-70:focus,.hover-black-70:hover{color:rgba(0,0,0,.7)}.hover-black-60:focus,.hover-black-60:hover{color:rgba(0,0,0,.6)}.hover-black-50:focus,.hover-black-50:hover{color:rgba(0,0,0,.5)}.hover-black-40:focus,.hover-black-40:hover{color:rgba(0,0,0,.4)}.hover-black-30:focus,.hover-black-30:hover{color:rgba(0,0,0,.3)}.hover-black-20:focus,.hover-black-20:hover{color:rgba(0,0,0,.2)}.hover-black-10:focus,.hover-black-10:hover{color:rgba(0,0,0,.1)}.hover-white-90:focus,.hover-white-90:hover{color:hsla(0,0%,100%,.9)}.hover-white-80:focus,.hover-white-80:hover{color:hsla(0,0%,100%,.8)}.hover-white-70:focus,.hover-white-70:hover{color:hsla(0,0%,100%,.7)}.hover-white-60:focus,.hover-white-60:hover{color:hsla(0,0%,100%,.6)}.hover-white-50:focus,.hover-white-50:hover{color:hsla(0,0%,100%,.5)}.hover-white-40:focus,.hover-white-40:hover{color:hsla(0,0%,100%,.4)}.hover-white-30:focus,.hover-white-30:hover{color:hsla(0,0%,100%,.3)}.hover-white-20:focus,.hover-white-20:hover{color:hsla(0,0%,100%,.2)}.hover-white-10:focus,.hover-white-10:hover{color:hsla(0,0%,100%,.1)}.hover-inherit:focus,.hover-inherit:hover{color:inherit}.hover-bg-black:focus,.hover-bg-black:hover{background-color:#000}.hover-bg-near-black:focus,.hover-bg-near-black:hover{background-color:#111}.hover-bg-dark-gray:focus,.hover-bg-dark-gray:hover{background-color:#333}.hover-bg-mid-gray:focus,.hover-bg-mid-gray:hover{background-color:#555}.hover-bg-gray:focus,.hover-bg-gray:hover{background-color:#777}.hover-bg-silver:focus,.hover-bg-silver:hover{background-color:#999}.hover-bg-light-silver:focus,.hover-bg-light-silver:hover{background-color:#aaa}.hover-bg-moon-gray:focus,.hover-bg-moon-gray:hover{background-color:#ccc}.hover-bg-light-gray:focus,.hover-bg-light-gray:hover{background-color:#eee}.hover-bg-near-white:focus,.hover-bg-near-white:hover{background-color:#f4f4f4}.hover-bg-white:focus,.hover-bg-white:hover{background-color:#fff}.hover-bg-transparent:focus,.hover-bg-transparent:hover{background-color:transparent}.hover-bg-black-90:focus,.hover-bg-black-90:hover{background-color:rgba(0,0,0,.9)}.hover-bg-black-80:focus,.hover-bg-black-80:hover{background-color:rgba(0,0,0,.8)}.hover-bg-black-70:focus,.hover-bg-black-70:hover{background-color:rgba(0,0,0,.7)}.hover-bg-black-60:focus,.hover-bg-black-60:hover{background-color:rgba(0,0,0,.6)}.hover-bg-black-50:focus,.hover-bg-black-50:hover{background-color:rgba(0,0,0,.5)}.hover-bg-black-40:focus,.hover-bg-black-40:hover{background-color:rgba(0,0,0,.4)}.hover-bg-black-30:focus,.hover-bg-black-30:hover{background-color:rgba(0,0,0,.3)}.hover-bg-black-20:focus,.hover-bg-black-20:hover{background-color:rgba(0,0,0,.2)}.hover-bg-black-10:focus,.hover-bg-black-10:hover{background-color:rgba(0,0,0,.1)}.hover-bg-white-90:focus,.hover-bg-white-90:hover{background-color:hsla(0,0%,100%,.9)}.hover-bg-white-80:focus,.hover-bg-white-80:hover{background-color:hsla(0,0%,100%,.8)}.hover-bg-white-70:focus,.hover-bg-white-70:hover{background-color:hsla(0,0%,100%,.7)}.hover-bg-white-60:focus,.hover-bg-white-60:hover{background-color:hsla(0,0%,100%,.6)}.hover-bg-white-50:focus,.hover-bg-white-50:hover{background-color:hsla(0,0%,100%,.5)}.hover-bg-white-40:focus,.hover-bg-white-40:hover{background-color:hsla(0,0%,100%,.4)}.hover-bg-white-30:focus,.hover-bg-white-30:hover{background-color:hsla(0,0%,100%,.3)}.hover-bg-white-20:focus,.hover-bg-white-20:hover{background-color:hsla(0,0%,100%,.2)}.hover-bg-white-10:focus,.hover-bg-white-10:hover{background-color:hsla(0,0%,100%,.1)}.hover-dark-red:focus,.hover-dark-red:hover{color:#e7040f}.hover-red:focus,.hover-red:hover{color:#ff4136}.hover-light-red:focus,.hover-light-red:hover{color:#ff725c}.hover-orange:focus,.hover-orange:hover{color:#ff6300}.hover-gold:focus,.hover-gold:hover{color:#ffb700}.hover-yellow:focus,.hover-yellow:hover{color:gold}.hover-light-yellow:focus,.hover-light-yellow:hover{color:#fbf1a9}.hover-purple:focus,.hover-purple:hover{color:#5e2ca5}.hover-light-purple:focus,.hover-light-purple:hover{color:#a463f2}.hover-dark-pink:focus,.hover-dark-pink:hover{color:#d5008f}.hover-hot-pink:focus,.hover-hot-pink:hover{color:#ff41b4}.hover-pink:focus,.hover-pink:hover{color:#ff80cc}.hover-light-pink:focus,.hover-light-pink:hover{color:#ffa3d7}.hover-dark-green:focus,.hover-dark-green:hover{color:#137752}.hover-green:focus,.hover-green:hover{color:#19a974}.hover-light-green:focus,.hover-light-green:hover{color:#9eebcf}.hover-navy:focus,.hover-navy:hover{color:#001b44}.hover-dark-blue:focus,.hover-dark-blue:hover{color:#00449e}.hover-blue:focus,.hover-blue:hover{color:#357edd}.hover-light-blue:focus,.hover-light-blue:hover{color:#96ccff}.hover-lightest-blue:focus,.hover-lightest-blue:hover{color:#cdecff}.hover-washed-blue:focus,.hover-washed-blue:hover{color:#f6fffe}.hover-washed-green:focus,.hover-washed-green:hover{color:#e8fdf5}.hover-washed-yellow:focus,.hover-washed-yellow:hover{color:#fffceb}.hover-washed-red:focus,.hover-washed-red:hover{color:#ffdfdf}.hover-bg-dark-red:focus,.hover-bg-dark-red:hover{background-color:#e7040f}.hover-bg-red:focus,.hover-bg-red:hover{background-color:#ff4136}.hover-bg-light-red:focus,.hover-bg-light-red:hover{background-color:#ff725c}.hover-bg-orange:focus,.hover-bg-orange:hover{background-color:#ff6300}.hover-bg-gold:focus,.hover-bg-gold:hover{background-color:#ffb700}.hover-bg-yellow:focus,.hover-bg-yellow:hover{background-color:gold}.hover-bg-light-yellow:focus,.hover-bg-light-yellow:hover{background-color:#fbf1a9}.hover-bg-purple:focus,.hover-bg-purple:hover{background-color:#5e2ca5}.hover-bg-light-purple:focus,.hover-bg-light-purple:hover{background-color:#a463f2}.hover-bg-dark-pink:focus,.hover-bg-dark-pink:hover{background-color:#d5008f}.hover-bg-hot-pink:focus,.hover-bg-hot-pink:hover{background-color:#ff41b4}.hover-bg-pink:focus,.hover-bg-pink:hover{background-color:#ff80cc}.hover-bg-light-pink:focus,.hover-bg-light-pink:hover{background-color:#ffa3d7}.hover-bg-dark-green:focus,.hover-bg-dark-green:hover{background-color:#137752}.hover-bg-green:focus,.hover-bg-green:hover{background-color:#19a974}.hover-bg-light-green:focus,.hover-bg-light-green:hover{background-color:#9eebcf}.hover-bg-navy:focus,.hover-bg-navy:hover{background-color:#001b44}.hover-bg-dark-blue:focus,.hover-bg-dark-blue:hover{background-color:#00449e}.hover-bg-blue:focus,.hover-bg-blue:hover{background-color:#357edd}.hover-bg-light-blue:focus,.hover-bg-light-blue:hover{background-color:#96ccff}.hover-bg-lightest-blue:focus,.hover-bg-lightest-blue:hover{background-color:#cdecff}.hover-bg-washed-blue:focus,.hover-bg-washed-blue:hover{background-color:#f6fffe}.hover-bg-washed-green:focus,.hover-bg-washed-green:hover{background-color:#e8fdf5}.hover-bg-washed-yellow:focus,.hover-bg-washed-yellow:hover{background-color:#fffceb}.hover-bg-washed-red:focus,.hover-bg-washed-red:hover{background-color:#ffdfdf}.hover-bg-inherit:focus,.hover-bg-inherit:hover{background-color:inherit}.pa0{padding:0}.pa1{padding:.25rem}.pa2{padding:.5rem}.pa3{padding:1rem}.pa4{padding:2rem}.pa5{padding:4rem}.pa6{padding:8rem}.pa7{padding:16rem}.pl0{padding-left:0}.pl1{padding-left:.25rem}.pl2{padding-left:.5rem}.pl3{padding-left:1rem}.pl4{padding-left:2rem}.pl5{padding-left:4rem}.pl6{padding-left:8rem}.pl7{padding-left:16rem}.pr0{padding-right:0}.pr1{padding-right:.25rem}.pr2{padding-right:.5rem}.pr3{padding-right:1rem}.pr4{padding-right:2rem}.pr5{padding-right:4rem}.pr6{padding-right:8rem}.pr7{padding-right:16rem}.pb0{padding-bottom:0}.pb1{padding-bottom:.25rem}.pb2{padding-bottom:.5rem}.pb3{padding-bottom:1rem}.pb4{padding-bottom:2rem}.pb5{padding-bottom:4rem}.pb6{padding-bottom:8rem}.pb7{padding-bottom:16rem}.pt0{padding-top:0}.pt1{padding-top:.25rem}.pt2{padding-top:.5rem}.pt3{padding-top:1rem}.pt4{padding-top:2rem}.pt5{padding-top:4rem}.pt6{padding-top:8rem}.pt7{padding-top:16rem}.pv0{padding-top:0;padding-bottom:0}.pv1{padding-top:.25rem;padding-bottom:.25rem}.pv2{padding-top:.5rem;padding-bottom:.5rem}.pv3{padding-top:1rem;padding-bottom:1rem}.pv4{padding-top:2rem;padding-bottom:2rem}.pv5{padding-top:4rem;padding-bottom:4rem}.pv6{padding-top:8rem;padding-bottom:8rem}.pv7{padding-top:16rem;padding-bottom:16rem}.ph0{padding-left:0;padding-right:0}.ph1{padding-left:.25rem;padding-right:.25rem}.ph2{padding-left:.5rem;padding-right:.5rem}.ph3{padding-left:1rem;padding-right:1rem}.ph4{padding-left:2rem;padding-right:2rem}.ph5{padding-left:4rem;padding-right:4rem}.ph6{padding-left:8rem;padding-right:8rem}.ph7{padding-left:16rem;padding-right:16rem}.ma0{margin:0}.ma1{margin:.25rem}.ma2{margin:.5rem}.ma3{margin:1rem}.ma4{margin:2rem}.ma5{margin:4rem}.ma6{margin:8rem}.ma7{margin:16rem}.ml0{margin-left:0}.ml1{margin-left:.25rem}.ml2{margin-left:.5rem}.ml3{margin-left:1rem}.ml4{margin-left:2rem}.ml5{margin-left:4rem}.ml6{margin-left:8rem}.ml7{margin-left:16rem}.mr0{margin-right:0}.mr1{margin-right:.25rem}.mr2{margin-right:.5rem}.mr3{margin-right:1rem}.mr4{margin-right:2rem}.mr5{margin-right:4rem}.mr6{margin-right:8rem}.mr7{margin-right:16rem}.mb0{margin-bottom:0}.mb1{margin-bottom:.25rem}.mb2{margin-bottom:.5rem}.mb3{margin-bottom:1rem}.mb4{margin-bottom:2rem}.mb5{margin-bottom:4rem}.mb6{margin-bottom:8rem}.mb7{margin-bottom:16rem}.mt0{margin-top:0}.mt1{margin-top:.25rem}.mt2{margin-top:.5rem}.mt3{margin-top:1rem}.mt4{margin-top:2rem}.mt5{margin-top:4rem}.mt6{margin-top:8rem}.mt7{margin-top:16rem}.mv0{margin-top:0;margin-bottom:0}.mv1{margin-top:.25rem;margin-bottom:.25rem}.mv2{margin-top:.5rem;margin-bottom:.5rem}.mv3{margin-top:1rem;margin-bottom:1rem}.mv4{margin-top:2rem;margin-bottom:2rem}.mv5{margin-top:4rem;margin-bottom:4rem}.mv6{margin-top:8rem;margin-bottom:8rem}.mv7{margin-top:16rem;margin-bottom:16rem}.mh0{margin-left:0;margin-right:0}.mh1{margin-left:.25rem;margin-right:.25rem}.mh2{margin-left:.5rem;margin-right:.5rem}.mh3{margin-left:1rem;margin-right:1rem}.mh4{margin-left:2rem;margin-right:2rem}.mh5{margin-left:4rem;margin-right:4rem}.mh6{margin-left:8rem;margin-right:8rem}.mh7{margin-left:16rem;margin-right:16rem}.na1{margin:-.25rem}.na2{margin:-.5rem}.na3{margin:-1rem}.na4{margin:-2rem}.na5{margin:-4rem}.na6{margin:-8rem}.na7{margin:-16rem}.nl1{margin-left:-.25rem}.nl2{margin-left:-.5rem}.nl3{margin-left:-1rem}.nl4{margin-left:-2rem}.nl5{margin-left:-4rem}.nl6{margin-left:-8rem}.nl7{margin-left:-16rem}.nr1{margin-right:-.25rem}.nr2{margin-right:-.5rem}.nr3{margin-right:-1rem}.nr4{margin-right:-2rem}.nr5{margin-right:-4rem}.nr6{margin-right:-8rem}.nr7{margin-right:-16rem}.nb1{margin-bottom:-.25rem}.nb2{margin-bottom:-.5rem}.nb3{margin-bottom:-1rem}.nb4{margin-bottom:-2rem}.nb5{margin-bottom:-4rem}.nb6{margin-bottom:-8rem}.nb7{margin-bottom:-16rem}.nt1{margin-top:-.25rem}.nt2{margin-top:-.5rem}.nt3{margin-top:-1rem}.nt4{margin-top:-2rem}.nt5{margin-top:-4rem}.nt6{margin-top:-8rem}.nt7{margin-top:-16rem}.collapse{border-collapse:collapse;border-spacing:0}.striped--light-silver:nth-child(odd){background-color:#aaa}.striped--moon-gray:nth-child(odd){background-color:#ccc}.striped--light-gray:nth-child(odd){background-color:#eee}.striped--near-white:nth-child(odd){background-color:#f4f4f4}.stripe-light:nth-child(odd){background-color:hsla(0,0%,100%,.1)}.stripe-dark:nth-child(odd){background-color:rgba(0,0,0,.1)}.strike{text-decoration:line-through}.underline{text-decoration:underline}.no-underline{text-decoration:none}.tl{text-align:left}.tr{text-align:right}.tc{text-align:center}.tj{text-align:justify}.ttc{text-transform:capitalize}.ttl{text-transform:lowercase}.ttu{text-transform:uppercase}.ttn{text-transform:none}.f-6,.f-headline{font-size:6rem}.f-5,.f-subheadline{font-size:5rem}.f1{font-size:3rem}.f2{font-size:2.25rem}.f3{font-size:1.5rem}.f4{font-size:1.25rem}.f5{font-size:1rem}.f6{font-size:.875rem}.f7{font-size:.75rem}.measure{max-width:30em}.measure-wide{max-width:34em}.measure-narrow{max-width:20em}.indent{text-indent:1em;margin-top:0;margin-bottom:0}.small-caps{font-variant:small-caps}.truncate{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.overflow-container{overflow-y:scroll}.center{margin-left:auto}.center,.mr-auto{margin-right:auto}.ml-auto{margin-left:auto}.clip{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ws-normal{white-space:normal}.nowrap{white-space:nowrap}.pre{white-space:pre}.v-base{vertical-align:baseline}.v-mid{vertical-align:middle}.v-top{vertical-align:top}.v-btm{vertical-align:bottom}.dim{opacity:1}.dim,.dim:focus,.dim:hover{transition:opacity .15s ease-in}.dim:focus,.dim:hover{opacity:.5}.dim:active{opacity:.8;transition:opacity .15s ease-out}.glow,.glow:focus,.glow:hover{transition:opacity .15s ease-in}.glow:focus,.glow:hover{opacity:1}.hide-child .child{opacity:0;transition:opacity .15s ease-in}.hide-child:active .child,.hide-child:focus .child,.hide-child:hover .child{opacity:1;transition:opacity .15s ease-in}.underline-hover:focus,.underline-hover:hover{text-decoration:underline}.grow{-moz-osx-font-smoothing:grayscale;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateZ(0);transform:translateZ(0);transition:-webkit-transform .25s ease-out;transition:transform .25s ease-out;transition:transform .25s ease-out,-webkit-transform .25s ease-out}.grow:focus,.grow:hover{-webkit-transform:scale(1.05);transform:scale(1.05)}.grow:active{-webkit-transform:scale(.9);transform:scale(.9)}.grow-large{-moz-osx-font-smoothing:grayscale;-webkit-backface-visibility:hidden;backface-visibility:hidden;-webkit-transform:translateZ(0);transform:translateZ(0);transition:-webkit-transform .25s ease-in-out;transition:transform .25s ease-in-out;transition:transform .25s ease-in-out,-webkit-transform .25s ease-in-out}.grow-large:focus,.grow-large:hover{-webkit-transform:scale(1.2);transform:scale(1.2)}.grow-large:active{-webkit-transform:scale(.95);transform:scale(.95)}.pointer:hover,.shadow-hover{cursor:pointer}.shadow-hover{position:relative;transition:all .5s cubic-bezier(.165,.84,.44,1)}.shadow-hover:after{content:"";box-shadow:0 0 16px 2px rgba(0,0,0,.2);border-radius:inherit;opacity:0;position:absolute;top:0;left:0;width:100%;height:100%;z-index:-1;transition:opacity .5s cubic-bezier(.165,.84,.44,1)}.shadow-hover:focus:after,.shadow-hover:hover:after{opacity:1}.bg-animate,.bg-animate:focus,.bg-animate:hover{transition:background-color .15s ease-in-out}.z-0{z-index:0}.z-1{z-index:1}.z-2{z-index:2}.z-3{z-index:3}.z-4{z-index:4}.z-5{z-index:5}.z-999{z-index:999}.z-9999{z-index:9999}.z-max{z-index:2147483647}.z-inherit{z-index:inherit}.z-initial{z-index:auto}.z-unset{z-index:unset}.nested-copy-line-height ol,.nested-copy-line-height p,.nested-copy-line-height ul{line-height:1.5}.nested-headline-line-height h1,.nested-headline-line-height h2,.nested-headline-line-height h3,.nested-headline-line-height h4,.nested-headline-line-height h5,.nested-headline-line-height h6{line-height:1.25}.nested-list-reset ol,.nested-list-reset ul{padding-left:0;margin-left:0;list-style-type:none}.nested-copy-indent p+p{text-indent:1em;margin-top:0;margin-bottom:0}.nested-copy-separator p+p{margin-top:1.5em}.nested-img img{width:100%;max-width:100%;display:block}.nested-links a{color:#357edd;transition:color .15s ease-in}.nested-links a:focus,.nested-links a:hover{color:#96ccff;transition:color .15s ease-in}.debug *{outline:1px solid gold}.debug-white *{outline:1px solid #fff}.debug-black *{outline:1px solid #000}.debug-grid{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAFElEQVR4AWPAC97/9x0eCsAEPgwAVLshdpENIxcAAAAASUVORK5CYII=) repeat 0 0}.debug-grid-16{background:transparent url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAMklEQVR4AWOgCLz/b0epAa6UGuBOqQHOQHLUgFEDnAbcBZ4UGwDOkiCnkIhdgNgNxAYAiYlD+8sEuo8AAAAASUVORK5CYII=) repeat 0 0}.debug-grid-8-solid{background:#fff url(data:image/gif;base64,R0lGODdhCAAIAPEAAADw/wDx/////wAAACwAAAAACAAIAAACDZQvgaeb/lxbAIKA8y0AOw==) repeat 0 0}.debug-grid-16-solid{background:#fff url(data:image/gif;base64,R0lGODdhEAAQAPEAAADw/wDx/xXy/////ywAAAAAEAAQAAACIZyPKckYDQFsb6ZqD85jZ2+BkwiRFKehhqQCQgDHcgwEBQA7) repeat 0 0}@media screen and (min-width:30em){.aspect-ratio-ns{height:0;position:relative}.aspect-ratio--16x9-ns{padding-bottom:56.25%}.aspect-ratio--9x16-ns{padding-bottom:177.77%}.aspect-ratio--4x3-ns{padding-bottom:75%}.aspect-ratio--3x4-ns{padding-bottom:133.33%}.aspect-ratio--6x4-ns{padding-bottom:66.6%}.aspect-ratio--4x6-ns{padding-bottom:150%}.aspect-ratio--8x5-ns{padding-bottom:62.5%}.aspect-ratio--5x8-ns{padding-bottom:160%}.aspect-ratio--7x5-ns{padding-bottom:71.42%}.aspect-ratio--5x7-ns{padding-bottom:140%}.aspect-ratio--1x1-ns{padding-bottom:100%}.aspect-ratio--object-ns{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}.cover-ns{background-size:cover!important}.contain-ns{background-size:contain!important}.bg-center-ns{background-position:50%}.bg-center-ns,.bg-top-ns{background-repeat:no-repeat}.bg-top-ns{background-position:top}.bg-right-ns{background-position:100%}.bg-bottom-ns,.bg-right-ns{background-repeat:no-repeat}.bg-bottom-ns{background-position:bottom}.bg-left-ns{background-repeat:no-repeat;background-position:0}.outline-ns{outline:1px solid}.outline-transparent-ns{outline:1px solid transparent}.outline-0-ns{outline:0}.ba-ns{border-style:solid;border-width:1px}.bt-ns{border-top-style:solid;border-top-width:1px}.br-ns{border-right-style:solid;border-right-width:1px}.bb-ns{border-bottom-style:solid;border-bottom-width:1px}.bl-ns{border-left-style:solid;border-left-width:1px}.bn-ns{border-style:none;border-width:0}.br0-ns{border-radius:0}.br1-ns{border-radius:.125rem}.br2-ns{border-radius:.25rem}.br3-ns{border-radius:.5rem}.br4-ns{border-radius:1rem}.br-100-ns{border-radius:100%}.br-pill-ns{border-radius:9999px}.br--bottom-ns{border-top-left-radius:0;border-top-right-radius:0}.br--top-ns{border-bottom-right-radius:0}.br--right-ns,.br--top-ns{border-bottom-left-radius:0}.br--right-ns{border-top-left-radius:0}.br--left-ns{border-top-right-radius:0;border-bottom-right-radius:0}.b--dotted-ns{border-style:dotted}.b--dashed-ns{border-style:dashed}.b--solid-ns{border-style:solid}.b--none-ns{border-style:none}.bw0-ns{border-width:0}.bw1-ns{border-width:.125rem}.bw2-ns{border-width:.25rem}.bw3-ns{border-width:.5rem}.bw4-ns{border-width:1rem}.bw5-ns{border-width:2rem}.bt-0-ns{border-top-width:0}.br-0-ns{border-right-width:0}.bb-0-ns{border-bottom-width:0}.bl-0-ns{border-left-width:0}.shadow-1-ns{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.shadow-2-ns{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.shadow-3-ns{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.shadow-4-ns{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.shadow-5-ns{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}.top-0-ns{top:0}.left-0-ns{left:0}.right-0-ns{right:0}.bottom-0-ns{bottom:0}.top-1-ns{top:1rem}.left-1-ns{left:1rem}.right-1-ns{right:1rem}.bottom-1-ns{bottom:1rem}.top-2-ns{top:2rem}.left-2-ns{left:2rem}.right-2-ns{right:2rem}.bottom-2-ns{bottom:2rem}.top--1-ns{top:-1rem}.right--1-ns{right:-1rem}.bottom--1-ns{bottom:-1rem}.left--1-ns{left:-1rem}.top--2-ns{top:-2rem}.right--2-ns{right:-2rem}.bottom--2-ns{bottom:-2rem}.left--2-ns{left:-2rem}.absolute--fill-ns{top:0;right:0;bottom:0;left:0}.cl-ns{clear:left}.cr-ns{clear:right}.cb-ns{clear:both}.cn-ns{clear:none}.dn-ns{display:none}.di-ns{display:inline}.db-ns{display:block}.dib-ns{display:inline-block}.dit-ns{display:inline-table}.dt-ns{display:table}.dtc-ns{display:table-cell}.dt-row-ns{display:table-row}.dt-row-group-ns{display:table-row-group}.dt-column-ns{display:table-column}.dt-column-group-ns{display:table-column-group}.dt--fixed-ns{table-layout:fixed;width:100%}.flex-ns{display:flex}.inline-flex-ns{display:inline-flex}.flex-auto-ns{flex:1 1 auto;min-width:0;min-height:0}.flex-none-ns{flex:none}.flex-column-ns{flex-direction:column}.flex-row-ns{flex-direction:row}.flex-wrap-ns{flex-wrap:wrap}.flex-nowrap-ns{flex-wrap:nowrap}.flex-wrap-reverse-ns{flex-wrap:wrap-reverse}.flex-column-reverse-ns{flex-direction:column-reverse}.flex-row-reverse-ns{flex-direction:row-reverse}.items-start-ns{align-items:flex-start}.items-end-ns{align-items:flex-end}.items-center-ns{align-items:center}.items-baseline-ns{align-items:baseline}.items-stretch-ns{align-items:stretch}.self-start-ns{align-self:flex-start}.self-end-ns{align-self:flex-end}.self-center-ns{align-self:center}.self-baseline-ns{align-self:baseline}.self-stretch-ns{align-self:stretch}.justify-start-ns{justify-content:flex-start}.justify-end-ns{justify-content:flex-end}.justify-center-ns{justify-content:center}.justify-between-ns{justify-content:space-between}.justify-around-ns{justify-content:space-around}.content-start-ns{align-content:flex-start}.content-end-ns{align-content:flex-end}.content-center-ns{align-content:center}.content-between-ns{align-content:space-between}.content-around-ns{align-content:space-around}.content-stretch-ns{align-content:stretch}.order-0-ns{order:0}.order-1-ns{order:1}.order-2-ns{order:2}.order-3-ns{order:3}.order-4-ns{order:4}.order-5-ns{order:5}.order-6-ns{order:6}.order-7-ns{order:7}.order-8-ns{order:8}.order-last-ns{order:99999}.flex-grow-0-ns{flex-grow:0}.flex-grow-1-ns{flex-grow:1}.flex-shrink-0-ns{flex-shrink:0}.flex-shrink-1-ns{flex-shrink:1}.fl-ns{float:left}.fl-ns,.fr-ns{_display:inline}.fr-ns{float:right}.fn-ns{float:none}.i-ns{font-style:italic}.fs-normal-ns{font-style:normal}.normal-ns{font-weight:400}.b-ns{font-weight:700}.fw1-ns{font-weight:100}.fw2-ns{font-weight:200}.fw3-ns{font-weight:300}.fw4-ns{font-weight:400}.fw5-ns{font-weight:500}.fw6-ns{font-weight:600}.fw7-ns{font-weight:700}.fw8-ns{font-weight:800}.fw9-ns{font-weight:900}.h1-ns{height:1rem}.h2-ns{height:2rem}.h3-ns{height:4rem}.h4-ns{height:8rem}.h5-ns{height:16rem}.h-25-ns{height:25%}.h-50-ns{height:50%}.h-75-ns{height:75%}.h-100-ns{height:100%}.min-h-100-ns{min-height:100%}.vh-25-ns{height:25vh}.vh-50-ns{height:50vh}.vh-75-ns{height:75vh}.vh-100-ns{height:100vh}.min-vh-100-ns{min-height:100vh}.h-auto-ns{height:auto}.h-inherit-ns{height:inherit}.tracked-ns{letter-spacing:.1em}.tracked-tight-ns{letter-spacing:-.05em}.tracked-mega-ns{letter-spacing:.25em}.lh-solid-ns{line-height:1}.lh-title-ns{line-height:1.25}.lh-copy-ns{line-height:1.5}.mw-100-ns{max-width:100%}.mw1-ns{max-width:1rem}.mw2-ns{max-width:2rem}.mw3-ns{max-width:4rem}.mw4-ns{max-width:8rem}.mw5-ns{max-width:16rem}.mw6-ns{max-width:32rem}.mw7-ns{max-width:48rem}.mw8-ns{max-width:64rem}.mw9-ns{max-width:96rem}.mw-none-ns{max-width:none}.w1-ns{width:1rem}.w2-ns{width:2rem}.w3-ns{width:4rem}.w4-ns{width:8rem}.w5-ns{width:16rem}.w-10-ns{width:10%}.w-20-ns{width:20%}.w-25-ns{width:25%}.w-30-ns{width:30%}.w-33-ns{width:33%}.w-34-ns{width:34%}.w-40-ns{width:40%}.w-50-ns{width:50%}.w-60-ns{width:60%}.w-70-ns{width:70%}.w-75-ns{width:75%}.w-80-ns{width:80%}.w-90-ns{width:90%}.w-100-ns{width:100%}.w-third-ns{width:33.33333%}.w-two-thirds-ns{width:66.66667%}.w-auto-ns{width:auto}.overflow-visible-ns{overflow:visible}.overflow-hidden-ns{overflow:hidden}.overflow-scroll-ns{overflow:scroll}.overflow-auto-ns{overflow:auto}.overflow-x-visible-ns{overflow-x:visible}.overflow-x-hidden-ns{overflow-x:hidden}.overflow-x-scroll-ns{overflow-x:scroll}.overflow-x-auto-ns{overflow-x:auto}.overflow-y-visible-ns{overflow-y:visible}.overflow-y-hidden-ns{overflow-y:hidden}.overflow-y-scroll-ns{overflow-y:scroll}.overflow-y-auto-ns{overflow-y:auto}.static-ns{position:static}.relative-ns{position:relative}.absolute-ns{position:absolute}.fixed-ns{position:fixed}.rotate-45-ns{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.rotate-90-ns{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.rotate-135-ns{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.rotate-180-ns{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.rotate-225-ns{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.rotate-270-ns{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.rotate-315-ns{-webkit-transform:rotate(315deg);transform:rotate(315deg)}.pa0-ns{padding:0}.pa1-ns{padding:.25rem}.pa2-ns{padding:.5rem}.pa3-ns{padding:1rem}.pa4-ns{padding:2rem}.pa5-ns{padding:4rem}.pa6-ns{padding:8rem}.pa7-ns{padding:16rem}.pl0-ns{padding-left:0}.pl1-ns{padding-left:.25rem}.pl2-ns{padding-left:.5rem}.pl3-ns{padding-left:1rem}.pl4-ns{padding-left:2rem}.pl5-ns{padding-left:4rem}.pl6-ns{padding-left:8rem}.pl7-ns{padding-left:16rem}.pr0-ns{padding-right:0}.pr1-ns{padding-right:.25rem}.pr2-ns{padding-right:.5rem}.pr3-ns{padding-right:1rem}.pr4-ns{padding-right:2rem}.pr5-ns{padding-right:4rem}.pr6-ns{padding-right:8rem}.pr7-ns{padding-right:16rem}.pb0-ns{padding-bottom:0}.pb1-ns{padding-bottom:.25rem}.pb2-ns{padding-bottom:.5rem}.pb3-ns{padding-bottom:1rem}.pb4-ns{padding-bottom:2rem}.pb5-ns{padding-bottom:4rem}.pb6-ns{padding-bottom:8rem}.pb7-ns{padding-bottom:16rem}.pt0-ns{padding-top:0}.pt1-ns{padding-top:.25rem}.pt2-ns{padding-top:.5rem}.pt3-ns{padding-top:1rem}.pt4-ns{padding-top:2rem}.pt5-ns{padding-top:4rem}.pt6-ns{padding-top:8rem}.pt7-ns{padding-top:16rem}.pv0-ns{padding-top:0;padding-bottom:0}.pv1-ns{padding-top:.25rem;padding-bottom:.25rem}.pv2-ns{padding-top:.5rem;padding-bottom:.5rem}.pv3-ns{padding-top:1rem;padding-bottom:1rem}.pv4-ns{padding-top:2rem;padding-bottom:2rem}.pv5-ns{padding-top:4rem;padding-bottom:4rem}.pv6-ns{padding-top:8rem;padding-bottom:8rem}.pv7-ns{padding-top:16rem;padding-bottom:16rem}.ph0-ns{padding-left:0;padding-right:0}.ph1-ns{padding-left:.25rem;padding-right:.25rem}.ph2-ns{padding-left:.5rem;padding-right:.5rem}.ph3-ns{padding-left:1rem;padding-right:1rem}.ph4-ns{padding-left:2rem;padding-right:2rem}.ph5-ns{padding-left:4rem;padding-right:4rem}.ph6-ns{padding-left:8rem;padding-right:8rem}.ph7-ns{padding-left:16rem;padding-right:16rem}.ma0-ns{margin:0}.ma1-ns{margin:.25rem}.ma2-ns{margin:.5rem}.ma3-ns{margin:1rem}.ma4-ns{margin:2rem}.ma5-ns{margin:4rem}.ma6-ns{margin:8rem}.ma7-ns{margin:16rem}.ml0-ns{margin-left:0}.ml1-ns{margin-left:.25rem}.ml2-ns{margin-left:.5rem}.ml3-ns{margin-left:1rem}.ml4-ns{margin-left:2rem}.ml5-ns{margin-left:4rem}.ml6-ns{margin-left:8rem}.ml7-ns{margin-left:16rem}.mr0-ns{margin-right:0}.mr1-ns{margin-right:.25rem}.mr2-ns{margin-right:.5rem}.mr3-ns{margin-right:1rem}.mr4-ns{margin-right:2rem}.mr5-ns{margin-right:4rem}.mr6-ns{margin-right:8rem}.mr7-ns{margin-right:16rem}.mb0-ns{margin-bottom:0}.mb1-ns{margin-bottom:.25rem}.mb2-ns{margin-bottom:.5rem}.mb3-ns{margin-bottom:1rem}.mb4-ns{margin-bottom:2rem}.mb5-ns{margin-bottom:4rem}.mb6-ns{margin-bottom:8rem}.mb7-ns{margin-bottom:16rem}.mt0-ns{margin-top:0}.mt1-ns{margin-top:.25rem}.mt2-ns{margin-top:.5rem}.mt3-ns{margin-top:1rem}.mt4-ns{margin-top:2rem}.mt5-ns{margin-top:4rem}.mt6-ns{margin-top:8rem}.mt7-ns{margin-top:16rem}.mv0-ns{margin-top:0;margin-bottom:0}.mv1-ns{margin-top:.25rem;margin-bottom:.25rem}.mv2-ns{margin-top:.5rem;margin-bottom:.5rem}.mv3-ns{margin-top:1rem;margin-bottom:1rem}.mv4-ns{margin-top:2rem;margin-bottom:2rem}.mv5-ns{margin-top:4rem;margin-bottom:4rem}.mv6-ns{margin-top:8rem;margin-bottom:8rem}.mv7-ns{margin-top:16rem;margin-bottom:16rem}.mh0-ns{margin-left:0;margin-right:0}.mh1-ns{margin-left:.25rem;margin-right:.25rem}.mh2-ns{margin-left:.5rem;margin-right:.5rem}.mh3-ns{margin-left:1rem;margin-right:1rem}.mh4-ns{margin-left:2rem;margin-right:2rem}.mh5-ns{margin-left:4rem;margin-right:4rem}.mh6-ns{margin-left:8rem;margin-right:8rem}.mh7-ns{margin-left:16rem;margin-right:16rem}.na1-ns{margin:-.25rem}.na2-ns{margin:-.5rem}.na3-ns{margin:-1rem}.na4-ns{margin:-2rem}.na5-ns{margin:-4rem}.na6-ns{margin:-8rem}.na7-ns{margin:-16rem}.nl1-ns{margin-left:-.25rem}.nl2-ns{margin-left:-.5rem}.nl3-ns{margin-left:-1rem}.nl4-ns{margin-left:-2rem}.nl5-ns{margin-left:-4rem}.nl6-ns{margin-left:-8rem}.nl7-ns{margin-left:-16rem}.nr1-ns{margin-right:-.25rem}.nr2-ns{margin-right:-.5rem}.nr3-ns{margin-right:-1rem}.nr4-ns{margin-right:-2rem}.nr5-ns{margin-right:-4rem}.nr6-ns{margin-right:-8rem}.nr7-ns{margin-right:-16rem}.nb1-ns{margin-bottom:-.25rem}.nb2-ns{margin-bottom:-.5rem}.nb3-ns{margin-bottom:-1rem}.nb4-ns{margin-bottom:-2rem}.nb5-ns{margin-bottom:-4rem}.nb6-ns{margin-bottom:-8rem}.nb7-ns{margin-bottom:-16rem}.nt1-ns{margin-top:-.25rem}.nt2-ns{margin-top:-.5rem}.nt3-ns{margin-top:-1rem}.nt4-ns{margin-top:-2rem}.nt5-ns{margin-top:-4rem}.nt6-ns{margin-top:-8rem}.nt7-ns{margin-top:-16rem}.strike-ns{text-decoration:line-through}.underline-ns{text-decoration:underline}.no-underline-ns{text-decoration:none}.tl-ns{text-align:left}.tr-ns{text-align:right}.tc-ns{text-align:center}.tj-ns{text-align:justify}.ttc-ns{text-transform:capitalize}.ttl-ns{text-transform:lowercase}.ttu-ns{text-transform:uppercase}.ttn-ns{text-transform:none}.f-6-ns,.f-headline-ns{font-size:6rem}.f-5-ns,.f-subheadline-ns{font-size:5rem}.f1-ns{font-size:3rem}.f2-ns{font-size:2.25rem}.f3-ns{font-size:1.5rem}.f4-ns{font-size:1.25rem}.f5-ns{font-size:1rem}.f6-ns{font-size:.875rem}.f7-ns{font-size:.75rem}.measure-ns{max-width:30em}.measure-wide-ns{max-width:34em}.measure-narrow-ns{max-width:20em}.indent-ns{text-indent:1em;margin-top:0;margin-bottom:0}.small-caps-ns{font-variant:small-caps}.truncate-ns{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.center-ns{margin-left:auto}.center-ns,.mr-auto-ns{margin-right:auto}.ml-auto-ns{margin-left:auto}.clip-ns{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ws-normal-ns{white-space:normal}.nowrap-ns{white-space:nowrap}.pre-ns{white-space:pre}.v-base-ns{vertical-align:baseline}.v-mid-ns{vertical-align:middle}.v-top-ns{vertical-align:top}.v-btm-ns{vertical-align:bottom}}@media screen and (min-width:30em) and (max-width:60em){.aspect-ratio-m{height:0;position:relative}.aspect-ratio--16x9-m{padding-bottom:56.25%}.aspect-ratio--9x16-m{padding-bottom:177.77%}.aspect-ratio--4x3-m{padding-bottom:75%}.aspect-ratio--3x4-m{padding-bottom:133.33%}.aspect-ratio--6x4-m{padding-bottom:66.6%}.aspect-ratio--4x6-m{padding-bottom:150%}.aspect-ratio--8x5-m{padding-bottom:62.5%}.aspect-ratio--5x8-m{padding-bottom:160%}.aspect-ratio--7x5-m{padding-bottom:71.42%}.aspect-ratio--5x7-m{padding-bottom:140%}.aspect-ratio--1x1-m{padding-bottom:100%}.aspect-ratio--object-m{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}.cover-m{background-size:cover!important}.contain-m{background-size:contain!important}.bg-center-m{background-position:50%}.bg-center-m,.bg-top-m{background-repeat:no-repeat}.bg-top-m{background-position:top}.bg-right-m{background-position:100%}.bg-bottom-m,.bg-right-m{background-repeat:no-repeat}.bg-bottom-m{background-position:bottom}.bg-left-m{background-repeat:no-repeat;background-position:0}.outline-m{outline:1px solid}.outline-transparent-m{outline:1px solid transparent}.outline-0-m{outline:0}.ba-m{border-style:solid;border-width:1px}.bt-m{border-top-style:solid;border-top-width:1px}.br-m{border-right-style:solid;border-right-width:1px}.bb-m{border-bottom-style:solid;border-bottom-width:1px}.bl-m{border-left-style:solid;border-left-width:1px}.bn-m{border-style:none;border-width:0}.br0-m{border-radius:0}.br1-m{border-radius:.125rem}.br2-m{border-radius:.25rem}.br3-m{border-radius:.5rem}.br4-m{border-radius:1rem}.br-100-m{border-radius:100%}.br-pill-m{border-radius:9999px}.br--bottom-m{border-top-left-radius:0;border-top-right-radius:0}.br--top-m{border-bottom-right-radius:0}.br--right-m,.br--top-m{border-bottom-left-radius:0}.br--right-m{border-top-left-radius:0}.br--left-m{border-top-right-radius:0;border-bottom-right-radius:0}.b--dotted-m{border-style:dotted}.b--dashed-m{border-style:dashed}.b--solid-m{border-style:solid}.b--none-m{border-style:none}.bw0-m{border-width:0}.bw1-m{border-width:.125rem}.bw2-m{border-width:.25rem}.bw3-m{border-width:.5rem}.bw4-m{border-width:1rem}.bw5-m{border-width:2rem}.bt-0-m{border-top-width:0}.br-0-m{border-right-width:0}.bb-0-m{border-bottom-width:0}.bl-0-m{border-left-width:0}.shadow-1-m{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.shadow-2-m{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.shadow-3-m{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.shadow-4-m{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.shadow-5-m{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}.top-0-m{top:0}.left-0-m{left:0}.right-0-m{right:0}.bottom-0-m{bottom:0}.top-1-m{top:1rem}.left-1-m{left:1rem}.right-1-m{right:1rem}.bottom-1-m{bottom:1rem}.top-2-m{top:2rem}.left-2-m{left:2rem}.right-2-m{right:2rem}.bottom-2-m{bottom:2rem}.top--1-m{top:-1rem}.right--1-m{right:-1rem}.bottom--1-m{bottom:-1rem}.left--1-m{left:-1rem}.top--2-m{top:-2rem}.right--2-m{right:-2rem}.bottom--2-m{bottom:-2rem}.left--2-m{left:-2rem}.absolute--fill-m{top:0;right:0;bottom:0;left:0}.cl-m{clear:left}.cr-m{clear:right}.cb-m{clear:both}.cn-m{clear:none}.dn-m{display:none}.di-m{display:inline}.db-m{display:block}.dib-m{display:inline-block}.dit-m{display:inline-table}.dt-m{display:table}.dtc-m{display:table-cell}.dt-row-m{display:table-row}.dt-row-group-m{display:table-row-group}.dt-column-m{display:table-column}.dt-column-group-m{display:table-column-group}.dt--fixed-m{table-layout:fixed;width:100%}.flex-m{display:flex}.inline-flex-m{display:inline-flex}.flex-auto-m{flex:1 1 auto;min-width:0;min-height:0}.flex-none-m{flex:none}.flex-column-m{flex-direction:column}.flex-row-m{flex-direction:row}.flex-wrap-m{flex-wrap:wrap}.flex-nowrap-m{flex-wrap:nowrap}.flex-wrap-reverse-m{flex-wrap:wrap-reverse}.flex-column-reverse-m{flex-direction:column-reverse}.flex-row-reverse-m{flex-direction:row-reverse}.items-start-m{align-items:flex-start}.items-end-m{align-items:flex-end}.items-center-m{align-items:center}.items-baseline-m{align-items:baseline}.items-stretch-m{align-items:stretch}.self-start-m{align-self:flex-start}.self-end-m{align-self:flex-end}.self-center-m{align-self:center}.self-baseline-m{align-self:baseline}.self-stretch-m{align-self:stretch}.justify-start-m{justify-content:flex-start}.justify-end-m{justify-content:flex-end}.justify-center-m{justify-content:center}.justify-between-m{justify-content:space-between}.justify-around-m{justify-content:space-around}.content-start-m{align-content:flex-start}.content-end-m{align-content:flex-end}.content-center-m{align-content:center}.content-between-m{align-content:space-between}.content-around-m{align-content:space-around}.content-stretch-m{align-content:stretch}.order-0-m{order:0}.order-1-m{order:1}.order-2-m{order:2}.order-3-m{order:3}.order-4-m{order:4}.order-5-m{order:5}.order-6-m{order:6}.order-7-m{order:7}.order-8-m{order:8}.order-last-m{order:99999}.flex-grow-0-m{flex-grow:0}.flex-grow-1-m{flex-grow:1}.flex-shrink-0-m{flex-shrink:0}.flex-shrink-1-m{flex-shrink:1}.fl-m{float:left}.fl-m,.fr-m{_display:inline}.fr-m{float:right}.fn-m{float:none}.i-m{font-style:italic}.fs-normal-m{font-style:normal}.normal-m{font-weight:400}.b-m{font-weight:700}.fw1-m{font-weight:100}.fw2-m{font-weight:200}.fw3-m{font-weight:300}.fw4-m{font-weight:400}.fw5-m{font-weight:500}.fw6-m{font-weight:600}.fw7-m{font-weight:700}.fw8-m{font-weight:800}.fw9-m{font-weight:900}.h1-m{height:1rem}.h2-m{height:2rem}.h3-m{height:4rem}.h4-m{height:8rem}.h5-m{height:16rem}.h-25-m{height:25%}.h-50-m{height:50%}.h-75-m{height:75%}.h-100-m{height:100%}.min-h-100-m{min-height:100%}.vh-25-m{height:25vh}.vh-50-m{height:50vh}.vh-75-m{height:75vh}.vh-100-m{height:100vh}.min-vh-100-m{min-height:100vh}.h-auto-m{height:auto}.h-inherit-m{height:inherit}.tracked-m{letter-spacing:.1em}.tracked-tight-m{letter-spacing:-.05em}.tracked-mega-m{letter-spacing:.25em}.lh-solid-m{line-height:1}.lh-title-m{line-height:1.25}.lh-copy-m{line-height:1.5}.mw-100-m{max-width:100%}.mw1-m{max-width:1rem}.mw2-m{max-width:2rem}.mw3-m{max-width:4rem}.mw4-m{max-width:8rem}.mw5-m{max-width:16rem}.mw6-m{max-width:32rem}.mw7-m{max-width:48rem}.mw8-m{max-width:64rem}.mw9-m{max-width:96rem}.mw-none-m{max-width:none}.w1-m{width:1rem}.w2-m{width:2rem}.w3-m{width:4rem}.w4-m{width:8rem}.w5-m{width:16rem}.w-10-m{width:10%}.w-20-m{width:20%}.w-25-m{width:25%}.w-30-m{width:30%}.w-33-m{width:33%}.w-34-m{width:34%}.w-40-m{width:40%}.w-50-m{width:50%}.w-60-m{width:60%}.w-70-m{width:70%}.w-75-m{width:75%}.w-80-m{width:80%}.w-90-m{width:90%}.w-100-m{width:100%}.w-third-m{width:33.33333%}.w-two-thirds-m{width:66.66667%}.w-auto-m{width:auto}.overflow-visible-m{overflow:visible}.overflow-hidden-m{overflow:hidden}.overflow-scroll-m{overflow:scroll}.overflow-auto-m{overflow:auto}.overflow-x-visible-m{overflow-x:visible}.overflow-x-hidden-m{overflow-x:hidden}.overflow-x-scroll-m{overflow-x:scroll}.overflow-x-auto-m{overflow-x:auto}.overflow-y-visible-m{overflow-y:visible}.overflow-y-hidden-m{overflow-y:hidden}.overflow-y-scroll-m{overflow-y:scroll}.overflow-y-auto-m{overflow-y:auto}.static-m{position:static}.relative-m{position:relative}.absolute-m{position:absolute}.fixed-m{position:fixed}.rotate-45-m{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.rotate-90-m{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.rotate-135-m{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.rotate-180-m{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.rotate-225-m{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.rotate-270-m{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.rotate-315-m{-webkit-transform:rotate(315deg);transform:rotate(315deg)}.pa0-m{padding:0}.pa1-m{padding:.25rem}.pa2-m{padding:.5rem}.pa3-m{padding:1rem}.pa4-m{padding:2rem}.pa5-m{padding:4rem}.pa6-m{padding:8rem}.pa7-m{padding:16rem}.pl0-m{padding-left:0}.pl1-m{padding-left:.25rem}.pl2-m{padding-left:.5rem}.pl3-m{padding-left:1rem}.pl4-m{padding-left:2rem}.pl5-m{padding-left:4rem}.pl6-m{padding-left:8rem}.pl7-m{padding-left:16rem}.pr0-m{padding-right:0}.pr1-m{padding-right:.25rem}.pr2-m{padding-right:.5rem}.pr3-m{padding-right:1rem}.pr4-m{padding-right:2rem}.pr5-m{padding-right:4rem}.pr6-m{padding-right:8rem}.pr7-m{padding-right:16rem}.pb0-m{padding-bottom:0}.pb1-m{padding-bottom:.25rem}.pb2-m{padding-bottom:.5rem}.pb3-m{padding-bottom:1rem}.pb4-m{padding-bottom:2rem}.pb5-m{padding-bottom:4rem}.pb6-m{padding-bottom:8rem}.pb7-m{padding-bottom:16rem}.pt0-m{padding-top:0}.pt1-m{padding-top:.25rem}.pt2-m{padding-top:.5rem}.pt3-m{padding-top:1rem}.pt4-m{padding-top:2rem}.pt5-m{padding-top:4rem}.pt6-m{padding-top:8rem}.pt7-m{padding-top:16rem}.pv0-m{padding-top:0;padding-bottom:0}.pv1-m{padding-top:.25rem;padding-bottom:.25rem}.pv2-m{padding-top:.5rem;padding-bottom:.5rem}.pv3-m{padding-top:1rem;padding-bottom:1rem}.pv4-m{padding-top:2rem;padding-bottom:2rem}.pv5-m{padding-top:4rem;padding-bottom:4rem}.pv6-m{padding-top:8rem;padding-bottom:8rem}.pv7-m{padding-top:16rem;padding-bottom:16rem}.ph0-m{padding-left:0;padding-right:0}.ph1-m{padding-left:.25rem;padding-right:.25rem}.ph2-m{padding-left:.5rem;padding-right:.5rem}.ph3-m{padding-left:1rem;padding-right:1rem}.ph4-m{padding-left:2rem;padding-right:2rem}.ph5-m{padding-left:4rem;padding-right:4rem}.ph6-m{padding-left:8rem;padding-right:8rem}.ph7-m{padding-left:16rem;padding-right:16rem}.ma0-m{margin:0}.ma1-m{margin:.25rem}.ma2-m{margin:.5rem}.ma3-m{margin:1rem}.ma4-m{margin:2rem}.ma5-m{margin:4rem}.ma6-m{margin:8rem}.ma7-m{margin:16rem}.ml0-m{margin-left:0}.ml1-m{margin-left:.25rem}.ml2-m{margin-left:.5rem}.ml3-m{margin-left:1rem}.ml4-m{margin-left:2rem}.ml5-m{margin-left:4rem}.ml6-m{margin-left:8rem}.ml7-m{margin-left:16rem}.mr0-m{margin-right:0}.mr1-m{margin-right:.25rem}.mr2-m{margin-right:.5rem}.mr3-m{margin-right:1rem}.mr4-m{margin-right:2rem}.mr5-m{margin-right:4rem}.mr6-m{margin-right:8rem}.mr7-m{margin-right:16rem}.mb0-m{margin-bottom:0}.mb1-m{margin-bottom:.25rem}.mb2-m{margin-bottom:.5rem}.mb3-m{margin-bottom:1rem}.mb4-m{margin-bottom:2rem}.mb5-m{margin-bottom:4rem}.mb6-m{margin-bottom:8rem}.mb7-m{margin-bottom:16rem}.mt0-m{margin-top:0}.mt1-m{margin-top:.25rem}.mt2-m{margin-top:.5rem}.mt3-m{margin-top:1rem}.mt4-m{margin-top:2rem}.mt5-m{margin-top:4rem}.mt6-m{margin-top:8rem}.mt7-m{margin-top:16rem}.mv0-m{margin-top:0;margin-bottom:0}.mv1-m{margin-top:.25rem;margin-bottom:.25rem}.mv2-m{margin-top:.5rem;margin-bottom:.5rem}.mv3-m{margin-top:1rem;margin-bottom:1rem}.mv4-m{margin-top:2rem;margin-bottom:2rem}.mv5-m{margin-top:4rem;margin-bottom:4rem}.mv6-m{margin-top:8rem;margin-bottom:8rem}.mv7-m{margin-top:16rem;margin-bottom:16rem}.mh0-m{margin-left:0;margin-right:0}.mh1-m{margin-left:.25rem;margin-right:.25rem}.mh2-m{margin-left:.5rem;margin-right:.5rem}.mh3-m{margin-left:1rem;margin-right:1rem}.mh4-m{margin-left:2rem;margin-right:2rem}.mh5-m{margin-left:4rem;margin-right:4rem}.mh6-m{margin-left:8rem;margin-right:8rem}.mh7-m{margin-left:16rem;margin-right:16rem}.na1-m{margin:-.25rem}.na2-m{margin:-.5rem}.na3-m{margin:-1rem}.na4-m{margin:-2rem}.na5-m{margin:-4rem}.na6-m{margin:-8rem}.na7-m{margin:-16rem}.nl1-m{margin-left:-.25rem}.nl2-m{margin-left:-.5rem}.nl3-m{margin-left:-1rem}.nl4-m{margin-left:-2rem}.nl5-m{margin-left:-4rem}.nl6-m{margin-left:-8rem}.nl7-m{margin-left:-16rem}.nr1-m{margin-right:-.25rem}.nr2-m{margin-right:-.5rem}.nr3-m{margin-right:-1rem}.nr4-m{margin-right:-2rem}.nr5-m{margin-right:-4rem}.nr6-m{margin-right:-8rem}.nr7-m{margin-right:-16rem}.nb1-m{margin-bottom:-.25rem}.nb2-m{margin-bottom:-.5rem}.nb3-m{margin-bottom:-1rem}.nb4-m{margin-bottom:-2rem}.nb5-m{margin-bottom:-4rem}.nb6-m{margin-bottom:-8rem}.nb7-m{margin-bottom:-16rem}.nt1-m{margin-top:-.25rem}.nt2-m{margin-top:-.5rem}.nt3-m{margin-top:-1rem}.nt4-m{margin-top:-2rem}.nt5-m{margin-top:-4rem}.nt6-m{margin-top:-8rem}.nt7-m{margin-top:-16rem}.strike-m{text-decoration:line-through}.underline-m{text-decoration:underline}.no-underline-m{text-decoration:none}.tl-m{text-align:left}.tr-m{text-align:right}.tc-m{text-align:center}.tj-m{text-align:justify}.ttc-m{text-transform:capitalize}.ttl-m{text-transform:lowercase}.ttu-m{text-transform:uppercase}.ttn-m{text-transform:none}.f-6-m,.f-headline-m{font-size:6rem}.f-5-m,.f-subheadline-m{font-size:5rem}.f1-m{font-size:3rem}.f2-m{font-size:2.25rem}.f3-m{font-size:1.5rem}.f4-m{font-size:1.25rem}.f5-m{font-size:1rem}.f6-m{font-size:.875rem}.f7-m{font-size:.75rem}.measure-m{max-width:30em}.measure-wide-m{max-width:34em}.measure-narrow-m{max-width:20em}.indent-m{text-indent:1em;margin-top:0;margin-bottom:0}.small-caps-m{font-variant:small-caps}.truncate-m{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.center-m{margin-left:auto}.center-m,.mr-auto-m{margin-right:auto}.ml-auto-m{margin-left:auto}.clip-m{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ws-normal-m{white-space:normal}.nowrap-m{white-space:nowrap}.pre-m{white-space:pre}.v-base-m{vertical-align:baseline}.v-mid-m{vertical-align:middle}.v-top-m{vertical-align:top}.v-btm-m{vertical-align:bottom}}@media screen and (min-width:60em){.aspect-ratio-l{height:0;position:relative}.aspect-ratio--16x9-l{padding-bottom:56.25%}.aspect-ratio--9x16-l{padding-bottom:177.77%}.aspect-ratio--4x3-l{padding-bottom:75%}.aspect-ratio--3x4-l{padding-bottom:133.33%}.aspect-ratio--6x4-l{padding-bottom:66.6%}.aspect-ratio--4x6-l{padding-bottom:150%}.aspect-ratio--8x5-l{padding-bottom:62.5%}.aspect-ratio--5x8-l{padding-bottom:160%}.aspect-ratio--7x5-l{padding-bottom:71.42%}.aspect-ratio--5x7-l{padding-bottom:140%}.aspect-ratio--1x1-l{padding-bottom:100%}.aspect-ratio--object-l{position:absolute;top:0;right:0;bottom:0;left:0;width:100%;height:100%;z-index:100}.cover-l{background-size:cover!important}.contain-l{background-size:contain!important}.bg-center-l{background-position:50%}.bg-center-l,.bg-top-l{background-repeat:no-repeat}.bg-top-l{background-position:top}.bg-right-l{background-position:100%}.bg-bottom-l,.bg-right-l{background-repeat:no-repeat}.bg-bottom-l{background-position:bottom}.bg-left-l{background-repeat:no-repeat;background-position:0}.outline-l{outline:1px solid}.outline-transparent-l{outline:1px solid transparent}.outline-0-l{outline:0}.ba-l{border-style:solid;border-width:1px}.bt-l{border-top-style:solid;border-top-width:1px}.br-l{border-right-style:solid;border-right-width:1px}.bb-l{border-bottom-style:solid;border-bottom-width:1px}.bl-l{border-left-style:solid;border-left-width:1px}.bn-l{border-style:none;border-width:0}.br0-l{border-radius:0}.br1-l{border-radius:.125rem}.br2-l{border-radius:.25rem}.br3-l{border-radius:.5rem}.br4-l{border-radius:1rem}.br-100-l{border-radius:100%}.br-pill-l{border-radius:9999px}.br--bottom-l{border-top-left-radius:0;border-top-right-radius:0}.br--top-l{border-bottom-right-radius:0}.br--right-l,.br--top-l{border-bottom-left-radius:0}.br--right-l{border-top-left-radius:0}.br--left-l{border-top-right-radius:0;border-bottom-right-radius:0}.b--dotted-l{border-style:dotted}.b--dashed-l{border-style:dashed}.b--solid-l{border-style:solid}.b--none-l{border-style:none}.bw0-l{border-width:0}.bw1-l{border-width:.125rem}.bw2-l{border-width:.25rem}.bw3-l{border-width:.5rem}.bw4-l{border-width:1rem}.bw5-l{border-width:2rem}.bt-0-l{border-top-width:0}.br-0-l{border-right-width:0}.bb-0-l{border-bottom-width:0}.bl-0-l{border-left-width:0}.shadow-1-l{box-shadow:0 0 4px 2px rgba(0,0,0,.2)}.shadow-2-l{box-shadow:0 0 8px 2px rgba(0,0,0,.2)}.shadow-3-l{box-shadow:2px 2px 4px 2px rgba(0,0,0,.2)}.shadow-4-l{box-shadow:2px 2px 8px 0 rgba(0,0,0,.2)}.shadow-5-l{box-shadow:4px 4px 8px 0 rgba(0,0,0,.2)}.top-0-l{top:0}.left-0-l{left:0}.right-0-l{right:0}.bottom-0-l{bottom:0}.top-1-l{top:1rem}.left-1-l{left:1rem}.right-1-l{right:1rem}.bottom-1-l{bottom:1rem}.top-2-l{top:2rem}.left-2-l{left:2rem}.right-2-l{right:2rem}.bottom-2-l{bottom:2rem}.top--1-l{top:-1rem}.right--1-l{right:-1rem}.bottom--1-l{bottom:-1rem}.left--1-l{left:-1rem}.top--2-l{top:-2rem}.right--2-l{right:-2rem}.bottom--2-l{bottom:-2rem}.left--2-l{left:-2rem}.absolute--fill-l{top:0;right:0;bottom:0;left:0}.cl-l{clear:left}.cr-l{clear:right}.cb-l{clear:both}.cn-l{clear:none}.dn-l{display:none}.di-l{display:inline}.db-l{display:block}.dib-l{display:inline-block}.dit-l{display:inline-table}.dt-l{display:table}.dtc-l{display:table-cell}.dt-row-l{display:table-row}.dt-row-group-l{display:table-row-group}.dt-column-l{display:table-column}.dt-column-group-l{display:table-column-group}.dt--fixed-l{table-layout:fixed;width:100%}.flex-l{display:flex}.inline-flex-l{display:inline-flex}.flex-auto-l{flex:1 1 auto;min-width:0;min-height:0}.flex-none-l{flex:none}.flex-column-l{flex-direction:column}.flex-row-l{flex-direction:row}.flex-wrap-l{flex-wrap:wrap}.flex-nowrap-l{flex-wrap:nowrap}.flex-wrap-reverse-l{flex-wrap:wrap-reverse}.flex-column-reverse-l{flex-direction:column-reverse}.flex-row-reverse-l{flex-direction:row-reverse}.items-start-l{align-items:flex-start}.items-end-l{align-items:flex-end}.items-center-l{align-items:center}.items-baseline-l{align-items:baseline}.items-stretch-l{align-items:stretch}.self-start-l{align-self:flex-start}.self-end-l{align-self:flex-end}.self-center-l{align-self:center}.self-baseline-l{align-self:baseline}.self-stretch-l{align-self:stretch}.justify-start-l{justify-content:flex-start}.justify-end-l{justify-content:flex-end}.justify-center-l{justify-content:center}.justify-between-l{justify-content:space-between}.justify-around-l{justify-content:space-around}.content-start-l{align-content:flex-start}.content-end-l{align-content:flex-end}.content-center-l{align-content:center}.content-between-l{align-content:space-between}.content-around-l{align-content:space-around}.content-stretch-l{align-content:stretch}.order-0-l{order:0}.order-1-l{order:1}.order-2-l{order:2}.order-3-l{order:3}.order-4-l{order:4}.order-5-l{order:5}.order-6-l{order:6}.order-7-l{order:7}.order-8-l{order:8}.order-last-l{order:99999}.flex-grow-0-l{flex-grow:0}.flex-grow-1-l{flex-grow:1}.flex-shrink-0-l{flex-shrink:0}.flex-shrink-1-l{flex-shrink:1}.fl-l{float:left}.fl-l,.fr-l{_display:inline}.fr-l{float:right}.fn-l{float:none}.i-l{font-style:italic}.fs-normal-l{font-style:normal}.normal-l{font-weight:400}.b-l{font-weight:700}.fw1-l{font-weight:100}.fw2-l{font-weight:200}.fw3-l{font-weight:300}.fw4-l{font-weight:400}.fw5-l{font-weight:500}.fw6-l{font-weight:600}.fw7-l{font-weight:700}.fw8-l{font-weight:800}.fw9-l{font-weight:900}.h1-l{height:1rem}.h2-l{height:2rem}.h3-l{height:4rem}.h4-l{height:8rem}.h5-l{height:16rem}.h-25-l{height:25%}.h-50-l{height:50%}.h-75-l{height:75%}.h-100-l{height:100%}.min-h-100-l{min-height:100%}.vh-25-l{height:25vh}.vh-50-l{height:50vh}.vh-75-l{height:75vh}.vh-100-l{height:100vh}.min-vh-100-l{min-height:100vh}.h-auto-l{height:auto}.h-inherit-l{height:inherit}.tracked-l{letter-spacing:.1em}.tracked-tight-l{letter-spacing:-.05em}.tracked-mega-l{letter-spacing:.25em}.lh-solid-l{line-height:1}.lh-title-l{line-height:1.25}.lh-copy-l{line-height:1.5}.mw-100-l{max-width:100%}.mw1-l{max-width:1rem}.mw2-l{max-width:2rem}.mw3-l{max-width:4rem}.mw4-l{max-width:8rem}.mw5-l{max-width:16rem}.mw6-l{max-width:32rem}.mw7-l{max-width:48rem}.mw8-l{max-width:64rem}.mw9-l{max-width:96rem}.mw-none-l{max-width:none}.w1-l{width:1rem}.w2-l{width:2rem}.w3-l{width:4rem}.w4-l{width:8rem}.w5-l{width:16rem}.w-10-l{width:10%}.w-20-l{width:20%}.w-25-l{width:25%}.w-30-l{width:30%}.w-33-l{width:33%}.w-34-l{width:34%}.w-40-l{width:40%}.w-50-l{width:50%}.w-60-l{width:60%}.w-70-l{width:70%}.w-75-l{width:75%}.w-80-l{width:80%}.w-90-l{width:90%}.w-100-l{width:100%}.w-third-l{width:33.33333%}.w-two-thirds-l{width:66.66667%}.w-auto-l{width:auto}.overflow-visible-l{overflow:visible}.overflow-hidden-l{overflow:hidden}.overflow-scroll-l{overflow:scroll}.overflow-auto-l{overflow:auto}.overflow-x-visible-l{overflow-x:visible}.overflow-x-hidden-l{overflow-x:hidden}.overflow-x-scroll-l{overflow-x:scroll}.overflow-x-auto-l{overflow-x:auto}.overflow-y-visible-l{overflow-y:visible}.overflow-y-hidden-l{overflow-y:hidden}.overflow-y-scroll-l{overflow-y:scroll}.overflow-y-auto-l{overflow-y:auto}.static-l{position:static}.relative-l{position:relative}.absolute-l{position:absolute}.fixed-l{position:fixed}.rotate-45-l{-webkit-transform:rotate(45deg);transform:rotate(45deg)}.rotate-90-l{-webkit-transform:rotate(90deg);transform:rotate(90deg)}.rotate-135-l{-webkit-transform:rotate(135deg);transform:rotate(135deg)}.rotate-180-l{-webkit-transform:rotate(180deg);transform:rotate(180deg)}.rotate-225-l{-webkit-transform:rotate(225deg);transform:rotate(225deg)}.rotate-270-l{-webkit-transform:rotate(270deg);transform:rotate(270deg)}.rotate-315-l{-webkit-transform:rotate(315deg);transform:rotate(315deg)}.pa0-l{padding:0}.pa1-l{padding:.25rem}.pa2-l{padding:.5rem}.pa3-l{padding:1rem}.pa4-l{padding:2rem}.pa5-l{padding:4rem}.pa6-l{padding:8rem}.pa7-l{padding:16rem}.pl0-l{padding-left:0}.pl1-l{padding-left:.25rem}.pl2-l{padding-left:.5rem}.pl3-l{padding-left:1rem}.pl4-l{padding-left:2rem}.pl5-l{padding-left:4rem}.pl6-l{padding-left:8rem}.pl7-l{padding-left:16rem}.pr0-l{padding-right:0}.pr1-l{padding-right:.25rem}.pr2-l{padding-right:.5rem}.pr3-l{padding-right:1rem}.pr4-l{padding-right:2rem}.pr5-l{padding-right:4rem}.pr6-l{padding-right:8rem}.pr7-l{padding-right:16rem}.pb0-l{padding-bottom:0}.pb1-l{padding-bottom:.25rem}.pb2-l{padding-bottom:.5rem}.pb3-l{padding-bottom:1rem}.pb4-l{padding-bottom:2rem}.pb5-l{padding-bottom:4rem}.pb6-l{padding-bottom:8rem}.pb7-l{padding-bottom:16rem}.pt0-l{padding-top:0}.pt1-l{padding-top:.25rem}.pt2-l{padding-top:.5rem}.pt3-l{padding-top:1rem}.pt4-l{padding-top:2rem}.pt5-l{padding-top:4rem}.pt6-l{padding-top:8rem}.pt7-l{padding-top:16rem}.pv0-l{padding-top:0;padding-bottom:0}.pv1-l{padding-top:.25rem;padding-bottom:.25rem}.pv2-l{padding-top:.5rem;padding-bottom:.5rem}.pv3-l{padding-top:1rem;padding-bottom:1rem}.pv4-l{padding-top:2rem;padding-bottom:2rem}.pv5-l{padding-top:4rem;padding-bottom:4rem}.pv6-l{padding-top:8rem;padding-bottom:8rem}.pv7-l{padding-top:16rem;padding-bottom:16rem}.ph0-l{padding-left:0;padding-right:0}.ph1-l{padding-left:.25rem;padding-right:.25rem}.ph2-l{padding-left:.5rem;padding-right:.5rem}.ph3-l{padding-left:1rem;padding-right:1rem}.ph4-l{padding-left:2rem;padding-right:2rem}.ph5-l{padding-left:4rem;padding-right:4rem}.ph6-l{padding-left:8rem;padding-right:8rem}.ph7-l{padding-left:16rem;padding-right:16rem}.ma0-l{margin:0}.ma1-l{margin:.25rem}.ma2-l{margin:.5rem}.ma3-l{margin:1rem}.ma4-l{margin:2rem}.ma5-l{margin:4rem}.ma6-l{margin:8rem}.ma7-l{margin:16rem}.ml0-l{margin-left:0}.ml1-l{margin-left:.25rem}.ml2-l{margin-left:.5rem}.ml3-l{margin-left:1rem}.ml4-l{margin-left:2rem}.ml5-l{margin-left:4rem}.ml6-l{margin-left:8rem}.ml7-l{margin-left:16rem}.mr0-l{margin-right:0}.mr1-l{margin-right:.25rem}.mr2-l{margin-right:.5rem}.mr3-l{margin-right:1rem}.mr4-l{margin-right:2rem}.mr5-l{margin-right:4rem}.mr6-l{margin-right:8rem}.mr7-l{margin-right:16rem}.mb0-l{margin-bottom:0}.mb1-l{margin-bottom:.25rem}.mb2-l{margin-bottom:.5rem}.mb3-l{margin-bottom:1rem}.mb4-l{margin-bottom:2rem}.mb5-l{margin-bottom:4rem}.mb6-l{margin-bottom:8rem}.mb7-l{margin-bottom:16rem}.mt0-l{margin-top:0}.mt1-l{margin-top:.25rem}.mt2-l{margin-top:.5rem}.mt3-l{margin-top:1rem}.mt4-l{margin-top:2rem}.mt5-l{margin-top:4rem}.mt6-l{margin-top:8rem}.mt7-l{margin-top:16rem}.mv0-l{margin-top:0;margin-bottom:0}.mv1-l{margin-top:.25rem;margin-bottom:.25rem}.mv2-l{margin-top:.5rem;margin-bottom:.5rem}.mv3-l{margin-top:1rem;margin-bottom:1rem}.mv4-l{margin-top:2rem;margin-bottom:2rem}.mv5-l{margin-top:4rem;margin-bottom:4rem}.mv6-l{margin-top:8rem;margin-bottom:8rem}.mv7-l{margin-top:16rem;margin-bottom:16rem}.mh0-l{margin-left:0;margin-right:0}.mh1-l{margin-left:.25rem;margin-right:.25rem}.mh2-l{margin-left:.5rem;margin-right:.5rem}.mh3-l{margin-left:1rem;margin-right:1rem}.mh4-l{margin-left:2rem;margin-right:2rem}.mh5-l{margin-left:4rem;margin-right:4rem}.mh6-l{margin-left:8rem;margin-right:8rem}.mh7-l{margin-left:16rem;margin-right:16rem}.na1-l{margin:-.25rem}.na2-l{margin:-.5rem}.na3-l{margin:-1rem}.na4-l{margin:-2rem}.na5-l{margin:-4rem}.na6-l{margin:-8rem}.na7-l{margin:-16rem}.nl1-l{margin-left:-.25rem}.nl2-l{margin-left:-.5rem}.nl3-l{margin-left:-1rem}.nl4-l{margin-left:-2rem}.nl5-l{margin-left:-4rem}.nl6-l{margin-left:-8rem}.nl7-l{margin-left:-16rem}.nr1-l{margin-right:-.25rem}.nr2-l{margin-right:-.5rem}.nr3-l{margin-right:-1rem}.nr4-l{margin-right:-2rem}.nr5-l{margin-right:-4rem}.nr6-l{margin-right:-8rem}.nr7-l{margin-right:-16rem}.nb1-l{margin-bottom:-.25rem}.nb2-l{margin-bottom:-.5rem}.nb3-l{margin-bottom:-1rem}.nb4-l{margin-bottom:-2rem}.nb5-l{margin-bottom:-4rem}.nb6-l{margin-bottom:-8rem}.nb7-l{margin-bottom:-16rem}.nt1-l{margin-top:-.25rem}.nt2-l{margin-top:-.5rem}.nt3-l{margin-top:-1rem}.nt4-l{margin-top:-2rem}.nt5-l{margin-top:-4rem}.nt6-l{margin-top:-8rem}.nt7-l{margin-top:-16rem}.strike-l{text-decoration:line-through}.underline-l{text-decoration:underline}.no-underline-l{text-decoration:none}.tl-l{text-align:left}.tr-l{text-align:right}.tc-l{text-align:center}.tj-l{text-align:justify}.ttc-l{text-transform:capitalize}.ttl-l{text-transform:lowercase}.ttu-l{text-transform:uppercase}.ttn-l{text-transform:none}.f-6-l,.f-headline-l{font-size:6rem}.f-5-l,.f-subheadline-l{font-size:5rem}.f1-l{font-size:3rem}.f2-l{font-size:2.25rem}.f3-l{font-size:1.5rem}.f4-l{font-size:1.25rem}.f5-l{font-size:1rem}.f6-l{font-size:.875rem}.f7-l{font-size:.75rem}.measure-l{max-width:30em}.measure-wide-l{max-width:34em}.measure-narrow-l{max-width:20em}.indent-l{text-indent:1em;margin-top:0;margin-bottom:0}.small-caps-l{font-variant:small-caps}.truncate-l{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.center-l{margin-left:auto}.center-l,.mr-auto-l{margin-right:auto}.ml-auto-l{margin-left:auto}.clip-l{position:fixed!important;_position:absolute!important;clip:rect(1px 1px 1px 1px);clip:rect(1px,1px,1px,1px)}.ws-normal-l{white-space:normal}.nowrap-l{white-space:nowrap}.pre-l{white-space:pre}.v-base-l{vertical-align:baseline}.v-mid-l{vertical-align:middle}.v-top-l{vertical-align:top}.v-btm-l{vertical-align:bottom}} + diff --git a/system/author/editor/editor-blox.twig b/system/author/editor/editor-blox.twig index fdc4792..d8cf7a6 100644 --- a/system/author/editor/editor-blox.twig +++ b/system/author/editor/editor-blox.twig @@ -1,82 +1,82 @@ -{% extends 'layouts/layoutBlox.twig' %} -{% block title %}Visual Content Editor{% endblock %} - -{% block content %} - -
- -
- -
- - -
-
- -
- -
{{title}}
- - {% for block in content %} -
{{block}}
- {% endfor %} - -
- - -
-
- -
- - -
-
- - - - - - - - - - - - - -
-
-
-
- -
- - - - - - - - - - - - - - - -
- -
- -
- - {% include 'editor/publish-controller.twig' %} - - - {{ csrf_field() | raw }} - -
- +{% extends 'layouts/layoutBlox.twig' %} +{% block title %}Visual Content Editor{% endblock %} + +{% block content %} + +
+ +
+ +
+ + +
+
+ +
+ +
{{title}}
+ + {% for block in content %} +
{{block}}
+ {% endfor %} + +
+ + +
+
+ +
+ + +
+
+ + + + + + + + + + + + + +
+
+
+
+ +
+ + + + + + + + + + + + + + + +
+ +
+ +
+ + {% include 'editor/publish-controller.twig' %} + + + {{ csrf_field() | raw }} + +
+ {% endblock %} \ No newline at end of file diff --git a/system/author/editor/editor-raw.twig b/system/author/editor/editor-raw.twig index a4657ee..db69474 100644 --- a/system/author/editor/editor-raw.twig +++ b/system/author/editor/editor-raw.twig @@ -1,38 +1,38 @@ -{% extends 'layouts/layoutEditor.twig' %} -{% block title %}Raw Content Editor{% endblock %} - -{% block content %} - -
- -
-
- -
- -
-
- - - ${ errors.title } -
-
- - - ${ errors.content } -
- -
- -
- -
-
- - {% include 'editor/publish-controller.twig' %} - - {{ csrf_field() | raw }} - -
- +{% extends 'layouts/layoutEditor.twig' %} +{% block title %}Raw Content Editor{% endblock %} + +{% block content %} + +
+ +
+
+ +
+ +
+
+ + + ${ errors.title } +
+
+ + + ${ errors.content } +
+ +
+ +
+ +
+
+ + {% include 'editor/publish-controller.twig' %} + + {{ csrf_field() | raw }} + +
+ {% endblock %} \ No newline at end of file diff --git a/system/author/editor/publish-controller.twig b/system/author/editor/publish-controller.twig index 53c5210..5b86543 100644 --- a/system/author/editor/publish-controller.twig +++ b/system/author/editor/publish-controller.twig @@ -1,21 +1,21 @@ -
-
${ errors.message }
- -
- - - raw moderaw - visual modevisual - -
- - - +
+
${ errors.message }
+ +
+ + + raw moderaw + visual modevisual + +
+ + +
\ No newline at end of file diff --git a/system/author/intern404.twig b/system/author/intern404.twig index 0ef70a7..d23cbe1 100644 --- a/system/author/intern404.twig +++ b/system/author/intern404.twig @@ -1,10 +1,10 @@ -{% extends 'layouts/layoutBlank.twig' %} - -{% block title %}ERROR 404: Page not found{% endblock %} - -{% block content %} - -

Not Found

-

Sorry, but we did not find the page that you are looking for.

- +{% extends 'layouts/layoutBlank.twig' %} + +{% block title %}ERROR 404: Page not found{% endblock %} + +{% block content %} + +

Not Found

+

Sorry, but we did not find the page that you are looking for.

+ {% endblock %} \ No newline at end of file diff --git a/system/author/js/auth.js b/system/author/js/auth.js index b2f0469..2190668 100644 --- a/system/author/js/auth.js +++ b/system/author/js/auth.js @@ -1,30 +1,30 @@ -/************************************* -** LOGIN TIMER ** -*************************************/ - -var wait = document.getElementById('wait'); - -if(wait) -{ - var loginbtn = document.getElementById("loginbutton"); - var seconds = parseInt(wait.innerHTML); - - loginbtn.disabled = true; - loginbtn.value = ''; - - var counter = setInterval(function () { - - seconds = seconds - 1; - wait.innerHTML = seconds; - - if (seconds == 0) { - loginbtn.disabled = false; - loginbtn.value = 'Login'; - var countdown = document.getElementById("counter"); - - countdown.parentNode.removeChild(countdown); - - clearInterval(counter); - } - }, 1000); +/************************************* +** LOGIN TIMER ** +*************************************/ + +var wait = document.getElementById('wait'); + +if(wait) +{ + var loginbtn = document.getElementById("loginbutton"); + var seconds = parseInt(wait.innerHTML); + + loginbtn.disabled = true; + loginbtn.value = ''; + + var counter = setInterval(function () { + + seconds = seconds - 1; + wait.innerHTML = seconds; + + if (seconds == 0) { + loginbtn.disabled = false; + loginbtn.value = 'Login'; + var countdown = document.getElementById("counter"); + + countdown.parentNode.removeChild(countdown); + + clearInterval(counter); + } + }, 1000); } \ No newline at end of file diff --git a/system/author/js/author.js b/system/author/js/author.js index 308b0ed..39cff88 100644 --- a/system/author/js/author.js +++ b/system/author/js/author.js @@ -1,341 +1,341 @@ - /********************************** - ** Global HttpRequest-Function ** - ** for AJAX-Requests ** - **********************************/ - - function prepareHttpRequest() - { - var httpRequest; - if (window.XMLHttpRequest){ // Mozilla, Safari, ... - httpRequest = new XMLHttpRequest(); - } - else if (window.ActiveXObject){ // IE - try{ - httpRequest = new ActiveXObject("Msxml2.XMLHTTP"); - } - catch (e){ - try{ - httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); - } - catch (e) {} - } - } - if (!httpRequest){ - alert('Giving up :( Cannot create an XMLHTTP instance'); - return false; - } - return httpRequest; - } - - function prepareCORSRequest(method, url){ - var xhr = prepareHttpRequest(); - if ("withCredentials" in xhr) - { - xhr.open(method, url, true); - } - else if (typeof XDomainRequest != "undefined") - { - xhr = new XDomainRequest(); - xhr.open(method, url); - } - else - { - xhr = null; - } - return xhr; - } - - function sendJson(callback, getPost, url, jsonData, cors) - { - if(cors) - { - var httpRequest = prepareCORSRequest(getPost, url); - } - else - { - var httpRequest = prepareHttpRequest(); - httpRequest.open(getPost, url, true); - } - - httpRequest.onreadystatechange = function(e) - { - if (this.readyState == 4) - { - if(httpRequest.response && callback) - { - callback(httpRequest.response, this.status); - } - } - }; - - // httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); - // httpRequest.setRequestHeader('Content-Type', 'text/plain'); - httpRequest.setRequestHeader('Content-Type', 'application/json'); - - // required for slim - httpRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); - - if(jsonData) - { - httpRequest.send(JSON.stringify(jsonData)); - } - else - { - httpRequest.send(); - } - } - - var openModal = document.getElementById("openModal"), - closeModal = document.getElementById("closeModal"); - - if(openModal && closeModal) - { - openModal.addEventListener("click", function(e){ e.preventDefault(); toggle("modalWindow", "show"); }); - closeModal.addEventListener("click", function(e){ e.preventDefault(); toggle("modalWindow", "show"); }); - } - - var mobileMenu = document.getElementById("mobile-menu"); - - if(mobileMenu) - { - mobileMenu.addEventListener("click", function(e){ toggle("sidebar-menu", "expand"); }); - } - - function toggle(myid, myclass) - { - var toggleElement = document.getElementById(myid); - toggleElement.classList.toggle(myclass); - } - - if (window.Element && !Element.prototype.closest) { - Element.prototype.closest = - function(s) { - var matches = (this.document || this.ownerDocument).querySelectorAll(s), - i, - el = this; - do { - i = matches.length; - while (--i >= 0 && matches.item(i) !== el) {}; - } while ((i < 0) && (el = el.parentElement)); - return el; - }; - } - - - /********************************** - ** START VERSION CHECK ** - **********************************/ - - if(document.getElementById("system")) - { - getVersions('system', document.getElementsByClassName("fc-system-version")); - } - - if(document.getElementById("plugins")) - { - getVersions('plugins', document.getElementsByClassName("fc-plugin-version")); - } - - if(document.getElementById("themes")) - { - getVersions('theme', document.getElementsByClassName("fc-theme-version")); - } - - function getVersions(name, value) - { - var getPost = 'GET'; - url = 'https://typemill.net/api/v1/checkversion?'; - - if(name == 'plugins') - { - var pluginList = '&plugins='; - for (var i = 0, len = value.length; i < len; i++) - { - pluginList += value[i].id + ','; - } - - url += pluginList; - } - - if(name == 'theme') - { - var themeList = '&themes='; - for (var i = 0, len = value.length; i < len; i++) - { - themeList += value[i].id + ','; - } - - url += themeList; - } - - sendJson(function(response) - { - if(response !== 'error') - { - var versions = JSON.parse(response); - - if(name == 'system' && versions.system) - { - updateVersions(versions.system); - } - if(name == 'plugins' && versions.plugins) - { - updateVersions(versions.plugins); - } - if(name == 'theme' && versions.themes) - { - updateVersions(versions.themes); - } - } - else - { - return false; - } - }, getPost, url, false, true); - } - - function updateVersions(elementVersions) - { - for (var key in elementVersions) - { - if (elementVersions.hasOwnProperty(key)) - { - singleElement = document.getElementById(key); - - if(elementVersions[key] && singleElement && cmpVersions(elementVersions[key], singleElement.innerHTML) > 0) - { - singleElement.innerHTML = "update
to " + elementVersions[key] + "
"; - singleElement.classList.add("show-banner"); - } - } - } - } - - /* credit: https://stackoverflow.com/questions/6832596/how-to-compare-software-version-number-using-js-only-number */ - function cmpVersions (a, b) - { - var i, diff; - var regExStrip0 = /(\.0+)+$/; - var segmentsA = a.replace(regExStrip0, '').split('.'); - var segmentsB = b.replace(regExStrip0, '').split('.'); - var l = Math.min(segmentsA.length, segmentsB.length); - - for (i = 0; i < l; i++) - { - diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10); - if (diff) - { - return diff; - } - } - return segmentsA.length - segmentsB.length; - } - - - /************************************* - ** CARDS: ACTIVATE/OPEN CLOSE ** - *************************************/ - - var cards = document.getElementsByClassName("card"); - if(cards) - { - for (var i = 0, len = cards.length; i < len; i++) - { - cards[i].addEventListener("click", function(e) - { - if(e.target.classList.contains("fc-active")) - { - this.getElementsByClassName("fc-settings")[0].classList.toggle("active"); - } - if(e.target.classList.contains("fc-settings")) - { - this.getElementsByClassName("cardFields")[0].classList.toggle("open"); - this.getElementsByClassName("fc-settings")[0].classList.toggle("expand"); - } - }); - } - } - - /************************************* - ** COLOR PICKER ** - *************************************/ - - var target = document.querySelectorAll('input[type=color]'); - // set hooks for each target element - for (var i = 0, len = target.length; i < len; ++i) - { - var thisTarget = target[i]; - - (function(thisTarget){ - - /* hide the input field and show color box instead */ - var box = document.createElement('div'); - - box.className = 'color-box'; - box.style.backgroundColor = thisTarget.value; - box.setAttribute('data-color', thisTarget.value); - thisTarget.parentNode.insertBefore(box, thisTarget); - thisTarget.type = 'hidden'; - - var picker = new CP(box), - code = document.createElement('input'); - - picker.target.onclick = function(e) - { - e.preventDefault(); - }; - - code.className = 'color-code'; - code.pattern = '^#[A-Fa-f0-9]{6}$'; - code.type = 'text'; - - picker.on("enter", function() { - code.value = '#' + CP._HSV2HEX(this.get()); - }); - - - picker.on("change", function(color) { - thisTarget.value = '#' + color; - this.target.style.backgroundColor = '#' + color; - code.value = '#' + color; - }); - - picker.picker.firstChild.appendChild(code); - - function update() { - if (this.value.length) { - picker.set(this.value); - picker.trigger("change", [this.value.slice(1)]); - } - } - - code.oncut = update; - code.onpaste = update; - code.onkeyup = update; - code.oninput = update; - - - })(thisTarget); - } - - /** - * Element.closest() polyfill - * https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill - */ - if (!Element.prototype.closest) { - if (!Element.prototype.matches) { - Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; - } - Element.prototype.closest = function (s) { - var el = this; - var ancestor = this; - if (!document.documentElement.contains(el)) return null; - do { - if (ancestor.matches(s)) return ancestor; - ancestor = ancestor.parentElement; - } while (ancestor !== null); - return null; - }; - } - + /********************************** + ** Global HttpRequest-Function ** + ** for AJAX-Requests ** + **********************************/ + + function prepareHttpRequest() + { + var httpRequest; + if (window.XMLHttpRequest){ // Mozilla, Safari, ... + httpRequest = new XMLHttpRequest(); + } + else if (window.ActiveXObject){ // IE + try{ + httpRequest = new ActiveXObject("Msxml2.XMLHTTP"); + } + catch (e){ + try{ + httpRequest = new ActiveXObject("Microsoft.XMLHTTP"); + } + catch (e) {} + } + } + if (!httpRequest){ + alert('Giving up :( Cannot create an XMLHTTP instance'); + return false; + } + return httpRequest; + } + + function prepareCORSRequest(method, url){ + var xhr = prepareHttpRequest(); + if ("withCredentials" in xhr) + { + xhr.open(method, url, true); + } + else if (typeof XDomainRequest != "undefined") + { + xhr = new XDomainRequest(); + xhr.open(method, url); + } + else + { + xhr = null; + } + return xhr; + } + + function sendJson(callback, getPost, url, jsonData, cors) + { + if(cors) + { + var httpRequest = prepareCORSRequest(getPost, url); + } + else + { + var httpRequest = prepareHttpRequest(); + httpRequest.open(getPost, url, true); + } + + httpRequest.onreadystatechange = function(e) + { + if (this.readyState == 4) + { + if(httpRequest.response && callback) + { + callback(httpRequest.response, this.status); + } + } + }; + + // httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded'); + // httpRequest.setRequestHeader('Content-Type', 'text/plain'); + httpRequest.setRequestHeader('Content-Type', 'application/json'); + + // required for slim + httpRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + + if(jsonData) + { + httpRequest.send(JSON.stringify(jsonData)); + } + else + { + httpRequest.send(); + } + } + + var openModal = document.getElementById("openModal"), + closeModal = document.getElementById("closeModal"); + + if(openModal && closeModal) + { + openModal.addEventListener("click", function(e){ e.preventDefault(); toggle("modalWindow", "show"); }); + closeModal.addEventListener("click", function(e){ e.preventDefault(); toggle("modalWindow", "show"); }); + } + + var mobileMenu = document.getElementById("mobile-menu"); + + if(mobileMenu) + { + mobileMenu.addEventListener("click", function(e){ toggle("sidebar-menu", "expand"); }); + } + + function toggle(myid, myclass) + { + var toggleElement = document.getElementById(myid); + toggleElement.classList.toggle(myclass); + } + + if (window.Element && !Element.prototype.closest) { + Element.prototype.closest = + function(s) { + var matches = (this.document || this.ownerDocument).querySelectorAll(s), + i, + el = this; + do { + i = matches.length; + while (--i >= 0 && matches.item(i) !== el) {}; + } while ((i < 0) && (el = el.parentElement)); + return el; + }; + } + + + /********************************** + ** START VERSION CHECK ** + **********************************/ + + if(document.getElementById("system")) + { + getVersions('system', document.getElementsByClassName("fc-system-version")); + } + + if(document.getElementById("plugins")) + { + getVersions('plugins', document.getElementsByClassName("fc-plugin-version")); + } + + if(document.getElementById("themes")) + { + getVersions('theme', document.getElementsByClassName("fc-theme-version")); + } + + function getVersions(name, value) + { + var getPost = 'GET'; + url = 'https://typemill.net/api/v1/checkversion?'; + + if(name == 'plugins') + { + var pluginList = '&plugins='; + for (var i = 0, len = value.length; i < len; i++) + { + pluginList += value[i].id + ','; + } + + url += pluginList; + } + + if(name == 'theme') + { + var themeList = '&themes='; + for (var i = 0, len = value.length; i < len; i++) + { + themeList += value[i].id + ','; + } + + url += themeList; + } + + sendJson(function(response) + { + if(response !== 'error') + { + var versions = JSON.parse(response); + + if(name == 'system' && versions.system) + { + updateVersions(versions.system); + } + if(name == 'plugins' && versions.plugins) + { + updateVersions(versions.plugins); + } + if(name == 'theme' && versions.themes) + { + updateVersions(versions.themes); + } + } + else + { + return false; + } + }, getPost, url, false, true); + } + + function updateVersions(elementVersions) + { + for (var key in elementVersions) + { + if (elementVersions.hasOwnProperty(key)) + { + singleElement = document.getElementById(key); + + if(elementVersions[key] && singleElement && cmpVersions(elementVersions[key], singleElement.innerHTML) > 0) + { + singleElement.innerHTML = "update
to " + elementVersions[key] + "
"; + singleElement.classList.add("show-banner"); + } + } + } + } + + /* credit: https://stackoverflow.com/questions/6832596/how-to-compare-software-version-number-using-js-only-number */ + function cmpVersions (a, b) + { + var i, diff; + var regExStrip0 = /(\.0+)+$/; + var segmentsA = a.replace(regExStrip0, '').split('.'); + var segmentsB = b.replace(regExStrip0, '').split('.'); + var l = Math.min(segmentsA.length, segmentsB.length); + + for (i = 0; i < l; i++) + { + diff = parseInt(segmentsA[i], 10) - parseInt(segmentsB[i], 10); + if (diff) + { + return diff; + } + } + return segmentsA.length - segmentsB.length; + } + + + /************************************* + ** CARDS: ACTIVATE/OPEN CLOSE ** + *************************************/ + + var cards = document.getElementsByClassName("card"); + if(cards) + { + for (var i = 0, len = cards.length; i < len; i++) + { + cards[i].addEventListener("click", function(e) + { + if(e.target.classList.contains("fc-active")) + { + this.getElementsByClassName("fc-settings")[0].classList.toggle("active"); + } + if(e.target.classList.contains("fc-settings")) + { + this.getElementsByClassName("cardFields")[0].classList.toggle("open"); + this.getElementsByClassName("fc-settings")[0].classList.toggle("expand"); + } + }); + } + } + + /************************************* + ** COLOR PICKER ** + *************************************/ + + var target = document.querySelectorAll('input[type=color]'); + // set hooks for each target element + for (var i = 0, len = target.length; i < len; ++i) + { + var thisTarget = target[i]; + + (function(thisTarget){ + + /* hide the input field and show color box instead */ + var box = document.createElement('div'); + + box.className = 'color-box'; + box.style.backgroundColor = thisTarget.value; + box.setAttribute('data-color', thisTarget.value); + thisTarget.parentNode.insertBefore(box, thisTarget); + thisTarget.type = 'hidden'; + + var picker = new CP(box), + code = document.createElement('input'); + + picker.target.onclick = function(e) + { + e.preventDefault(); + }; + + code.className = 'color-code'; + code.pattern = '^#[A-Fa-f0-9]{6}$'; + code.type = 'text'; + + picker.on("enter", function() { + code.value = '#' + CP._HSV2HEX(this.get()); + }); + + + picker.on("change", function(color) { + thisTarget.value = '#' + color; + this.target.style.backgroundColor = '#' + color; + code.value = '#' + color; + }); + + picker.picker.firstChild.appendChild(code); + + function update() { + if (this.value.length) { + picker.set(this.value); + picker.trigger("change", [this.value.slice(1)]); + } + } + + code.oncut = update; + code.onpaste = update; + code.onkeyup = update; + code.oninput = update; + + + })(thisTarget); + } + + /** + * Element.closest() polyfill + * https://developer.mozilla.org/en-US/docs/Web/API/Element/closest#Polyfill + */ + if (!Element.prototype.closest) { + if (!Element.prototype.matches) { + Element.prototype.matches = Element.prototype.msMatchesSelector || Element.prototype.webkitMatchesSelector; + } + Element.prototype.closest = function (s) { + var el = this; + var ancestor = this; + if (!document.documentElement.contains(el)) return null; + do { + if (ancestor.matches(s)) return ancestor; + ancestor = ancestor.parentElement; + } while (ancestor !== null); + return null; + }; + } + \ No newline at end of file diff --git a/system/author/js/autosize.min.js b/system/author/js/autosize.min.js index 4d9b4e9..4b086e8 100644 --- a/system/author/js/autosize.min.js +++ b/system/author/js/autosize.min.js @@ -1,6 +1,6 @@ -/*! - autosize 4.0.2 - license: MIT - http://www.jacklmoore.com/autosize -*/ +/*! + autosize 4.0.2 + license: MIT + http://www.jacklmoore.com/autosize +*/ !function(e,t){if("function"==typeof define&&define.amd)define(["module","exports"],t);else if("undefined"!=typeof exports)t(module,exports);else{var n={exports:{}};t(n,n.exports),e.autosize=n.exports}}(this,function(e,t){"use strict";var n,o,p="function"==typeof Map?new Map:(n=[],o=[],{has:function(e){return-1 + * @license MIT + */ +e.exports=function(e){return null!=e&&null!=e.constructor&&"function"==typeof e.constructor.isBuffer&&e.constructor.isBuffer(e)}},function(e,t,n){"use strict";function r(e){this.defaults=e,this.interceptors={request:new i,response:new i}}var o=n(2),s=n(6),i=n(7),a=n(8),u=n(22);r.prototype.request=function(e){"string"==typeof e?(e=arguments[1]||{},e.url=arguments[0]):e=e||{},e=u(this.defaults,e),e.method=e.method?e.method.toLowerCase():"get";var t=[a,void 0],n=Promise.resolve(e);for(this.interceptors.request.forEach(function(e){t.unshift(e.fulfilled,e.rejected)}),this.interceptors.response.forEach(function(e){t.push(e.fulfilled,e.rejected)});t.length;)n=n.then(t.shift(),t.shift());return n},r.prototype.getUri=function(e){return e=u(this.defaults,e),s(e.url,e.params,e.paramsSerializer).replace(/^\?/,"")},o.forEach(["delete","get","head","options"],function(e){r.prototype[e]=function(t,n){return this.request(o.merge(n||{},{method:e,url:t}))}}),o.forEach(["post","put","patch"],function(e){r.prototype[e]=function(t,n,r){return this.request(o.merge(r||{},{method:e,url:t,data:n}))}}),e.exports=r},function(e,t,n){"use strict";function r(e){return encodeURIComponent(e).replace(/%40/gi,"@").replace(/%3A/gi,":").replace(/%24/g,"$").replace(/%2C/gi,",").replace(/%20/g,"+").replace(/%5B/gi,"[").replace(/%5D/gi,"]")}var o=n(2);e.exports=function(e,t,n){if(!t)return e;var s;if(n)s=n(t);else if(o.isURLSearchParams(t))s=t.toString();else{var i=[];o.forEach(t,function(e,t){null!==e&&"undefined"!=typeof e&&(o.isArray(e)?t+="[]":e=[e],o.forEach(e,function(e){o.isDate(e)?e=e.toISOString():o.isObject(e)&&(e=JSON.stringify(e)),i.push(r(t)+"="+r(e))}))}),s=i.join("&")}if(s){var a=e.indexOf("#");a!==-1&&(e=e.slice(0,a)),e+=(e.indexOf("?")===-1?"?":"&")+s}return e}},function(e,t,n){"use strict";function r(){this.handlers=[]}var o=n(2);r.prototype.use=function(e,t){return this.handlers.push({fulfilled:e,rejected:t}),this.handlers.length-1},r.prototype.eject=function(e){this.handlers[e]&&(this.handlers[e]=null)},r.prototype.forEach=function(e){o.forEach(this.handlers,function(t){null!==t&&e(t)})},e.exports=r},function(e,t,n){"use strict";function r(e){e.cancelToken&&e.cancelToken.throwIfRequested()}var o=n(2),s=n(9),i=n(10),a=n(11),u=n(20),c=n(21);e.exports=function(e){r(e),e.baseURL&&!u(e.url)&&(e.url=c(e.baseURL,e.url)),e.headers=e.headers||{},e.data=s(e.data,e.headers,e.transformRequest),e.headers=o.merge(e.headers.common||{},e.headers[e.method]||{},e.headers||{}),o.forEach(["delete","get","head","post","put","patch","common"],function(t){delete e.headers[t]});var t=e.adapter||a.adapter;return t(e).then(function(t){return r(e),t.data=s(t.data,t.headers,e.transformResponse),t},function(t){return i(t)||(r(e),t&&t.response&&(t.response.data=s(t.response.data,t.response.headers,e.transformResponse))),Promise.reject(t)})}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t,n){return r.forEach(n,function(n){e=n(e,t)}),e}},function(e,t){"use strict";e.exports=function(e){return!(!e||!e.__CANCEL__)}},function(e,t,n){"use strict";function r(e,t){!s.isUndefined(e)&&s.isUndefined(e["Content-Type"])&&(e["Content-Type"]=t)}function o(){var e;return"undefined"!=typeof process&&"[object process]"===Object.prototype.toString.call(process)?e=n(13):"undefined"!=typeof XMLHttpRequest&&(e=n(13)),e}var s=n(2),i=n(12),a={"Content-Type":"application/x-www-form-urlencoded"},u={adapter:o(),transformRequest:[function(e,t){return i(t,"Accept"),i(t,"Content-Type"),s.isFormData(e)||s.isArrayBuffer(e)||s.isBuffer(e)||s.isStream(e)||s.isFile(e)||s.isBlob(e)?e:s.isArrayBufferView(e)?e.buffer:s.isURLSearchParams(e)?(r(t,"application/x-www-form-urlencoded;charset=utf-8"),e.toString()):s.isObject(e)?(r(t,"application/json;charset=utf-8"),JSON.stringify(e)):e}],transformResponse:[function(e){if("string"==typeof e)try{e=JSON.parse(e)}catch(e){}return e}],timeout:0,xsrfCookieName:"XSRF-TOKEN",xsrfHeaderName:"X-XSRF-TOKEN",maxContentLength:-1,validateStatus:function(e){return e>=200&&e<300}};u.headers={common:{Accept:"application/json, text/plain, */*"}},s.forEach(["delete","get","head"],function(e){u.headers[e]={}}),s.forEach(["post","put","patch"],function(e){u.headers[e]=s.merge(a)}),e.exports=u},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){r.forEach(e,function(n,r){r!==t&&r.toUpperCase()===t.toUpperCase()&&(e[t]=n,delete e[r])})}},function(e,t,n){"use strict";var r=n(2),o=n(14),s=n(6),i=n(17),a=n(18),u=n(15);e.exports=function(e){return new Promise(function(t,c){var f=e.data,p=e.headers;r.isFormData(f)&&delete p["Content-Type"];var d=new XMLHttpRequest;if(e.auth){var l=e.auth.username||"",h=e.auth.password||"";p.Authorization="Basic "+btoa(l+":"+h)}if(d.open(e.method.toUpperCase(),s(e.url,e.params,e.paramsSerializer),!0),d.timeout=e.timeout,d.onreadystatechange=function(){if(d&&4===d.readyState&&(0!==d.status||d.responseURL&&0===d.responseURL.indexOf("file:"))){var n="getAllResponseHeaders"in d?i(d.getAllResponseHeaders()):null,r=e.responseType&&"text"!==e.responseType?d.response:d.responseText,s={data:r,status:d.status,statusText:d.statusText,headers:n,config:e,request:d};o(t,c,s),d=null}},d.onabort=function(){d&&(c(u("Request aborted",e,"ECONNABORTED",d)),d=null)},d.onerror=function(){c(u("Network Error",e,null,d)),d=null},d.ontimeout=function(){c(u("timeout of "+e.timeout+"ms exceeded",e,"ECONNABORTED",d)),d=null},r.isStandardBrowserEnv()){var m=n(19),y=(e.withCredentials||a(e.url))&&e.xsrfCookieName?m.read(e.xsrfCookieName):void 0;y&&(p[e.xsrfHeaderName]=y)}if("setRequestHeader"in d&&r.forEach(p,function(e,t){"undefined"==typeof f&&"content-type"===t.toLowerCase()?delete p[t]:d.setRequestHeader(t,e)}),e.withCredentials&&(d.withCredentials=!0),e.responseType)try{d.responseType=e.responseType}catch(t){if("json"!==e.responseType)throw t}"function"==typeof e.onDownloadProgress&&d.addEventListener("progress",e.onDownloadProgress),"function"==typeof e.onUploadProgress&&d.upload&&d.upload.addEventListener("progress",e.onUploadProgress),e.cancelToken&&e.cancelToken.promise.then(function(e){d&&(d.abort(),c(e),d=null)}),void 0===f&&(f=null),d.send(f)})}},function(e,t,n){"use strict";var r=n(15);e.exports=function(e,t,n){var o=n.config.validateStatus;!o||o(n.status)?e(n):t(r("Request failed with status code "+n.status,n.config,null,n.request,n))}},function(e,t,n){"use strict";var r=n(16);e.exports=function(e,t,n,o,s){var i=new Error(e);return r(i,t,n,o,s)}},function(e,t){"use strict";e.exports=function(e,t,n,r,o){return e.config=t,n&&(e.code=n),e.request=r,e.response=o,e.isAxiosError=!0,e.toJSON=function(){return{message:this.message,name:this.name,description:this.description,number:this.number,fileName:this.fileName,lineNumber:this.lineNumber,columnNumber:this.columnNumber,stack:this.stack,config:this.config,code:this.code}},e}},function(e,t,n){"use strict";var r=n(2),o=["age","authorization","content-length","content-type","etag","expires","from","host","if-modified-since","if-unmodified-since","last-modified","location","max-forwards","proxy-authorization","referer","retry-after","user-agent"];e.exports=function(e){var t,n,s,i={};return e?(r.forEach(e.split("\n"),function(e){if(s=e.indexOf(":"),t=r.trim(e.substr(0,s)).toLowerCase(),n=r.trim(e.substr(s+1)),t){if(i[t]&&o.indexOf(t)>=0)return;"set-cookie"===t?i[t]=(i[t]?i[t]:[]).concat([n]):i[t]=i[t]?i[t]+", "+n:n}}),i):i}},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){function e(e){var t=e;return n&&(o.setAttribute("href",t),t=o.href),o.setAttribute("href",t),{href:o.href,protocol:o.protocol?o.protocol.replace(/:$/,""):"",host:o.host,search:o.search?o.search.replace(/^\?/,""):"",hash:o.hash?o.hash.replace(/^#/,""):"",hostname:o.hostname,port:o.port,pathname:"/"===o.pathname.charAt(0)?o.pathname:"/"+o.pathname}}var t,n=/(msie|trident)/i.test(navigator.userAgent),o=document.createElement("a");return t=e(window.location.href),function(n){var o=r.isString(n)?e(n):n;return o.protocol===t.protocol&&o.host===t.host}}():function(){return function(){return!0}}()},function(e,t,n){"use strict";var r=n(2);e.exports=r.isStandardBrowserEnv()?function(){return{write:function(e,t,n,o,s,i){var a=[];a.push(e+"="+encodeURIComponent(t)),r.isNumber(n)&&a.push("expires="+new Date(n).toGMTString()),r.isString(o)&&a.push("path="+o),r.isString(s)&&a.push("domain="+s),i===!0&&a.push("secure"),document.cookie=a.join("; ")},read:function(e){var t=document.cookie.match(new RegExp("(^|;\\s*)("+e+")=([^;]*)"));return t?decodeURIComponent(t[3]):null},remove:function(e){this.write(e,"",Date.now()-864e5)}}}():function(){return{write:function(){},read:function(){return null},remove:function(){}}}()},function(e,t){"use strict";e.exports=function(e){return/^([a-z][a-z\d\+\-\.]*:)?\/\//i.test(e)}},function(e,t){"use strict";e.exports=function(e,t){return t?e.replace(/\/+$/,"")+"/"+t.replace(/^\/+/,""):e}},function(e,t,n){"use strict";var r=n(2);e.exports=function(e,t){t=t||{};var n={};return r.forEach(["url","method","params","data"],function(e){"undefined"!=typeof t[e]&&(n[e]=t[e])}),r.forEach(["headers","auth","proxy"],function(o){r.isObject(t[o])?n[o]=r.deepMerge(e[o],t[o]):"undefined"!=typeof t[o]?n[o]=t[o]:r.isObject(e[o])?n[o]=r.deepMerge(e[o]):"undefined"!=typeof e[o]&&(n[o]=e[o])}),r.forEach(["baseURL","transformRequest","transformResponse","paramsSerializer","timeout","withCredentials","adapter","responseType","xsrfCookieName","xsrfHeaderName","onUploadProgress","onDownloadProgress","maxContentLength","validateStatus","maxRedirects","httpAgent","httpsAgent","cancelToken","socketPath"],function(r){"undefined"!=typeof t[r]?n[r]=t[r]:"undefined"!=typeof e[r]&&(n[r]=e[r])}),n}},function(e,t){"use strict";function n(e){this.message=e}n.prototype.toString=function(){return"Cancel"+(this.message?": "+this.message:"")},n.prototype.__CANCEL__=!0,e.exports=n},function(e,t,n){"use strict";function r(e){if("function"!=typeof e)throw new TypeError("executor must be a function.");var t;this.promise=new Promise(function(e){t=e});var n=this;e(function(e){n.reason||(n.reason=new o(e),t(n.reason))})}var o=n(23);r.prototype.throwIfRequested=function(){if(this.reason)throw this.reason},r.source=function(){var e,t=new r(function(t){e=t});return{token:t,cancel:e}},e.exports=r},function(e,t){"use strict";e.exports=function(e){return function(t){return e.apply(null,t)}}}])}); +//# sourceMappingURL=axios.min.map \ No newline at end of file diff --git a/system/author/js/color-picker.min.js b/system/author/js/color-picker.min.js index 97f5955..d294cdd 100644 --- a/system/author/js/color-picker.min.js +++ b/system/author/js/color-picker.min.js @@ -1,9 +1,9 @@ -/*! - * ========================================================== - * COLOR PICKER PLUGIN 1.4.1 - * ========================================================== - * Author: Taufik Nurrohman - * License: MIT - * ---------------------------------------------------------- - */ +/*! + * ========================================================== + * COLOR PICKER PLUGIN 1.4.1 + * ========================================================== + * Author: Taufik Nurrohman + * License: MIT + * ---------------------------------------------------------- + */ !function(t,n,e){function r(t){return void 0!==t}function i(t){return"string"==typeof t}function o(t){return"object"==typeof t}function u(t){return Object.keys(t).length}function c(t,n,e){return n>t?n:t>e?e:t}function s(t,n){return parseInt(t,n||10)}function a(t){return Math.round(t)}function f(t){var n,e,r,i,o,u,c,s,f=+t[0],l=+t[1],h=+t[2];switch(i=Math.floor(6*f),o=6*f-i,u=h*(1-l),c=h*(1-o*l),s=h*(1-(1-o)*l),i=i||0,c=c||0,s=s||0,i%6){case 0:n=h,e=s,r=u;break;case 1:n=c,e=h,r=u;break;case 2:n=u,e=h,r=s;break;case 3:n=u,e=c,r=h;break;case 4:n=s,e=u,r=h;break;case 5:n=h,e=u,r=c}return[a(255*n),a(255*e),a(255*r)]}function l(t){return p(f(t))}function h(t){var n,e=+t[0],r=+t[1],i=+t[2],o=Math.max(e,r,i),u=Math.min(e,r,i),c=o-u,s=0===o?0:c/o,a=o/255;switch(o){case u:n=0;break;case e:n=r-i+c*(i>r?6:0),n/=6*c;break;case r:n=i-e+2*c,n/=6*c;break;case i:n=e-r+4*c,n/=6*c}return[n,s,a]}function p(t){var n=+t[2]|+t[1]<<8|+t[0]<<16;return n="000000"+n.toString(16),n.slice(-6)}function v(t){return h(d(t))}function d(t){return 3===t.length&&(t=t.replace(/./g,"$&$&")),[s(t[0]+t[1],16),s(t[2]+t[3],16),s(t[4]+t[5],16)]}function g(t){return[+t[0]/360,+t[1]/100,+t[2]/100]}function y(t){return[a(360*+t[0]),a(100*+t[1]),a(100*+t[2])]}function x(t){return[+t[0]/255,+t[1]/255,+t[2]/255]}function H(t){if(o(t))return t;var n=/\s*rgb\s*\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)\s*$/i.exec(t),e=/\s*hsv\s*\(\s*(\d+)\s*,\s*(\d+)%\s*,\s*(\d+)%\s*\)\s*$/i.exec(t),r="#"===t[0]&&t.match(/^#([\da-f]{3}|[\da-f]{6})$/i);return r?v(t.slice(1)):e?g([+e[1],+e[2],+e[3]]):n?h([+n[1],+n[2],+n[3]]):[0,1,1]}var b="__instance__",m="firstChild",k=setTimeout;!function(t){t.version="1.4.1",t[b]={},t.each=function(n,e){return k(function(){var e,r=t[b];for(e in r)n.call(r[e],e,r)},0===e?0:e||1),t},t.parse=H,t._HSV2RGB=f,t._HSV2HEX=l,t._RGB2HSV=h,t._HEX2HSV=v,t._HEX2RGB=function(t){return x(d(t))},t.HSV2RGB=function(t){return f(g(t))},t.HSV2HEX=function(t){return l(g(t))},t.RGB2HSV=function(t){return y(h(t))},t.RGB2HEX=p,t.HEX2HSV=function(t){return y(v(t))},t.HEX2RGB=d}(t[e]=function(s,a,h){function p(t,n,e){t=t.split(/\s+/);for(var r=0,i=t.length;i>r;++r)n.addEventListener(t[r],e,!1)}function v(t,n,e){t=t.split(/\s+/);for(var r=0,i=t.length;i>r;++r)n.removeEventListener(t[r],e)}function d(t,n){var e="touches",r="clientX",i="clientY",o=n[e]?n[e][0][r]:n[r],u=n[e]?n[e][0][i]:n[i],c=g(t);return{x:o-c.l,y:u-c.t}}function g(n){var e,r,i;return n===t?(e=t.pageXOffset||M.scrollLeft,r=t.pageYOffset||M.scrollTop):(i=n.getBoundingClientRect(),e=i.left,r=i.top),{l:e,t:r}}function y(t,n){for(;(t=t.parentElement)&&t!==n;);return t}function x(t){t&&t.preventDefault()}function H(n){return n===t?{w:t.innerWidth,h:t.innerHeight}:{w:n.offsetWidth,h:n.offsetHeight}}function w(t){return j||(r(t)?t:!1)}function E(t){j=t}function S(t,n,e){return r(t)?r(n)?(r(O[t])||(O[t]={}),r(e)||(e=u(O[t])),O[t][e]=n,$):O[t]:O}function X(t,n){return r(t)?r(n)?(delete O[t][n],$):(O[t]={},$):(O={},$)}function _(t,n,e){if(!r(O[t]))return $;if(r(e))r(O[t][e])&&O[t][e].apply($,n);else for(var i in O[t])O[t][i].apply($,n);return $}function B(t,n){t&&"h"!==t||_("change:h",n),t&&"sv"!==t||_("change:sv",n),_("change",n)}function R(){return T.parentNode}function V(e,r){function i(t){var n=t.target,e=n===s||y(n,s)===s;e?(V(),_("enter")):$.exit()}function o(t){var n=(f(I),f([I[0],1,1]));q.style.backgroundColor="rgb("+n.join(",")+")",E(I),x(t)}function u(t){var n=c(d(P,t).y,0,L);I[0]=(L-n)/L,F.style.top=n-D/2+"px",o(t)}function g(t){var n=d(q,t),e=c(n.x,0,j),r=c(n.y,0,O);I[1]=1-(j-e)/j,I[2]=(O-r)/O,J.style.right=j-e-tn/2+"px",J.style.top=r-nn/2+"px",o(t)}function b(t){U&&(u(t),on=[l(I)],K||(_("drag:h",on),_("drag",on),B("h",on))),Z&&(g(t),on=[l(I)],Q||(_("drag:sv",on),_("drag",on),B("sv",on))),K=0,Q=0}function m(t){var n=t.target,e=U?"h":"sv",r=[l(I),$],i=n===s||y(n,s)===s,o=n===T||y(n,T)===T;i||o?o&&(_("stop:"+e,r),_("stop",r),B(e,r)):R()&&a!==!1&&($.exit(),B(0,r)),U=0,Z=0}function k(t){K=1,U=1,b(t),x(t),_("start:h",on),_("start",on),B("h",on)}function S(t){Q=1,Z=1,b(t),x(t),_("start:sv",on),_("start",on),B("sv",on)}e||((h||r||C).appendChild(T),$.visible=!0),en=H(T).w,rn=H(T).h;var X=H(q),M=H(J),L=H(P).h,j=X.w,O=X.h,D=H(F).h,tn=M.w,nn=M.h;e?(T.style.left=T.style.top="-9999px",a!==!1&&p(a,s,i),$.create=function(){return V(1),_("create"),$},$.destroy=function(){return a!==!1&&v(a,s,i),$.exit(),E(!1),_("destroy"),$}):G(),A=function(){I=w(I),o(),F.style.top=L-D/2-L*+I[0]+"px",J.style.right=j-tn/2-j*+I[1]+"px",J.style.top=O-nn/2-O*+I[2]+"px"},$.exit=function(){return R()&&(R().removeChild(T),$.visible=!1),v(N,P,k),v(N,q,S),v(W,n,b),v(Y,n,m),v(z,t,G),_("exit"),$},A(),e||(p(N,P,k),p(N,q,S),p(W,n,b),p(Y,n,m),p(z,t,G))}function G(){return $.fit()}var C=n.body,M=n.documentElement,$=this,L=t[e],j=!1,O={},T=n.createElement("div"),N="touchstart mousedown",W="touchmove mousemove",Y="touchend mouseup",z="orientationchange resize";if(!($ instanceof L))return new L(s,a);L[b][s.id||s.name||u(L[b])]=$,r(a)&&a!==!0||(a=N),E(L.parse(s.getAttribute("data-color")||s.value||[0,1,1])),T.className="color-picker",T.innerHTML='
';var A,D=T[m].children,I=w([0,1,1]),P=D[0],q=D[1],F=P[m],J=q[m],K=0,Q=0,U=0,Z=0,tn=0,nn=0,en=0,rn=0,on=[l(I)];return V(1),k(function(){var t=[l(I)];_("create",t),B(0,t)},0),$.fit=function(n){var e=H(t),i=H(M),u=e.w-i.w,a=e.h-M.clientHeight,f=g(t),l=g(s);if(tn=l.l+f.l,nn=l.t+f.t+H(s).h,o(n))r(n[0])&&(tn=n[0]),r(n[1])&&(nn=n[1]);else{var h=f.l,p=f.t,v=f.l+e.w-en-u,d=f.t+e.h-rn-a;tn=c(tn,h,v)>>0,nn=c(nn,p,d)>>0}return T.style.left=tn+"px",T.style.top=nn+"px",_("fit"),$},$.set=function(t){return r(t)?(i(t)&&(t=L.parse(t)),E(t),A(),$):w()},$.get=function(t){return w(t)},$.source=s,$.self=T,$.visible=!1,$.on=S,$.off=X,$.fire=_,$.hooks=O,$.enter=function(t){return V(0,t),_("enter"),$},$})}(window,document,"CP"); \ No newline at end of file diff --git a/system/author/js/lazy-video.js b/system/author/js/lazy-video.js index 4f655e2..b23518c 100644 --- a/system/author/js/lazy-video.js +++ b/system/author/js/lazy-video.js @@ -1,61 +1,61 @@ - -let typemillUtilities = { - - setYoutubeItems: function() - { - this.youtubeItems = document.querySelectorAll( ".youtube" ); - }, - addYoutubePlayButtons: function(){ - if(this.youtubeItems) - { - for(var i = 0; i < this.youtubeItems.length; i++) - { - var youtubeItem = this.youtubeItems[i]; - this.addYoutubePlayButton(youtubeItem); - } - } - }, - - addYoutubePlayButton: function(element) - { - console.info(element.parentNode); - element.parentNode.classList.add("video-container"); - - var youtubePlaybutton = document.createElement("button"); - youtubePlaybutton.classList.add("play-video"); - youtubePlaybutton.value = "Play"; - - element.parentNode.appendChild(youtubePlaybutton); - }, - - start: function(){ - this.setYoutubeItems(); - this.addYoutubePlayButtons(); - this.listenToYoutube(); - }, - - listenToYoutube: function(){ - document.addEventListener('click', function (event) { - - if (event.target.matches('.play-video')) { - - var youtubeID = event.target.parentNode.getElementsByClassName('youtube')[0].id; - - event.preventDefault(); - event.stopPropagation(); - - var iframe = document.createElement( "iframe" ); - - iframe.setAttribute( "frameborder", "0" ); - iframe.setAttribute( "allowfullscreen", "" ); - iframe.setAttribute( "width", "560" ); - iframe.setAttribute( "height", "315" ); - iframe.setAttribute( "src", "https://www.youtube-nocookie.com/embed/" + youtubeID + "?rel=0&showinfo=0&autoplay=1" ); - - var videocontainer = event.target.parentNode; - videocontainer.innerHTML = ""; - videocontainer.appendChild( iframe ); - } - }, true); - }, + +let typemillUtilities = { + + setYoutubeItems: function() + { + this.youtubeItems = document.querySelectorAll( ".youtube" ); + }, + addYoutubePlayButtons: function(){ + if(this.youtubeItems) + { + for(var i = 0; i < this.youtubeItems.length; i++) + { + var youtubeItem = this.youtubeItems[i]; + this.addYoutubePlayButton(youtubeItem); + } + } + }, + + addYoutubePlayButton: function(element) + { + console.info(element.parentNode); + element.parentNode.classList.add("video-container"); + + var youtubePlaybutton = document.createElement("button"); + youtubePlaybutton.classList.add("play-video"); + youtubePlaybutton.value = "Play"; + + element.parentNode.appendChild(youtubePlaybutton); + }, + + start: function(){ + this.setYoutubeItems(); + this.addYoutubePlayButtons(); + this.listenToYoutube(); + }, + + listenToYoutube: function(){ + document.addEventListener('click', function (event) { + + if (event.target.matches('.play-video')) { + + var youtubeID = event.target.parentNode.getElementsByClassName('youtube')[0].id; + + event.preventDefault(); + event.stopPropagation(); + + var iframe = document.createElement( "iframe" ); + + iframe.setAttribute( "frameborder", "0" ); + iframe.setAttribute( "allowfullscreen", "" ); + iframe.setAttribute( "width", "560" ); + iframe.setAttribute( "height", "315" ); + iframe.setAttribute( "src", "https://www.youtube-nocookie.com/embed/" + youtubeID + "?rel=0&showinfo=0&autoplay=1" ); + + var videocontainer = event.target.parentNode; + videocontainer.innerHTML = ""; + videocontainer.appendChild( iframe ); + } + }, true); + }, }; \ No newline at end of file diff --git a/system/author/js/sortable.min.js b/system/author/js/sortable.min.js index 1682d85..ffe3125 100644 --- a/system/author/js/sortable.min.js +++ b/system/author/js/sortable.min.js @@ -1,3 +1,3 @@ -/*! Sortable 1.8.4 - MIT | git://github.com/SortableJS/Sortable.git */ - +/*! Sortable 1.8.4 - MIT | git://github.com/SortableJS/Sortable.git */ + !function(t){"use strict";"function"==typeof define&&define.amd?define(t):"undefined"!=typeof module&&void 0!==module.exports?module.exports=t():window.Sortable=t()}(function(){"use strict";if("undefined"==typeof window||!window.document)return function(){throw new Error("Sortable.js requires a window with a document")};var U,V,f,u,q,G,h,X,Y,A,K,n,Z,Q,l,s,c,p,k,J,$,tt,et,ot,g,nt,I=[],B=!1,v=!1,it=!1,d=[],rt=!1,at=!1,m=[],i=/\s+/g,lt="Sortable"+(new Date).getTime(),b=window,st=b.document,w=b.parseInt,ct=b.setTimeout,e=b.jQuery||b.Zepto,o=b.Polymer,r={capture:!1,passive:!1},dt=!!navigator.userAgent.match(/(?:Trident.*rv[ :]?11\.|msie|iemobile)/i),_=!!navigator.userAgent.match(/Edge/i),y=!!navigator.userAgent.match(/firefox/i),D=!(!navigator.userAgent.match(/safari/i)||navigator.userAgent.match(/chrome/i)||navigator.userAgent.match(/android/i)),S=!!navigator.userAgent.match(/iP(ad|od|hone)/i),T=_||dt?"cssFloat":"float",a="draggable"in st.createElement("div"),C=function(){if(dt)return!1;var t=st.createElement("x");return t.style.cssText="pointer-events:auto","auto"===t.style.pointerEvents}(),ht=!1,E=!1,ut=Math.abs,x=Math.min,N=Math.max,M=[],P=function(t,e){var o=Dt(t),n=w(o.width)-w(o.paddingLeft)-w(o.paddingRight)-w(o.borderLeftWidth)-w(o.borderRightWidth),i=Mt(t,0,e),r=Mt(t,1,e),a=i&&Dt(i),l=r&&Dt(r),s=a&&w(a.marginLeft)+w(a.marginRight)+Lt(i).width,c=l&&w(l.marginLeft)+w(l.marginRight)+Lt(r).width;if("flex"===o.display)return"column"===o.flexDirection||"column-reverse"===o.flexDirection?"vertical":"horizontal";if("grid"===o.display)return o.gridTemplateColumns.split(" ").length<=1?"vertical":"horizontal";if(i&&"none"!==a.float){var d="left"===a.float?"left":"right";return!r||"both"!==l.clear&&l.clear!==d?"horizontal":"vertical"}return i&&("block"===a.display||"flex"===a.display||"table"===a.display||"grid"===a.display||n<=s&&"none"===o[T]||r&&"none"===o[T]&&n=n.left-i&&t<=n.right+i,a=e>=n.top-i&&e<=n.bottom+i;if(r&&a)return d[o]}}(t.clientX,t.clientY);e&&e[lt]._onDragOver({clientX:t.clientX,clientY:t.clientY,target:e,rootEl:e})}};function mt(t,e){if(!t||!t.nodeType||1!==t.nodeType)throw"Sortable: `el` must be HTMLElement, not "+{}.toString.call(t);this.el=t,this.options=e=Bt({},e),t[lt]=this;var o={group:null,sort:!0,disabled:!1,store:null,handle:null,scroll:!0,scrollSensitivity:30,scrollSpeed:10,bubbleScroll:!0,draggable:/[uo]l/i.test(t.nodeName)?">li":">*",swapThreshold:1,invertSwap:!1,invertedSwapThreshold:null,removeCloneOnHide:!0,direction:function(){return P(t,this.options)},ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",ignore:"a, img",filter:null,preventOnFilter:!0,animation:0,easing:null,setData:function(t,e){t.setData("Text",e.textContent)},dropBubble:!1,dragoverBubble:!1,dataIdAttr:"data-id",delay:0,touchStartThreshold:w(window.devicePixelRatio,10)||1,forceFallback:!1,fallbackClass:"sortable-fallback",fallbackOnBody:!1,fallbackTolerance:0,fallbackOffset:{x:0,y:0},supportPointer:!1!==mt.supportPointer&&("PointerEvent"in window||window.navigator&&"msPointerEnabled"in window.navigator),emptyInsertThreshold:5};for(var n in o)!(n in e)&&(e[n]=o[n]);for(var i in W(e),this)"_"===i.charAt(0)&&"function"==typeof this[i]&&(this[i]=this[i].bind(this));this.nativeDraggable=!e.forceFallback&&a,this.nativeDraggable&&(this.options.touchStartThreshold=1),e.supportPointer?wt(t,"pointerdown",this._onTapStart):(wt(t,"mousedown",this._onTapStart),wt(t,"touchstart",this._onTapStart)),this.nativeDraggable&&(wt(t,"dragover",this),wt(t,"dragenter",this)),d.push(this.el),e.store&&e.store.get&&this.sort(e.store.get(this)||[])}function bt(t,e,o,n){if(t){o=o||st;do{if(null!=e&&(">"===e[0]&&t.parentNode===o&&kt(t,e.substring(1))||kt(t,e))||n&&t===o)return t;if(t===o)break}while(t=(i=t).host&&i!==st&&i.host.nodeType?i.host:i.parentNode)}var i;return null}function wt(t,e,o){t.addEventListener(e,o,r)}function _t(t,e,o){t.removeEventListener(e,o,r)}function yt(t,e,o){if(t&&e)if(t.classList)t.classList[o?"add":"remove"](e);else{var n=(" "+t.className+" ").replace(i," ").replace(" "+e+" "," ");t.className=(n+(o?" "+e:"")).replace(i," ")}}function Dt(t,e,o){var n=t&&t.style;if(n){if(void 0===o)return st.defaultView&&st.defaultView.getComputedStyle?o=st.defaultView.getComputedStyle(t,""):t.currentStyle&&(o=t.currentStyle),void 0===e?o:o[e];e in n||-1!==e.indexOf("webkit")||(e="-webkit-"+e),n[e]=o+("string"==typeof o?"":"px")}}function St(t){var e="";do{var o=Dt(t,"transform");o&&"none"!==o&&(e=o+" "+e)}while(t=t.parentNode);return window.DOMMatrix?new DOMMatrix(e):window.WebKitCSSMatrix?new WebKitCSSMatrix(e):window.CSSMatrix?new CSSMatrix(e):void 0}function Tt(t,e,o){if(t){var n=t.getElementsByTagName(e),i=0,r=n.length;if(o)for(;i=Math.floor(this.options.touchStartThreshold/(this.nativeDraggable&&window.devicePixelRatio||1))&&this._disableDelayedDrag()},_disableDelayedDrag:function(){U&&xt(U),clearTimeout(this._dragStartTimer),this._disableDelayedDragEvents()},_disableDelayedDragEvents:function(){var t=this.el.ownerDocument;_t(t,"mouseup",this._disableDelayedDrag),_t(t,"touchend",this._disableDelayedDrag),_t(t,"touchcancel",this._disableDelayedDrag),_t(t,"mousemove",this._delayedDragTouchMoveHandler),_t(t,"touchmove",this._delayedDragTouchMoveHandler),_t(t,"pointermove",this._delayedDragTouchMoveHandler)},_triggerDragStart:function(t,e){e=e||("touch"==t.pointerType?t:null),!this.nativeDraggable||e?this.options.supportPointer?wt(st,"pointermove",this._onTouchMove):wt(st,e?"touchmove":"mousemove",this._onTouchMove):(wt(U,"dragend",this),wt(q,"dragstart",this._onDragStart));try{st.selection?Ht(function(){st.selection.empty()}):window.getSelection().removeAllRanges()}catch(t){}},_dragStarted:function(t,e){if(v=!1,q&&U){this.nativeDraggable&&(wt(st,"dragover",this._handleAutoScroll),wt(st,"dragover",F));var o=this.options;!t&&yt(U,o.dragClass,!1),yt(U,o.ghostClass,!0),Dt(U,"transform",""),mt.active=this,t&&this._appendGhost(),Ct(this,q,"start",U,q,q,K,void 0,e)}else this._nulling()},_emulateDragOver:function(t){if(k){if(this._lastX===k.clientX&&this._lastY===k.clientY&&!t)return;this._lastX=k.clientX,this._lastY=k.clientY,z();for(var e=st.elementFromPoint(k.clientX,k.clientY),o=e;e&&e.shadowRoot;)o=e=e.shadowRoot.elementFromPoint(k.clientX,k.clientY);if(o)do{if(o[lt])if(o[lt]._onDragOver({clientX:k.clientX,clientY:k.clientY,target:e,rootEl:o})&&!this.options.dragoverBubble)break;e=o}while(o=o.parentNode);U.parentNode[lt]._computeIsAligned(k),j()}},_onTouchMove:function(t,e){if(p){var o=this.options,n=o.fallbackTolerance,i=o.fallbackOffset,r=t.touches?t.touches[0]:t,a=f&&St(f),l=f&&a&&a.a,s=f&&a&&a.d,c=S&&g&&Ft(g),d=(r.clientX-p.clientX+i.x)/(l||1)+(c?c[0]-m[0]:0)/(l||1),h=(r.clientY-p.clientY+i.y)/(s||1)+(c?c[1]-m[1]:0)/(s||1),u=t.touches?"translate3d("+d+"px,"+h+"px,0)":"translate("+d+"px,"+h+"px)";if(!mt.active&&!v){if(n&&x(ut(r.clientX-this._lastX),ut(r.clientY-this._lastY))' + - '
Choose a content-type
' + - '
' + - '
' + - '' + - '' + - '
' + - '
' + - '
' + - '' + - '' + - '' + - '
' + - '' + - '' + - '
' + - '
' + - '
' + - '
' + - '
' + - '
' + - '
', - data: function () { - return { - preview: 'visible', - edit: false, - compmarkdown: '', - componentType: '', - disabled: false, - load: false, - newblock: false, - } - }, - mounted: function() - { - eventBus.$on('closeComponents', this.closeComponents); - }, - methods: { - addNewBlock: function(event) - { - /* we have to get from dom because block-data might not be set when user clicked on add button before opened the component */ - var bloxeditor = event.target.closest('.blox-editor'); - var bloxid = bloxeditor.getElementsByClassName('blox')[0].dataset.id; - - this.switchToPreviewMode(); - - /* add new empty data */ - this.$root.$data.html.splice(bloxid,0, false); - this.$root.$data.markdown.splice(bloxid,0, ''); - - /* show overlay and bring newblock to front, so that user cannot change any other data (ids not synchronized with stored data now) */ - this.$root.$data.bloxOverlay = true; - this.$root.$data.newblock = true; - this.newblock = 'newblock'; - self.$root.$data.sortdisabled = true; - }, - closeNewBlock: function($event) - { - var bloxeditor = event.target.closest('.blox-editor'); - var bloxid = bloxeditor.getElementsByClassName('blox')[0].dataset.id; - - this.switchToPreviewMode(); - - this.$root.$data.bloxOverlay = false; - this.$root.$data.newblock = false; - this.newblock = false; - self.$root.$data.sortdisabled = false; - - this.$root.$data.html.splice(bloxid,1); - this.$root.$data.markdown.splice(bloxid,1); - }, - updateMarkdown: function($event) - { - this.compmarkdown = $event; - this.$nextTick(function () { - this.$refs.preview.style.minHeight = this.$refs.component.offsetHeight + 'px'; - }); - }, - switchToEditMode: function() - { - if(this.edit){ return; } - eventBus.$emit('closeComponents'); - self = this; - self.$root.$data.freeze = true; /* freeze the data */ - self.$root.$data.sortdisabled = true; /* disable sorting */ - this.preview = 'hidden'; /* hide the html-preview */ - this.edit = true; /* show the edit-mode */ - this.compmarkdown = self.$root.$data.blockMarkdown; /* get markdown data */ - this.componentType = self.$root.$data.blockType; /* get block-type of element */ - if(this.componentType == 'image-component') - { - setTimeout(function(){ - self.$nextTick(function () - { - self.$refs.preview.style.minHeight = self.$refs.component.offsetHeight + 'px'; - }); - }, 200); - } - else - { - this.$nextTick(function () - { - this.$refs.preview.style.minHeight = self.$refs.component.offsetHeight + 'px'; - }); - } - }, - closeComponents: function() - { - this.preview = 'visible'; - this.edit = false; - this.componentType = false; - if(this.$refs.preview) - { - this.$refs.preview.style.minHeight = "auto"; - } - }, - switchToPreviewMode: function() - { - self = this; - self.$root.$data.freeze = false; /* activate the data again */ - self.$root.sortdisabled = false; /* activate sorting again */ - this.preview = 'visible'; /* show the html-preview */ - this.edit = false; /* hide the edit mode */ - this.compmarkdown = ''; /* clear markdown content */ - this.componentType = false; /* delete the component type */ - self.$root.$data.blockType = false; - self.$root.$data.blockMarkdown = false; - self.$root.$data.file = false; - publishController.errors.message = false; /* delete all error messages */ - this.$refs.preview.style.minHeight = "auto"; - }, - freezePage: function() - { - this.disabled = 'disabled'; - this.load = true; - publishController.errors.message = false; - publishController.publishDisabled = true; - var self = this; - self.$root.$data.freeze = true; - }, - activatePage: function() - { - this.disabled = false; - this.load = false; - publishController.publishDisabled = false; - }, - getData: function() - { - self = this; - if(self.$root.$data.blockType != '') - { - this.switchToEditMode(); - } - }, - submitBlock: function(){ - var emptyline = /^\s*$(?:\r\n?|\n)/gm; - if(this.componentType == "code-component" || this.componentType == "math-component"){ } - else if(this.componentType == "ulist-component" || this.componentType == "olist-component") - { - var listend = (this.componentType == "ulist-component") ? '* \n' : '1. \n'; - var liststyle = (this.componentType == "ulist-component") ? '* ' : '1. '; - - if(this.compmarkdown.endsWith(listend)) - { - this.compmarkdown = this.compmarkdown.replace(listend, ''); - this.saveBlock(); - } - else - { - var mdtextarea = document.getElementsByTagName('textarea'); - var start = mdtextarea[0].selectionStart; - var end = mdtextarea[0].selectionEnd; - - this.compmarkdown = this.compmarkdown.substr(0, end) + liststyle + this.compmarkdown.substr(end); - - mdtextarea[0].focus(); - if(mdtextarea[0].setSelectionRange) - { - setTimeout(function(){ - var spacer = (this.componentType == "ulist-component") ? 2 : 3; - mdtextarea[0].setSelectionRange(end+spacer, end+spacer); - }, 1); - } - } - } - else if(this.compmarkdown.search(emptyline) > -1) - { - var checkempty = this.compmarkdown.replace(/(\r\n|\n|\r|\s)/gm,""); - if(checkempty == '') - { - this.switchToPreviewMode(); - } - else - { - this.saveBlock(); - } - } - }, - saveBlock: function() - { - if(this.compmarkdown == undefined || this.compmarkdown.replace(/(\r\n|\n|\r|\s)/gm,"") == '') - { - this.switchToPreviewMode(); - } - else - { - this.freezePage(); - - var self = this; - -/* if(this.componentType != 'definition-component') - { - var compmarkdown = this.compmarkdown.split('\n\n').join('\n'); - } -*/ var compmarkdown = this.compmarkdown; - - var params = { - 'url': document.getElementById("path").value, - 'markdown': compmarkdown, - 'block_id': self.$root.$data.blockId, - 'csrf_name': document.getElementById("csrf_name").value, - 'csrf_value': document.getElementById("csrf_value").value, - }; - - if(this.componentType == 'image-component' && self.$root.$data.file) - { - var url = self.$root.$data.root + '/api/v1/image'; - var method = 'PUT'; - } - else if(this.componentType == 'video-component') - { - var url = self.$root.$data.root + '/api/v1/video'; - var method = 'POST'; - } - else if(self.$root.$data.newblock || self.$root.$data.blockId == 99999) - { - var url = self.$root.$data.root + '/api/v1/block'; - var method = 'POST'; - } - else - { - var url = self.$root.$data.root + '/api/v1/block'; - var method = 'PUT'; - } - - sendJson(function(response, httpStatus) - { - if(httpStatus == 400) - { - self.activatePage(); - publishController.errors.message = "Looks like you are logged out. Please login and try again."; - } - else if(response) - { - self.activatePage(); - - var result = JSON.parse(response); - - if(result.errors) - { - publishController.errors.message = result.errors.message; - } - else - { - var thisBlockType = self.$root.$data.blockType; - - self.switchToPreviewMode(); - - if(self.$root.$data.blockId == 99999) - { - self.$root.$data.markdown.push(result.markdown); - self.$root.$data.html.push(result.content); - - self.$root.$data.blockMarkdown = ''; - self.$root.$data.blockType = 'markdown-component'; - self.getData(); - var textbox = document.querySelectorAll('textarea')[0]; - if(textbox){ textbox.style.height = "70px"; } - } - else if(self.$root.$data.newblock) - { - self.$root.$data.html.splice(result.id,1,result.content); - self.$root.$data.html.splice(result.toc.id,1,result.toc); - self.$root.$data.markdown[result.id] = result.markdown; - - self.$root.$data.blockMarkdown = ''; - self.$root.$data.blockType = ''; - self.$root.$data.bloxOverlay = false; - self.$root.$data.newblock = false; - self.newblock = false; - } - else - { - self.$root.$data.markdown[result.id] = result.markdown; - self.$root.$data.html.splice(result.id,1,result.content); - - if(result.id == 0){ self.$root.$data.title = result.content; } - - self.$root.$data.blockMarkdown = ''; - self.$root.$data.blockType = ''; - } - - /* update the table of content if in result */ - if(result.toc) - { - self.$root.$data.html.splice(result.toc.id, 1, result.toc); - } - - /* check math here */ - self.$root.checkMath(result.id); - - /* check youtube here */ - if(thisBlockType == "video-component" || thisBlockType == "image-component") - { - self.$root.checkVideo(result.id); - } - - /* update the navigation and mark navigation item as modified */ - navi.getNavi(); - - } - } - else if(httpStatus != 200) - { - self.activatePage(); - publishController.errors.message = "Sorry, something went wrong. Please refresh the page and try again."; - } - }, method, url, params); - } - }, - deleteBlock: function(event) - { - this.freezePage(); - - var bloxeditor = event.target.closest('.blox-editor'); - - var bloxid = bloxeditor.getElementsByClassName('blox')[0].dataset.id; - - var self = this; - - var url = self.$root.$data.root + '/api/v1/block'; - - var params = { - 'url': document.getElementById("path").value, - 'block_id': bloxid, - 'csrf_name': document.getElementById("csrf_name").value, - 'csrf_value': document.getElementById("csrf_value").value, - }; - - var method = 'DELETE'; - - sendJson(function(response, httpStatus) - { - if(httpStatus == 400) - { - self.activatePage(); - } - if(response) - { - self.activatePage(); - - var result = JSON.parse(response); - - if(result.errors) - { - publishController.errors.message = result.errors; - } - else - { - self.switchToPreviewMode(); - self.$root.$data.html.splice(bloxid,1); - self.$root.$data.markdown.splice(bloxid,1); - self.$root.$data.blockMarkdown = ''; - self.$root.$data.blockType = ''; - - /* update the table of content if in result */ - if(result.toc) - { - self.$root.$data.html.splice(result.toc.id, 1, result.toc); - } - - /* update the navigation and mark navigation item as modified */ - navi.getNavi(); - } - } - }, method, url, params); - }, - }, -}) - -const titleComponent = Vue.component('title-component', { - props: ['compmarkdown', 'disabled'], - template: '
', - mounted: function(){ - this.$refs.markdown.focus(); - autosize(document.querySelectorAll('textarea')); - }, - methods: { - updatemarkdown: function(event) - { - this.$emit('updatedMarkdown', event.target.value); - }, - }, -}) - -const markdownComponent = Vue.component('markdown-component', { - props: ['compmarkdown', 'disabled'], - template: '
' + - '
' + - '' + - '
', - mounted: function(){ - this.$refs.markdown.focus(); - autosize(document.querySelectorAll('textarea')); - }, - methods: { - updatemarkdown: function(event) - { - this.$emit('updatedMarkdown', event.target.value); - }, - }, -}) - -const hrComponent = Vue.component('hr-component', { - props: ['compmarkdown', 'disabled'], - template: '
' + - '
' + - '' + - '
', - mounted: function(){ - this.$refs.markdown.focus(); - autosize(document.querySelectorAll('textarea')); - this.$emit('updatedMarkdown', '---'); - }, - methods: { - updatemarkdown: function(event) - { - this.$emit('updatedMarkdown', event.target.value); - }, - }, -}) - -const tocComponent = Vue.component('toc-component', { - props: ['compmarkdown', 'disabled'], - template: '
' + - '
' + - '' + - '
', - mounted: function(){ - this.$refs.markdown.focus(); - autosize(document.querySelectorAll('textarea')); - this.$emit('updatedMarkdown', '[TOC]'); - }, - methods: { - updatemarkdown: function(event) - { - this.$emit('updatedMarkdown', event.target.value); - }, - }, -}) - -const codeComponent = Vue.component('code-component', { - props: ['compmarkdown', 'disabled'], - template: '
' + - '' + - '
' + - '' + - '
', - data: function(){ - return { - codeblock: '' - } - }, - mounted: function(){ - this.$refs.markdown.focus(); - if(this.compmarkdown) - { - var codeblock = this.compmarkdown.replace("````\n", ""); - codeblock = codeblock.replace("```\n", ""); - codeblock = codeblock.replace("\n````", ""); - codeblock = codeblock.replace("\n```", ""); - codeblock = codeblock.replace("\n\n", "\n"); - this.codeblock = codeblock; - } - this.$nextTick(function () { - autosize(document.querySelectorAll('textarea')); - }); - }, - methods: { - createmarkdown: function(event) - { - this.codeblock = event.target.value; - var codeblock = '````\n' + event.target.value + '\n````'; - this.updatemarkdown(codeblock); - }, - updatemarkdown: function(codeblock) - { - this.$emit('updatedMarkdown', codeblock); - }, - }, -}) - -const quoteComponent = Vue.component('quote-component', { - props: ['compmarkdown', 'disabled'], - template: '
' + - '' + - '
' + - '' + - '
', - data: function(){ - return { - quote: '' - } - }, - mounted: function(){ - this.$refs.markdown.focus(); - if(this.compmarkdown) - { - var quote = this.compmarkdown.replace("> ", ""); - quote = this.compmarkdown.replace(">", ""); - this.quote = quote; - } - this.$nextTick(function () { - autosize(document.querySelectorAll('textarea')); - }); - }, - methods: { - createmarkdown: function(event) - { - this.quote = event.target.value; - var quote = '> ' + event.target.value; - this.updatemarkdown(quote); - }, - updatemarkdown: function(quote) - { - this.$emit('updatedMarkdown', quote); - }, - }, -}) - -const ulistComponent = Vue.component('ulist-component', { - props: ['compmarkdown', 'disabled'], - template: '
' + - '
' + - '' + - '
', - mounted: function(){ - this.$refs.markdown.focus(); - if(!this.compmarkdown) - { - this.compmarkdown = '* '; - } - else - { - var lines = this.compmarkdown.split("\n"); - var length = lines.length - var md = ''; - - for(i = 0; i < length; i++) - { - var clean = lines[i]; - clean = clean.replace(/^- /, '* '); - clean = clean.replace(/^\+ /, '* '); - if(i == length-1) - { - md += clean; - } - else - { - md += clean + '\n'; - } - } - this.compmarkdown = md; - } - this.$nextTick(function () { - autosize(document.querySelectorAll('textarea')); - }); - }, - methods: { - updatemarkdown: function(event) - { - this.$emit('updatedMarkdown', event.target.value); - }, - }, -}) - -const olistComponent = Vue.component('olist-component', { - props: ['compmarkdown', 'disabled'], - template: '
' + - '
' + - '' + - '
', - mounted: function(){ - this.$refs.markdown.focus(); - if(!this.compmarkdown) - { - this.compmarkdown = '1. '; - } - this.$nextTick(function () { - autosize(document.querySelectorAll('textarea')); - }); - }, - methods: { - updatemarkdown: function(event) - { - this.$emit('updatedMarkdown', event.target.value); - }, - }, -}) - -const headlineComponent = Vue.component('headline-component', { - props: ['compmarkdown', 'disabled'], - template: '
' + - '
' + - '' + - '' + - '
', - data: function(){ - return { - level: '', - hlevel: '', - } - }, - mounted: function(){ - this.$refs.markdown.focus(); - if(!this.compmarkdown) - { - this.compmarkdown = '## '; - this.level = '2'; - this.hlevel = 'h2'; - } - else - { - this.level = this.getHeadlineLevel(this.compmarkdown); - this.hlevel = 'h' + this.level; - } - }, - methods: { - updatemarkdown: function(event) - { - var headline = event.target.value; - this.level = this.getHeadlineLevel(headline); - if(this.level > 6) - { - headline = '######' + headline.substr(this.level); - this.level = 6; - this.compmarkdown = headline; - } - else if(this.level < 2) - { - headline = '##' + headline.substr(this.level); - this.level = 2; - this.compmarkdown = headline; - } - this.hlevel = 'h' + this.level; - this.$emit('updatedMarkdown', headline); - }, - headlinedown: function() - { - this.level = this.getHeadlineLevel(this.compmarkdown); - if(this.level < 6) - { - this.compmarkdown = this.compmarkdown.substr(0, this.level) + '#' + this.compmarkdown.substr(this.level); - this.level = this.level+1; - this.hlevel = 'h' + this.level; - } - else - { - this.compmarkdown = '##' + this.compmarkdown.substr(this.level); - this.level = 2; - this.hlevel = 'h2'; - } - this.$emit('updatedMarkdown', this.compmarkdown); - }, - getHeadlineLevel: function(str) - { - var count = 0; - for(var i = 0; i < str.length; i++){ - if(str[i] != '#'){ return count } - count++; - } - return count; - }, - }, -}) - -const tableComponent = Vue.component('table-component', { - props: ['compmarkdown', 'disabled'], - data: function(){ - return { - table: [ - ['0', '1', '2'], - ['1', 'Head', 'Head'], - ['2', 'cell', 'cell'], - ['3', 'cell', 'cell'], - ], - editable: 'editable', - noteditable: 'noteditable', - cellcontent: '', - columnbar: false, - rowbar: false, - tablekey: 1, - } - }, - template: '
' + - '
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
{{value}} ' + - '
' + - '
add right column
' + - '
add left column
' + - '
delete column
' + - '
' + - '
' + - '
' + - '
add row above
' + - '
add row below
' + - '
delete row
' + - '
' + - '{{value}}
' + - '
' + - '
add row above
' + - '
add row below
' + - '
delete row
' + - '
' + - '{{ value }}
' + - '
', - mounted: function(){ - this.$refs.markdown.focus(); - if(this.compmarkdown) - { - var table = []; - var lines = this.compmarkdown.split("\n"); - var length = lines.length - var c = 1; - - for(i = 0; i < length; i++) - { - if(i == 1){ continue } - - var line = lines[i].trim(); - var row = line.split("|").map(function(cell){ - return cell.trim(); - }); - if(row[0] == ''){ row.shift() } - if(row[row.length-1] == ''){ row.pop() } - if(i == 0) - { - var rlength = row.length; - var row0 = []; - for(y = 0; y <= rlength; y++) { row0.push(y) } - table.push(row0); - } - row.splice(0,0,c); - c++; - table.push(row); - } - this.table = table; - } - }, - methods: { - updatedata: function(event,col,row) - { - this.table[row][col] = event.target.innerText; - this.markdowntable(); - }, - switchcolumnbar(value) - { - this.rowbar = false; - (this.columnbar == value || value == 0) ? this.columnbar = false : this.columnbar = value; - }, - switchrowbar(value) - { - this.columnbar = false; - (this.rowbar == value || value == 0 || value == 1 )? this.rowbar = false : this.rowbar = value; - }, - addaboverow: function(index) - { - var row = []; - var cols = this.table[0].length; - for(var i = 0; i < cols; i++){ row.push("new"); } - this.table.splice(index,0,row); - this.reindexrows(); - }, - addbelowrow: function(index) - { - var row = []; - var cols = this.table[0].length; - for(var i = 0; i < cols; i++){ row.push("new"); } - this.table.splice(index+1,0,row); - this.reindexrows(); - }, - deleterow: function(index) - { - this.table.splice(index,1); - this.reindexrows(); - }, - addrightcolumn: function(index) - { - var tableLength = this.table.length; - for (var i = 0; i < tableLength; i++) - { - this.table[i].splice(index+1,0,"new"); - } - this.reindexcolumns(); - }, - addleftcolumn: function(index) - { - var tableLength = this.table.length; - for (var i = 0; i < tableLength; i++) - { - this.table[i].splice(index,0,"new"); - } - this.reindexcolumns(); - }, - deletecolumn: function(index) - { - var tableLength = this.table.length; - for (var i = 0; i < tableLength; i++) - { - this.table[i].splice(index,1); - } - this.reindexcolumns(); - }, - reindexrows: function() - { - var tableRows = this.table.length; - for (var i = 0; i < tableRows; i++) - { - Vue.set(this.table[i], 0, i); - } - this.tablekey +=1; - this.markdowntable(); - }, - reindexcolumns: function() - { - var tableColumns = this.table[0].length; - for (var i = 0; i < tableColumns; i++) - { - Vue.set(this.table[0], i, i); - } - this.tablekey +=1; - this.markdowntable(); - }, - markdowntable: function() - { - var markdown = ''; - var separator = '\n|'; - var rows = this.table.length; - var cols = this.table[0].length; - - for(var i = 0; i < cols; i++) - { - if(i == 0){ continue; } - separator += '---|'; - } - - for(var i = 0; i < rows; i++) - { - var row = this.table[i]; - - if(i == 0){ continue; } - - for(var y = 0; y < cols; y++) - { - if(y == 0){ continue; } - - var value = row[y].trim(); - - if(y == 1) - { - markdown += '\n| ' + value + ' | '; - } - else - { - markdown += value + ' | '; - } - } - if(i == 1) { markdown = markdown + separator; } - } - this.$emit('updatedMarkdown', markdown); - }, - updatemarkdown: function(event) - { - this.$emit('updatedMarkdown', event.target.value); - }, - }, -}) - -const definitionComponent = Vue.component('definition-component', { - props: ['compmarkdown', 'disabled', 'load'], - data: function(){ - return { - definitionList: [], - } - }, - template: '
' + - '
' + - '' + - '
' + - '' + - '' + - '' + - '' + - '' + - '
' + - '
' + - '' + - '
' + - '
', - mounted: function(){ - if(this.compmarkdown) - { - var definitionList = this.compmarkdown.replace("\r\n", "\n"); - definitionList = definitionList.replace("\r", "\n"); - definitionList = definitionList.split("\n\n"); - - for(var i=0; i < definitionList.length; i++) - { - var definition = definitionList[i].split("\n"); - - var term = definition[0]; - var description = definition[1]; - var id = i; - - if(description && description.substring(0, 2) == ": ") - { - this.definitionList.push({'term': term ,'description': description.substring(2), 'id': id}); - } - } - } - else - { - this.definitionList.push({'term': '', 'description': '', 'id': 0}); - } - }, - methods: { - updateterm: function(event, dindex) - { - this.definitionList[dindex].term = event.target.value; - }, - updatedescription: function(event, dindex) - { - this.definitionList[dindex].description = event.target.value; - }, - addDefinition: function() - { - var id = this.definitionList.length; - this.definitionList.push({'term': '', 'description': '', 'id': id}); - this.updateMarkdown(); - }, - deleteDefinition: function(dindex) - { - this.definitionList.splice(dindex,1); - this.updateMarkdown(); - }, - moveDefinition: function(evt) - { - this.updateMarkdown(); - }, - updateMarkdown: function() - { - var length = this.definitionList.length; - var markdown = ''; - for(i = 0; i < length; i++) - { - markdown = markdown + this.definitionList[i].term + "\n: " + this.definitionList[i].description + "\n\n"; - } - this.$emit('updatedMarkdown', markdown); - }, - }, -}) - -const mathComponent = Vue.component('math-component', { - props: ['compmarkdown', 'disabled'], - template: '
' + - '' + - '
' + - '' + - '
', - data: function(){ - return { - mathblock: '' - } - }, - mounted: function(){ - this.$refs.markdown.focus(); - if(this.compmarkdown) - { - var dollarMath = new RegExp(/^\$\$[\S\s]+\$\$$/m); - var bracketMath = new RegExp(/^\\\[[\S\s]+\\\]$/m); - - if(dollarMath.test(this.compmarkdown) || bracketMath.test(this.compmarkdown)) - { - var mathExpression = this.compmarkdown.substring(2,this.compmarkdown.length-2); - this.mathblock = mathExpression.trim(); - } - } - this.$nextTick(function () { - autosize(document.querySelectorAll('textarea')); - }); - }, - methods: { - createmarkdown: function(event) - { - this.codeblock = event.target.value; - var codeblock = '$$\n' + event.target.value + '\n$$'; - this.updatemarkdown(codeblock); - }, - updatemarkdown: function(codeblock) - { - this.$emit('updatedMarkdown', codeblock); - }, - }, -}) - -const videoComponent = Vue.component('video-component', { - props: ['compmarkdown', 'disabled', 'load'], - template: '
' + - '
' + - '' + - '
' + - '
', - methods: { - updatemarkdown: function(event) - { - this.$emit('updatedMarkdown', event.target.value); - }, - }, -}) - -const imageComponent = Vue.component('image-component', { - props: ['compmarkdown', 'disabled'], - template: '
' + - '' + - ' ' + - '

drag a picture or click to select

' + - '
' + - '' + - '
' + - '
' + - '' + - '' + - '' + - '' + - '' + - '' + - '
', - data: function(){ - return { - maxsize: 5, // megabyte - imgpreview: false, - load: false, - imgmeta: false, - imgalt: '', - imgtitle: '', - imgcaption: '', - imglink: '', - imgclass: 'center', - imgid: '', - imgfile: 'imgplchldr', - } - }, - mounted: function(){ - - this.$refs.markdown.focus(); - - if(this.compmarkdown) - { - this.imgmeta = true; - - var imgmarkdown = this.compmarkdown; - - var imgcaption = imgmarkdown.match(/\*.*?\*/); - if(imgcaption){ - this.imgcaption = imgcaption[0].slice(1,-1); - imgmarkdown = imgmarkdown.replace(this.imgcaption,''); - imgmarkdown = imgmarkdown.replace(/\r?\n|\r/g,''); - } - - if(this.compmarkdown[0] == '[') - { - var imglink = this.compmarkdown.match(/\(.*?\)/g); - if(imglink[1]) - { - this.imglink = imglink[1].slice(1,-1); - imgmarkdown = imgmarkdown.replace(imglink[1],''); - imgmarkdown = imgmarkdown.slice(1, -1); - } - } - - var imgtitle = imgmarkdown.match(/\".*?\"/); - if(imgtitle) - { - this.imgtitle = imgtitle[0].slice(1,-1); - imgmarkdown = imgmarkdown.replace(imgtitle[0], ''); - } - - var imgalt = imgmarkdown.match(/\[.*?\]/); - if(imgalt) - { - this.imgalt = imgalt[0].slice(1,-1); - } - - var imgattr = imgmarkdown.match(/\{.*?\}/); - if(imgattr) - { - imgattr = imgattr[0].slice(1,-1); - imgattr = imgattr.split(' '); - for (var i = 0; i < imgattr.length; i++) - { - if(imgattr[i].charAt(0) == '.') - { - this.imgclass = imgattr[i].slice(1); - } - else if(imgattr[i].charAt(0) == '#') - { - this.imgid = imgattr[i].slice(1); - } - } - } - - var imgpreview = imgmarkdown.match(/\(.*?\)/); - if(imgpreview) - { - this.imgpreview = imgpreview[0].slice(1,-1); - this.imgfile = this.imgpreview; - } - } - }, - methods: { - isChecked: function(classname) - { - if(this.imgclass == classname) - { - return ' checked'; - } - }, - updatemarkdown: function(event) - { - this.$emit('updatedMarkdown', event.target.value); - }, - createmarkdown: function() - { - var errors = false; - - if(this.imgalt.length < 101) - { - imgmarkdown = '![' + this.imgalt + ']'; - } - else - { - errors = 'Maximum size of image alt-text is 100 characters'; - imgmarkdown = '![]'; - } - - if(this.imgtitle != '') - { - if(this.imgtitle.length < 101) - { - imgmarkdown = imgmarkdown + '(' + this.imgfile + ' "' + this.imgtitle + '")'; - } - else - { - errors = 'Maximum size of image title is 100 characters'; - } - } - else - { - imgmarkdown = imgmarkdown + '(' + this.imgfile + ')'; - } - - var imgattr = ''; - if(this.imgid != '') - { - if(this.imgid.length < 100) - { - imgattr = imgattr + '#' + this.imgid + ' '; - } - else - { - errors = 'Maximum size of image id is 100 characters'; - } - } - if(this.imgclass != '') - { - if(this.imgclass.length < 100) - { - imgattr = imgattr + '.' + this.imgclass; - } - else - { - errors = 'Maximum size of image class is 100 characters'; - } - } - if(this.imgid != '' || this.imgclass != '') - { - imgmarkdown = imgmarkdown + '{' + imgattr + '}'; - } - - if(this.imglink != '') - { - if(this.imglink.length < 101) - { - imgmarkdown = '[' + imgmarkdown + '](' + this.imglink + ')'; - } - else - { - errors = 'Maximum size of image link is 100 characters'; - } - } - - if(this.imgcaption != '') - { - if(this.imgcaption.length < 140) - { - imgmarkdown = imgmarkdown + '\n*' + this.imgcaption + '*'; - } - else - { - errors = 'Maximum size of image caption is 140 characters'; - } - } - - if(errors) - { - this.$parent.freezePage(); - publishController.errors.message = errors; - } - else - { - publishController.errors.message = false; - this.$parent.activatePage(); - this.$emit('updatedMarkdown', imgmarkdown); - } - }, - onFileChange: function( e ) - { - if(e.target.files.length > 0) - { - let imageFile = e.target.files[0]; - let size = imageFile.size / 1024 / 1024; - - if (!imageFile.type.match('image.*')) - { - publishController.errors.message = "Only images are allowed."; - } - else if (size > this.maxsize) - { - publishController.errors.message = "The maximal size of images is " + this.maxsize + " MB"; - } - else - { - self = this; - this.$parent.freezePage(); - this.$root.$data.file = true; - this.load = true; - - let reader = new FileReader(); - reader.readAsDataURL(imageFile); - reader.onload = function(e) { - self.imgpreview = e.target.result; - self.$emit('updatedMarkdown', '![](imgplchldr)'); - - - /* load image to server */ - var url = self.$root.$data.root + '/api/v1/image'; - - var params = { - 'url': document.getElementById("path").value, - 'image': e.target.result, - 'csrf_name': document.getElementById("csrf_name").value, - 'csrf_value': document.getElementById("csrf_value").value, - }; - - var method = 'POST'; - - sendJson(function(response, httpStatus) - { - if(httpStatus == 400) - { - self.$parent.activatePage(); - } - if(response) - { - self.$parent.activatePage(); - self.load = false; - - var result = JSON.parse(response); - - if(result.errors) - { - publishController.errors.message = result.errors; - } - else - { - self.imgmeta = true; - } - } - }, method, url, params); - } - } - } - } - } -}) - -let editor = new Vue({ - delimiters: ['${', '}'], - el: '#blox', - components: { - 'content-component': contentComponent, - 'markdown-component': markdownComponent, - 'hr-component': hrComponent, - 'toc-component': tocComponent, - 'title-component': titleComponent, - 'headline-component': headlineComponent, - 'image-component': imageComponent, - 'code-component': codeComponent, - 'quote-component': quoteComponent, - 'ulist-component': ulistComponent, - 'olist-component': olistComponent, - 'table-component': tableComponent, - 'definition-component': definitionComponent, - 'math-component': mathComponent, - }, - data: { - root: document.getElementById("main").dataset.url, - html: false, - title: false, - markdown: false, - blockId: false, - blockType: false, - blockMarkdown: false, - file: false, - freeze: false, - newBlocks: [], - addblock: false, - draftDisabled: true, - bloxOverlay: false, - sortdisabled: false, - }, - mounted: function(){ - - publishController.visual = true; - - var self = this; - - var url = this.root + '/api/v1/article/html'; - - var params = { - 'url': document.getElementById("path").value, - 'csrf_name': document.getElementById("csrf_name").value, - 'csrf_value': document.getElementById("csrf_value").value, - }; - - var method = 'POST'; - - sendJson(function(response, httpStatus) - { - if(httpStatus == 400) - { - } - if(response) - { - var result = JSON.parse(response); - - if(result.errors) - { - self.errors.title = result.errors; - } - else - { - var contenthtml = result.data; - self.title = contenthtml[0]; - self.html = contenthtml; - var initialcontent = document.getElementById("initial-content"); - initialcontent.parentNode.removeChild(initialcontent); - } - } - }, method, url, params); - - var url = this.root + '/api/v1/article/markdown'; - - sendJson(function(response, httpStatus) - { - if(httpStatus == 400) - { - } - if(response) - { - - var result = JSON.parse(response); - - if(result.errors) - { - self.errors.title = result.errors; - } - else - { - self.markdown = result.data; - - /* make math plugin working */ - if (typeof renderMathInElement === "function") { - self.$nextTick(function () { - renderMathInElement(document.body); - }); - } - - /* check for youtube videos */ - if (typeof typemillUtilities !== "undefined") - { - setTimeout(function(){ - self.$nextTick(function () - { - typemillUtilities.start(); - }); - }, 200); - } - } - } - }, method, url, params); - }, - methods: { - onStart: function(evt) - { - }, - moveBlock: function(evt) - { - var params = { - 'url': document.getElementById("path").value, - 'old_index': evt.oldIndex, - 'new_index': evt.newIndex, - 'csrf_name': document.getElementById("csrf_name").value, - 'csrf_value': document.getElementById("csrf_value").value, - }; - publishController.errors.message = false; - - var url = this.root + '/api/v1/moveblock'; - var self = this; - - var method = 'PUT'; - - sendJson(function(response, httpStatus) - { - if(httpStatus == 400) - { - } - if(response) - { - - var result = JSON.parse(response); - - if(result.errors) - { - publishController.errors.message = result.errors; - publishController.publishDisabled = false; - } - else - { - self.freeze = false; - - self.markdown = result.markdown; - self.blockMarkdown = ''; - self.blockType = ''; - - if(result.toc) - { - self.html.splice(result.toc.id, 1, result.toc); - } - - publishController.publishDisabled = false; - publishController.publishResult = ""; - - /* update the navigation and mark navigation item as modified */ - navi.getNavi(); - - /* update the math if plugin is there */ - self.checkMath(params.new_index+1); - } - } - }, method, url, params); - }, - setData: function(event, blocktype, body) - { - // change this, not needed anymore. - this.blockId = event.currentTarget.dataset.id; - this.blockMarkdown = this.markdown[this.blockId]; - if(blocktype) - { - this.blockType = blocktype; - } - else if(this.blockId == 0) - { - this.blockType = "title-component" - } - else - { - this.blockType = this.determineBlockType(this.blockMarkdown); - } - }, - clearData: function(event) - { - this.blockId = event.currentTarget.dataset.id; - this.blockMarkdown = this.markdown[this.blockId]; - }, - hideModal: function() - { - this.addblock = false; - }, - determineBlockType: function(block) - { - if(block.match(/^\d+\./)){ return "olist-component" } - - var lines = block.split("\n"); - if(lines.length > 1 && lines[1].substr(0,2) == ': ') - { - return "definition-component"; - } - else if(lines.length > 2 && lines[0].indexOf('|') != -1 && /[\-\|: ]{3,}$/.test(lines[1])) - { - return "table-component"; - } - - var firstChar = block[0]; - var secondChar = block[1]; - var thirdChar = block[2]; - - switch(firstChar){ - case ">": - return "quote-component"; - break; - case "#": - return "headline-component"; - break; - case "!": - if(secondChar == "[") { return "image-component" } - break; - case "[": - if(secondChar == "!" && thirdChar == "[") { return "image-component" } else { return "markdown-component" } - break; - case "\\": - if(secondChar == "["){ return "math-component" } else { return "markdown-component"; } - break; - case "$": - if(secondChar == "$"){ return "math-component" } else { return "markdown-component"; } - break; - case "`": - if(secondChar == "`" && thirdChar == "`") { return "code-component" } else { return "markdown-component" } - break; - case "*": - case "-": - case "+": - if(secondChar == " "){ return "ulist-component" } else { return "markdown-component" } - break; - default: - return 'markdown-component'; - } - }, - checkMath(elementid) - { - /* make math plugin working */ - if (typeof renderMathInElement === "function") - { - self.$nextTick(function () { - renderMathInElement(document.getElementById("blox-"+elementid)); - }); - } - if (typeof MathJax !== false) { - self.$nextTick(function () { - MathJax.Hub.Queue(["Typeset",MathJax.Hub,"blox-"+elementid]); - }); - } - }, - initiateVideo() - { - /* check for youtube videos */ - if (typeof typemillUtilities !== "undefined") - { - this.$nextTick(function () { - typemillUtilities.start(); - }); - } - }, - checkVideo(elementid) - { - /* check for youtube videos */ - var element = document.getElementById("blox-"+elementid); - if(element && typeof typemillUtilities !== "undefined") - { - imageElement = element.getElementsByClassName("youtube"); - if(imageElement[0]) - { - setTimeout(function(){ - self.$nextTick(function () - { - typemillUtilities.addYoutubePlayButton(imageElement[0]); - }); - }, 300); - } - } - } - } +const eventBus = new Vue(); + +const contentComponent = Vue.component('content-block', { + props: ['body', 'load'], + template: '
' + + '
Choose a content-type
' + + '
' + + '
' + + '' + + '' + + '
' + + '
' + + '
' + + '' + + '' + + '' + + '
' + + '' + + '' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
', + data: function () { + return { + preview: 'visible', + edit: false, + compmarkdown: '', + componentType: '', + disabled: false, + load: false, + newblock: false, + } + }, + mounted: function() + { + eventBus.$on('closeComponents', this.closeComponents); + }, + methods: { + addNewBlock: function(event) + { + /* we have to get from dom because block-data might not be set when user clicked on add button before opened the component */ + var bloxeditor = event.target.closest('.blox-editor'); + var bloxid = bloxeditor.getElementsByClassName('blox')[0].dataset.id; + + this.switchToPreviewMode(); + + /* add new empty data */ + this.$root.$data.html.splice(bloxid,0, false); + this.$root.$data.markdown.splice(bloxid,0, ''); + + /* show overlay and bring newblock to front, so that user cannot change any other data (ids not synchronized with stored data now) */ + this.$root.$data.bloxOverlay = true; + this.$root.$data.newblock = true; + this.newblock = 'newblock'; + self.$root.$data.sortdisabled = true; + }, + closeNewBlock: function($event) + { + var bloxeditor = event.target.closest('.blox-editor'); + var bloxid = bloxeditor.getElementsByClassName('blox')[0].dataset.id; + + this.switchToPreviewMode(); + + this.$root.$data.bloxOverlay = false; + this.$root.$data.newblock = false; + this.newblock = false; + self.$root.$data.sortdisabled = false; + + this.$root.$data.html.splice(bloxid,1); + this.$root.$data.markdown.splice(bloxid,1); + }, + updateMarkdown: function($event) + { + this.compmarkdown = $event; + this.$nextTick(function () { + this.$refs.preview.style.minHeight = this.$refs.component.offsetHeight + 'px'; + }); + }, + switchToEditMode: function() + { + if(this.edit){ return; } + eventBus.$emit('closeComponents'); + self = this; + self.$root.$data.freeze = true; /* freeze the data */ + self.$root.$data.sortdisabled = true; /* disable sorting */ + this.preview = 'hidden'; /* hide the html-preview */ + this.edit = true; /* show the edit-mode */ + this.compmarkdown = self.$root.$data.blockMarkdown; /* get markdown data */ + this.componentType = self.$root.$data.blockType; /* get block-type of element */ + if(this.componentType == 'image-component') + { + setTimeout(function(){ + self.$nextTick(function () + { + self.$refs.preview.style.minHeight = self.$refs.component.offsetHeight + 'px'; + }); + }, 200); + } + else + { + this.$nextTick(function () + { + this.$refs.preview.style.minHeight = self.$refs.component.offsetHeight + 'px'; + }); + } + }, + closeComponents: function() + { + this.preview = 'visible'; + this.edit = false; + this.componentType = false; + if(this.$refs.preview) + { + this.$refs.preview.style.minHeight = "auto"; + } + }, + switchToPreviewMode: function() + { + self = this; + self.$root.$data.freeze = false; /* activate the data again */ + self.$root.sortdisabled = false; /* activate sorting again */ + this.preview = 'visible'; /* show the html-preview */ + this.edit = false; /* hide the edit mode */ + this.compmarkdown = ''; /* clear markdown content */ + this.componentType = false; /* delete the component type */ + self.$root.$data.blockType = false; + self.$root.$data.blockMarkdown = false; + self.$root.$data.file = false; + publishController.errors.message = false; /* delete all error messages */ + this.$refs.preview.style.minHeight = "auto"; + }, + freezePage: function() + { + this.disabled = 'disabled'; + this.load = true; + publishController.errors.message = false; + publishController.publishDisabled = true; + var self = this; + self.$root.$data.freeze = true; + }, + activatePage: function() + { + this.disabled = false; + this.load = false; + publishController.publishDisabled = false; + }, + getData: function() + { + self = this; + if(self.$root.$data.blockType != '') + { + this.switchToEditMode(); + } + }, + submitBlock: function(){ + var emptyline = /^\s*$(?:\r\n?|\n)/gm; + if(this.componentType == "code-component" || this.componentType == "math-component"){ } + else if(this.componentType == "ulist-component" || this.componentType == "olist-component") + { + var listend = (this.componentType == "ulist-component") ? '* \n' : '1. \n'; + var liststyle = (this.componentType == "ulist-component") ? '* ' : '1. '; + + if(this.compmarkdown.endsWith(listend)) + { + this.compmarkdown = this.compmarkdown.replace(listend, ''); + this.saveBlock(); + } + else + { + var mdtextarea = document.getElementsByTagName('textarea'); + var start = mdtextarea[0].selectionStart; + var end = mdtextarea[0].selectionEnd; + + this.compmarkdown = this.compmarkdown.substr(0, end) + liststyle + this.compmarkdown.substr(end); + + mdtextarea[0].focus(); + if(mdtextarea[0].setSelectionRange) + { + setTimeout(function(){ + var spacer = (this.componentType == "ulist-component") ? 2 : 3; + mdtextarea[0].setSelectionRange(end+spacer, end+spacer); + }, 1); + } + } + } + else if(this.compmarkdown.search(emptyline) > -1) + { + var checkempty = this.compmarkdown.replace(/(\r\n|\n|\r|\s)/gm,""); + if(checkempty == '') + { + this.switchToPreviewMode(); + } + else + { + this.saveBlock(); + } + } + }, + saveBlock: function() + { + if(this.compmarkdown == undefined || this.compmarkdown.replace(/(\r\n|\n|\r|\s)/gm,"") == '') + { + this.switchToPreviewMode(); + } + else + { + this.freezePage(); + + var self = this; + +/* if(this.componentType != 'definition-component') + { + var compmarkdown = this.compmarkdown.split('\n\n').join('\n'); + } +*/ var compmarkdown = this.compmarkdown; + + var params = { + 'url': document.getElementById("path").value, + 'markdown': compmarkdown, + 'block_id': self.$root.$data.blockId, + 'csrf_name': document.getElementById("csrf_name").value, + 'csrf_value': document.getElementById("csrf_value").value, + }; + + if(this.componentType == 'image-component' && self.$root.$data.file) + { + var url = self.$root.$data.root + '/api/v1/image'; + var method = 'PUT'; + } + else if(this.componentType == 'video-component') + { + var url = self.$root.$data.root + '/api/v1/video'; + var method = 'POST'; + } + else if(self.$root.$data.newblock || self.$root.$data.blockId == 99999) + { + var url = self.$root.$data.root + '/api/v1/block'; + var method = 'POST'; + } + else + { + var url = self.$root.$data.root + '/api/v1/block'; + var method = 'PUT'; + } + + sendJson(function(response, httpStatus) + { + if(httpStatus == 400) + { + self.activatePage(); + publishController.errors.message = "Looks like you are logged out. Please login and try again."; + } + else if(response) + { + self.activatePage(); + + var result = JSON.parse(response); + + if(result.errors) + { + publishController.errors.message = result.errors.message; + } + else + { + var thisBlockType = self.$root.$data.blockType; + + self.switchToPreviewMode(); + + if(self.$root.$data.blockId == 99999) + { + self.$root.$data.markdown.push(result.markdown); + self.$root.$data.html.push(result.content); + + self.$root.$data.blockMarkdown = ''; + self.$root.$data.blockType = 'markdown-component'; + self.getData(); + var textbox = document.querySelectorAll('textarea')[0]; + if(textbox){ textbox.style.height = "70px"; } + } + else if(self.$root.$data.newblock) + { + self.$root.$data.html.splice(result.id,1,result.content); + self.$root.$data.html.splice(result.toc.id,1,result.toc); + self.$root.$data.markdown[result.id] = result.markdown; + + self.$root.$data.blockMarkdown = ''; + self.$root.$data.blockType = ''; + self.$root.$data.bloxOverlay = false; + self.$root.$data.newblock = false; + self.newblock = false; + } + else + { + self.$root.$data.markdown[result.id] = result.markdown; + self.$root.$data.html.splice(result.id,1,result.content); + + if(result.id == 0){ self.$root.$data.title = result.content; } + + self.$root.$data.blockMarkdown = ''; + self.$root.$data.blockType = ''; + } + + /* update the table of content if in result */ + if(result.toc) + { + self.$root.$data.html.splice(result.toc.id, 1, result.toc); + } + + /* check math here */ + self.$root.checkMath(result.id); + + /* check youtube here */ + if(thisBlockType == "video-component" || thisBlockType == "image-component") + { + self.$root.checkVideo(result.id); + } + + /* update the navigation and mark navigation item as modified */ + navi.getNavi(); + + } + } + else if(httpStatus != 200) + { + self.activatePage(); + publishController.errors.message = "Sorry, something went wrong. Please refresh the page and try again."; + } + }, method, url, params); + } + }, + deleteBlock: function(event) + { + this.freezePage(); + + var bloxeditor = event.target.closest('.blox-editor'); + + var bloxid = bloxeditor.getElementsByClassName('blox')[0].dataset.id; + + var self = this; + + var url = self.$root.$data.root + '/api/v1/block'; + + var params = { + 'url': document.getElementById("path").value, + 'block_id': bloxid, + 'csrf_name': document.getElementById("csrf_name").value, + 'csrf_value': document.getElementById("csrf_value").value, + }; + + var method = 'DELETE'; + + sendJson(function(response, httpStatus) + { + if(httpStatus == 400) + { + self.activatePage(); + } + if(response) + { + self.activatePage(); + + var result = JSON.parse(response); + + if(result.errors) + { + publishController.errors.message = result.errors; + } + else + { + self.switchToPreviewMode(); + self.$root.$data.html.splice(bloxid,1); + self.$root.$data.markdown.splice(bloxid,1); + self.$root.$data.blockMarkdown = ''; + self.$root.$data.blockType = ''; + + /* update the table of content if in result */ + if(result.toc) + { + self.$root.$data.html.splice(result.toc.id, 1, result.toc); + } + + /* update the navigation and mark navigation item as modified */ + navi.getNavi(); + } + } + }, method, url, params); + }, + }, +}) + +const titleComponent = Vue.component('title-component', { + props: ['compmarkdown', 'disabled'], + template: '
', + mounted: function(){ + this.$refs.markdown.focus(); + autosize(document.querySelectorAll('textarea')); + }, + methods: { + updatemarkdown: function(event) + { + this.$emit('updatedMarkdown', event.target.value); + }, + }, +}) + +const markdownComponent = Vue.component('markdown-component', { + props: ['compmarkdown', 'disabled'], + template: '
' + + '
' + + '' + + '
', + mounted: function(){ + this.$refs.markdown.focus(); + autosize(document.querySelectorAll('textarea')); + }, + methods: { + updatemarkdown: function(event) + { + this.$emit('updatedMarkdown', event.target.value); + }, + }, +}) + +const hrComponent = Vue.component('hr-component', { + props: ['compmarkdown', 'disabled'], + template: '
' + + '
' + + '' + + '
', + mounted: function(){ + this.$refs.markdown.focus(); + autosize(document.querySelectorAll('textarea')); + this.$emit('updatedMarkdown', '---'); + }, + methods: { + updatemarkdown: function(event) + { + this.$emit('updatedMarkdown', event.target.value); + }, + }, +}) + +const tocComponent = Vue.component('toc-component', { + props: ['compmarkdown', 'disabled'], + template: '
' + + '
' + + '' + + '
', + mounted: function(){ + this.$refs.markdown.focus(); + autosize(document.querySelectorAll('textarea')); + this.$emit('updatedMarkdown', '[TOC]'); + }, + methods: { + updatemarkdown: function(event) + { + this.$emit('updatedMarkdown', event.target.value); + }, + }, +}) + +const codeComponent = Vue.component('code-component', { + props: ['compmarkdown', 'disabled'], + template: '
' + + '' + + '
' + + '' + + '
', + data: function(){ + return { + codeblock: '' + } + }, + mounted: function(){ + this.$refs.markdown.focus(); + if(this.compmarkdown) + { + var codeblock = this.compmarkdown.replace("````\n", ""); + codeblock = codeblock.replace("```\n", ""); + codeblock = codeblock.replace("\n````", ""); + codeblock = codeblock.replace("\n```", ""); + codeblock = codeblock.replace("\n\n", "\n"); + this.codeblock = codeblock; + } + this.$nextTick(function () { + autosize(document.querySelectorAll('textarea')); + }); + }, + methods: { + createmarkdown: function(event) + { + this.codeblock = event.target.value; + var codeblock = '````\n' + event.target.value + '\n````'; + this.updatemarkdown(codeblock); + }, + updatemarkdown: function(codeblock) + { + this.$emit('updatedMarkdown', codeblock); + }, + }, +}) + +const quoteComponent = Vue.component('quote-component', { + props: ['compmarkdown', 'disabled'], + template: '
' + + '' + + '
' + + '' + + '
', + data: function(){ + return { + quote: '' + } + }, + mounted: function(){ + this.$refs.markdown.focus(); + if(this.compmarkdown) + { + var quote = this.compmarkdown.replace("> ", ""); + quote = this.compmarkdown.replace(">", ""); + this.quote = quote; + } + this.$nextTick(function () { + autosize(document.querySelectorAll('textarea')); + }); + }, + methods: { + createmarkdown: function(event) + { + this.quote = event.target.value; + var quote = '> ' + event.target.value; + this.updatemarkdown(quote); + }, + updatemarkdown: function(quote) + { + this.$emit('updatedMarkdown', quote); + }, + }, +}) + +const ulistComponent = Vue.component('ulist-component', { + props: ['compmarkdown', 'disabled'], + template: '
' + + '
' + + '' + + '
', + mounted: function(){ + this.$refs.markdown.focus(); + if(!this.compmarkdown) + { + this.compmarkdown = '* '; + } + else + { + var lines = this.compmarkdown.split("\n"); + var length = lines.length + var md = ''; + + for(i = 0; i < length; i++) + { + var clean = lines[i]; + clean = clean.replace(/^- /, '* '); + clean = clean.replace(/^\+ /, '* '); + if(i == length-1) + { + md += clean; + } + else + { + md += clean + '\n'; + } + } + this.compmarkdown = md; + } + this.$nextTick(function () { + autosize(document.querySelectorAll('textarea')); + }); + }, + methods: { + updatemarkdown: function(event) + { + this.$emit('updatedMarkdown', event.target.value); + }, + }, +}) + +const olistComponent = Vue.component('olist-component', { + props: ['compmarkdown', 'disabled'], + template: '
' + + '
' + + '' + + '
', + mounted: function(){ + this.$refs.markdown.focus(); + if(!this.compmarkdown) + { + this.compmarkdown = '1. '; + } + this.$nextTick(function () { + autosize(document.querySelectorAll('textarea')); + }); + }, + methods: { + updatemarkdown: function(event) + { + this.$emit('updatedMarkdown', event.target.value); + }, + }, +}) + +const headlineComponent = Vue.component('headline-component', { + props: ['compmarkdown', 'disabled'], + template: '
' + + '
' + + '' + + '' + + '
', + data: function(){ + return { + level: '', + hlevel: '', + } + }, + mounted: function(){ + this.$refs.markdown.focus(); + if(!this.compmarkdown) + { + this.compmarkdown = '## '; + this.level = '2'; + this.hlevel = 'h2'; + } + else + { + this.level = this.getHeadlineLevel(this.compmarkdown); + this.hlevel = 'h' + this.level; + } + }, + methods: { + updatemarkdown: function(event) + { + var headline = event.target.value; + this.level = this.getHeadlineLevel(headline); + if(this.level > 6) + { + headline = '######' + headline.substr(this.level); + this.level = 6; + this.compmarkdown = headline; + } + else if(this.level < 2) + { + headline = '##' + headline.substr(this.level); + this.level = 2; + this.compmarkdown = headline; + } + this.hlevel = 'h' + this.level; + this.$emit('updatedMarkdown', headline); + }, + headlinedown: function() + { + this.level = this.getHeadlineLevel(this.compmarkdown); + if(this.level < 6) + { + this.compmarkdown = this.compmarkdown.substr(0, this.level) + '#' + this.compmarkdown.substr(this.level); + this.level = this.level+1; + this.hlevel = 'h' + this.level; + } + else + { + this.compmarkdown = '##' + this.compmarkdown.substr(this.level); + this.level = 2; + this.hlevel = 'h2'; + } + this.$emit('updatedMarkdown', this.compmarkdown); + }, + getHeadlineLevel: function(str) + { + var count = 0; + for(var i = 0; i < str.length; i++){ + if(str[i] != '#'){ return count } + count++; + } + return count; + }, + }, +}) + +const tableComponent = Vue.component('table-component', { + props: ['compmarkdown', 'disabled'], + data: function(){ + return { + table: [ + ['0', '1', '2'], + ['1', 'Head', 'Head'], + ['2', 'cell', 'cell'], + ['3', 'cell', 'cell'], + ], + editable: 'editable', + noteditable: 'noteditable', + cellcontent: '', + columnbar: false, + rowbar: false, + tablekey: 1, + } + }, + template: '
' + + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '' + + '
{{value}} ' + + '
' + + '
add right column
' + + '
add left column
' + + '
delete column
' + + '
' + + '
' + + '
' + + '
add row above
' + + '
add row below
' + + '
delete row
' + + '
' + + '{{value}}
' + + '
' + + '
add row above
' + + '
add row below
' + + '
delete row
' + + '
' + + '{{ value }}
' + + '
', + mounted: function(){ + this.$refs.markdown.focus(); + if(this.compmarkdown) + { + var table = []; + var lines = this.compmarkdown.split("\n"); + var length = lines.length + var c = 1; + + for(i = 0; i < length; i++) + { + if(i == 1){ continue } + + var line = lines[i].trim(); + var row = line.split("|").map(function(cell){ + return cell.trim(); + }); + if(row[0] == ''){ row.shift() } + if(row[row.length-1] == ''){ row.pop() } + if(i == 0) + { + var rlength = row.length; + var row0 = []; + for(y = 0; y <= rlength; y++) { row0.push(y) } + table.push(row0); + } + row.splice(0,0,c); + c++; + table.push(row); + } + this.table = table; + } + }, + methods: { + updatedata: function(event,col,row) + { + this.table[row][col] = event.target.innerText; + this.markdowntable(); + }, + switchcolumnbar(value) + { + this.rowbar = false; + (this.columnbar == value || value == 0) ? this.columnbar = false : this.columnbar = value; + }, + switchrowbar(value) + { + this.columnbar = false; + (this.rowbar == value || value == 0 || value == 1 )? this.rowbar = false : this.rowbar = value; + }, + addaboverow: function(index) + { + var row = []; + var cols = this.table[0].length; + for(var i = 0; i < cols; i++){ row.push("new"); } + this.table.splice(index,0,row); + this.reindexrows(); + }, + addbelowrow: function(index) + { + var row = []; + var cols = this.table[0].length; + for(var i = 0; i < cols; i++){ row.push("new"); } + this.table.splice(index+1,0,row); + this.reindexrows(); + }, + deleterow: function(index) + { + this.table.splice(index,1); + this.reindexrows(); + }, + addrightcolumn: function(index) + { + var tableLength = this.table.length; + for (var i = 0; i < tableLength; i++) + { + this.table[i].splice(index+1,0,"new"); + } + this.reindexcolumns(); + }, + addleftcolumn: function(index) + { + var tableLength = this.table.length; + for (var i = 0; i < tableLength; i++) + { + this.table[i].splice(index,0,"new"); + } + this.reindexcolumns(); + }, + deletecolumn: function(index) + { + var tableLength = this.table.length; + for (var i = 0; i < tableLength; i++) + { + this.table[i].splice(index,1); + } + this.reindexcolumns(); + }, + reindexrows: function() + { + var tableRows = this.table.length; + for (var i = 0; i < tableRows; i++) + { + Vue.set(this.table[i], 0, i); + } + this.tablekey +=1; + this.markdowntable(); + }, + reindexcolumns: function() + { + var tableColumns = this.table[0].length; + for (var i = 0; i < tableColumns; i++) + { + Vue.set(this.table[0], i, i); + } + this.tablekey +=1; + this.markdowntable(); + }, + markdowntable: function() + { + var markdown = ''; + var separator = '\n|'; + var rows = this.table.length; + var cols = this.table[0].length; + + for(var i = 0; i < cols; i++) + { + if(i == 0){ continue; } + separator += '---|'; + } + + for(var i = 0; i < rows; i++) + { + var row = this.table[i]; + + if(i == 0){ continue; } + + for(var y = 0; y < cols; y++) + { + if(y == 0){ continue; } + + var value = row[y].trim(); + + if(y == 1) + { + markdown += '\n| ' + value + ' | '; + } + else + { + markdown += value + ' | '; + } + } + if(i == 1) { markdown = markdown + separator; } + } + this.$emit('updatedMarkdown', markdown); + }, + updatemarkdown: function(event) + { + this.$emit('updatedMarkdown', event.target.value); + }, + }, +}) + +const definitionComponent = Vue.component('definition-component', { + props: ['compmarkdown', 'disabled', 'load'], + data: function(){ + return { + definitionList: [], + } + }, + template: '
' + + '
' + + '' + + '
' + + '' + + '' + + '' + + '' + + '' + + '
' + + '
' + + '' + + '
' + + '
', + mounted: function(){ + if(this.compmarkdown) + { + var definitionList = this.compmarkdown.replace("\r\n", "\n"); + definitionList = definitionList.replace("\r", "\n"); + definitionList = definitionList.split("\n\n"); + + for(var i=0; i < definitionList.length; i++) + { + var definition = definitionList[i].split("\n"); + + var term = definition[0]; + var description = definition[1]; + var id = i; + + if(description && description.substring(0, 2) == ": ") + { + this.definitionList.push({'term': term ,'description': description.substring(2), 'id': id}); + } + } + } + else + { + this.definitionList.push({'term': '', 'description': '', 'id': 0}); + } + }, + methods: { + updateterm: function(event, dindex) + { + this.definitionList[dindex].term = event.target.value; + }, + updatedescription: function(event, dindex) + { + this.definitionList[dindex].description = event.target.value; + }, + addDefinition: function() + { + var id = this.definitionList.length; + this.definitionList.push({'term': '', 'description': '', 'id': id}); + this.updateMarkdown(); + }, + deleteDefinition: function(dindex) + { + this.definitionList.splice(dindex,1); + this.updateMarkdown(); + }, + moveDefinition: function(evt) + { + this.updateMarkdown(); + }, + updateMarkdown: function() + { + var length = this.definitionList.length; + var markdown = ''; + for(i = 0; i < length; i++) + { + markdown = markdown + this.definitionList[i].term + "\n: " + this.definitionList[i].description + "\n\n"; + } + this.$emit('updatedMarkdown', markdown); + }, + }, +}) + +const mathComponent = Vue.component('math-component', { + props: ['compmarkdown', 'disabled'], + template: '
' + + '' + + '
' + + '' + + '
', + data: function(){ + return { + mathblock: '' + } + }, + mounted: function(){ + this.$refs.markdown.focus(); + if(this.compmarkdown) + { + var dollarMath = new RegExp(/^\$\$[\S\s]+\$\$$/m); + var bracketMath = new RegExp(/^\\\[[\S\s]+\\\]$/m); + + if(dollarMath.test(this.compmarkdown) || bracketMath.test(this.compmarkdown)) + { + var mathExpression = this.compmarkdown.substring(2,this.compmarkdown.length-2); + this.mathblock = mathExpression.trim(); + } + } + this.$nextTick(function () { + autosize(document.querySelectorAll('textarea')); + }); + }, + methods: { + createmarkdown: function(event) + { + this.codeblock = event.target.value; + var codeblock = '$$\n' + event.target.value + '\n$$'; + this.updatemarkdown(codeblock); + }, + updatemarkdown: function(codeblock) + { + this.$emit('updatedMarkdown', codeblock); + }, + }, +}) + +const videoComponent = Vue.component('video-component', { + props: ['compmarkdown', 'disabled', 'load'], + template: '
' + + '
' + + '' + + '
' + + '
', + methods: { + updatemarkdown: function(event) + { + this.$emit('updatedMarkdown', event.target.value); + }, + }, +}) + +const imageComponent = Vue.component('image-component', { + props: ['compmarkdown', 'disabled'], + template: '
' + + '' + + ' ' + + '

drag a picture or click to select

' + + '
' + + '' + + '
' + + '
' + + '' + + '' + + '' + + '' + + '' + + '' + + '
', + data: function(){ + return { + maxsize: 5, // megabyte + imgpreview: false, + load: false, + imgmeta: false, + imgalt: '', + imgtitle: '', + imgcaption: '', + imglink: '', + imgclass: 'center', + imgid: '', + imgfile: 'imgplchldr', + } + }, + mounted: function(){ + + this.$refs.markdown.focus(); + + if(this.compmarkdown) + { + this.imgmeta = true; + + var imgmarkdown = this.compmarkdown; + + var imgcaption = imgmarkdown.match(/\*.*?\*/); + if(imgcaption){ + this.imgcaption = imgcaption[0].slice(1,-1); + imgmarkdown = imgmarkdown.replace(this.imgcaption,''); + imgmarkdown = imgmarkdown.replace(/\r?\n|\r/g,''); + } + + if(this.compmarkdown[0] == '[') + { + var imglink = this.compmarkdown.match(/\(.*?\)/g); + if(imglink[1]) + { + this.imglink = imglink[1].slice(1,-1); + imgmarkdown = imgmarkdown.replace(imglink[1],''); + imgmarkdown = imgmarkdown.slice(1, -1); + } + } + + var imgtitle = imgmarkdown.match(/\".*?\"/); + if(imgtitle) + { + this.imgtitle = imgtitle[0].slice(1,-1); + imgmarkdown = imgmarkdown.replace(imgtitle[0], ''); + } + + var imgalt = imgmarkdown.match(/\[.*?\]/); + if(imgalt) + { + this.imgalt = imgalt[0].slice(1,-1); + } + + var imgattr = imgmarkdown.match(/\{.*?\}/); + if(imgattr) + { + imgattr = imgattr[0].slice(1,-1); + imgattr = imgattr.split(' '); + for (var i = 0; i < imgattr.length; i++) + { + if(imgattr[i].charAt(0) == '.') + { + this.imgclass = imgattr[i].slice(1); + } + else if(imgattr[i].charAt(0) == '#') + { + this.imgid = imgattr[i].slice(1); + } + } + } + + var imgpreview = imgmarkdown.match(/\(.*?\)/); + if(imgpreview) + { + this.imgpreview = imgpreview[0].slice(1,-1); + this.imgfile = this.imgpreview; + } + } + }, + methods: { + isChecked: function(classname) + { + if(this.imgclass == classname) + { + return ' checked'; + } + }, + updatemarkdown: function(event) + { + this.$emit('updatedMarkdown', event.target.value); + }, + createmarkdown: function() + { + var errors = false; + + if(this.imgalt.length < 101) + { + imgmarkdown = '![' + this.imgalt + ']'; + } + else + { + errors = 'Maximum size of image alt-text is 100 characters'; + imgmarkdown = '![]'; + } + + if(this.imgtitle != '') + { + if(this.imgtitle.length < 101) + { + imgmarkdown = imgmarkdown + '(' + this.imgfile + ' "' + this.imgtitle + '")'; + } + else + { + errors = 'Maximum size of image title is 100 characters'; + } + } + else + { + imgmarkdown = imgmarkdown + '(' + this.imgfile + ')'; + } + + var imgattr = ''; + if(this.imgid != '') + { + if(this.imgid.length < 100) + { + imgattr = imgattr + '#' + this.imgid + ' '; + } + else + { + errors = 'Maximum size of image id is 100 characters'; + } + } + if(this.imgclass != '') + { + if(this.imgclass.length < 100) + { + imgattr = imgattr + '.' + this.imgclass; + } + else + { + errors = 'Maximum size of image class is 100 characters'; + } + } + if(this.imgid != '' || this.imgclass != '') + { + imgmarkdown = imgmarkdown + '{' + imgattr + '}'; + } + + if(this.imglink != '') + { + if(this.imglink.length < 101) + { + imgmarkdown = '[' + imgmarkdown + '](' + this.imglink + ')'; + } + else + { + errors = 'Maximum size of image link is 100 characters'; + } + } + + if(this.imgcaption != '') + { + if(this.imgcaption.length < 140) + { + imgmarkdown = imgmarkdown + '\n*' + this.imgcaption + '*'; + } + else + { + errors = 'Maximum size of image caption is 140 characters'; + } + } + + if(errors) + { + this.$parent.freezePage(); + publishController.errors.message = errors; + } + else + { + publishController.errors.message = false; + this.$parent.activatePage(); + this.$emit('updatedMarkdown', imgmarkdown); + } + }, + onFileChange: function( e ) + { + if(e.target.files.length > 0) + { + let imageFile = e.target.files[0]; + let size = imageFile.size / 1024 / 1024; + + if (!imageFile.type.match('image.*')) + { + publishController.errors.message = "Only images are allowed."; + } + else if (size > this.maxsize) + { + publishController.errors.message = "The maximal size of images is " + this.maxsize + " MB"; + } + else + { + self = this; + this.$parent.freezePage(); + this.$root.$data.file = true; + this.load = true; + + let reader = new FileReader(); + reader.readAsDataURL(imageFile); + reader.onload = function(e) { + self.imgpreview = e.target.result; + self.$emit('updatedMarkdown', '![](imgplchldr)'); + + + /* load image to server */ + var url = self.$root.$data.root + '/api/v1/image'; + + var params = { + 'url': document.getElementById("path").value, + 'image': e.target.result, + 'csrf_name': document.getElementById("csrf_name").value, + 'csrf_value': document.getElementById("csrf_value").value, + }; + + var method = 'POST'; + + sendJson(function(response, httpStatus) + { + if(httpStatus == 400) + { + self.$parent.activatePage(); + } + if(response) + { + self.$parent.activatePage(); + self.load = false; + + var result = JSON.parse(response); + + if(result.errors) + { + publishController.errors.message = result.errors; + } + else + { + self.imgmeta = true; + } + } + }, method, url, params); + } + } + } + } + } +}) + +let editor = new Vue({ + delimiters: ['${', '}'], + el: '#blox', + components: { + 'content-component': contentComponent, + 'markdown-component': markdownComponent, + 'hr-component': hrComponent, + 'toc-component': tocComponent, + 'title-component': titleComponent, + 'headline-component': headlineComponent, + 'image-component': imageComponent, + 'code-component': codeComponent, + 'quote-component': quoteComponent, + 'ulist-component': ulistComponent, + 'olist-component': olistComponent, + 'table-component': tableComponent, + 'definition-component': definitionComponent, + 'math-component': mathComponent, + }, + data: { + root: document.getElementById("main").dataset.url, + html: false, + title: false, + markdown: false, + blockId: false, + blockType: false, + blockMarkdown: false, + file: false, + freeze: false, + newBlocks: [], + addblock: false, + draftDisabled: true, + bloxOverlay: false, + sortdisabled: false, + }, + mounted: function(){ + + publishController.visual = true; + + var self = this; + + var url = this.root + '/api/v1/article/html'; + + var params = { + 'url': document.getElementById("path").value, + 'csrf_name': document.getElementById("csrf_name").value, + 'csrf_value': document.getElementById("csrf_value").value, + }; + + var method = 'POST'; + + sendJson(function(response, httpStatus) + { + if(httpStatus == 400) + { + } + if(response) + { + var result = JSON.parse(response); + + if(result.errors) + { + self.errors.title = result.errors; + } + else + { + var contenthtml = result.data; + self.title = contenthtml[0]; + self.html = contenthtml; + var initialcontent = document.getElementById("initial-content"); + initialcontent.parentNode.removeChild(initialcontent); + } + } + }, method, url, params); + + var url = this.root + '/api/v1/article/markdown'; + + sendJson(function(response, httpStatus) + { + if(httpStatus == 400) + { + } + if(response) + { + + var result = JSON.parse(response); + + if(result.errors) + { + self.errors.title = result.errors; + } + else + { + self.markdown = result.data; + + /* make math plugin working */ + if (typeof renderMathInElement === "function") { + self.$nextTick(function () { + renderMathInElement(document.body); + }); + } + + /* check for youtube videos */ + if (typeof typemillUtilities !== "undefined") + { + setTimeout(function(){ + self.$nextTick(function () + { + typemillUtilities.start(); + }); + }, 200); + } + } + } + }, method, url, params); + }, + methods: { + onStart: function(evt) + { + }, + moveBlock: function(evt) + { + var params = { + 'url': document.getElementById("path").value, + 'old_index': evt.oldIndex, + 'new_index': evt.newIndex, + 'csrf_name': document.getElementById("csrf_name").value, + 'csrf_value': document.getElementById("csrf_value").value, + }; + publishController.errors.message = false; + + var url = this.root + '/api/v1/moveblock'; + var self = this; + + var method = 'PUT'; + + sendJson(function(response, httpStatus) + { + if(httpStatus == 400) + { + } + if(response) + { + + var result = JSON.parse(response); + + if(result.errors) + { + publishController.errors.message = result.errors; + publishController.publishDisabled = false; + } + else + { + self.freeze = false; + + self.markdown = result.markdown; + self.blockMarkdown = ''; + self.blockType = ''; + + if(result.toc) + { + self.html.splice(result.toc.id, 1, result.toc); + } + + publishController.publishDisabled = false; + publishController.publishResult = ""; + + /* update the navigation and mark navigation item as modified */ + navi.getNavi(); + + /* update the math if plugin is there */ + self.checkMath(params.new_index+1); + } + } + }, method, url, params); + }, + setData: function(event, blocktype, body) + { + // change this, not needed anymore. + this.blockId = event.currentTarget.dataset.id; + this.blockMarkdown = this.markdown[this.blockId]; + if(blocktype) + { + this.blockType = blocktype; + } + else if(this.blockId == 0) + { + this.blockType = "title-component" + } + else + { + this.blockType = this.determineBlockType(this.blockMarkdown); + } + }, + clearData: function(event) + { + this.blockId = event.currentTarget.dataset.id; + this.blockMarkdown = this.markdown[this.blockId]; + }, + hideModal: function() + { + this.addblock = false; + }, + determineBlockType: function(block) + { + if(block.match(/^\d+\./)){ return "olist-component" } + + var lines = block.split("\n"); + if(lines.length > 1 && lines[1].substr(0,2) == ': ') + { + return "definition-component"; + } + else if(lines.length > 2 && lines[0].indexOf('|') != -1 && /[\-\|: ]{3,}$/.test(lines[1])) + { + return "table-component"; + } + + var firstChar = block[0]; + var secondChar = block[1]; + var thirdChar = block[2]; + + switch(firstChar){ + case ">": + return "quote-component"; + break; + case "#": + return "headline-component"; + break; + case "!": + if(secondChar == "[") { return "image-component" } + break; + case "[": + if(secondChar == "!" && thirdChar == "[") { return "image-component" } else { return "markdown-component" } + break; + case "\\": + if(secondChar == "["){ return "math-component" } else { return "markdown-component"; } + break; + case "$": + if(secondChar == "$"){ return "math-component" } else { return "markdown-component"; } + break; + case "`": + if(secondChar == "`" && thirdChar == "`") { return "code-component" } else { return "markdown-component" } + break; + case "*": + case "-": + case "+": + if(secondChar == " "){ return "ulist-component" } else { return "markdown-component" } + break; + default: + return 'markdown-component'; + } + }, + checkMath(elementid) + { + /* make math plugin working */ + if (typeof renderMathInElement === "function") + { + self.$nextTick(function () { + renderMathInElement(document.getElementById("blox-"+elementid)); + }); + } + if (typeof MathJax !== false) { + self.$nextTick(function () { + MathJax.Hub.Queue(["Typeset",MathJax.Hub,"blox-"+elementid]); + }); + } + }, + initiateVideo() + { + /* check for youtube videos */ + if (typeof typemillUtilities !== "undefined") + { + this.$nextTick(function () { + typemillUtilities.start(); + }); + } + }, + checkVideo(elementid) + { + /* check for youtube videos */ + var element = document.getElementById("blox-"+elementid); + if(element && typeof typemillUtilities !== "undefined") + { + imageElement = element.getElementsByClassName("youtube"); + if(imageElement[0]) + { + setTimeout(function(){ + self.$nextTick(function () + { + typemillUtilities.addYoutubePlayButton(imageElement[0]); + }); + }, 300); + } + } + } + } }); \ No newline at end of file diff --git a/system/author/js/vue-editor.js b/system/author/js/vue-editor.js index 13ef10b..c0a08ab 100644 --- a/system/author/js/vue-editor.js +++ b/system/author/js/vue-editor.js @@ -1,26 +1,26 @@ -let editor = new Vue({ - delimiters: ['${', '}'], - el: '#editor', - data: { - errors: { - title: false, - content: false, - }, - form: { - title: document.getElementById("title").value, - content: document.getElementById("content").value, - } - }, - mounted: function(){ - autosize(document.querySelector('textarea')); - publishController.raw = true; - }, - methods: { - changeContent: function(e){ - publishController.draftDisabled = false; - publishController.publishDisabled = false; - publishController.draftResult = ""; - publishController.publishResult = ""; - }, - } +let editor = new Vue({ + delimiters: ['${', '}'], + el: '#editor', + data: { + errors: { + title: false, + content: false, + }, + form: { + title: document.getElementById("title").value, + content: document.getElementById("content").value, + } + }, + mounted: function(){ + autosize(document.querySelector('textarea')); + publishController.raw = true; + }, + methods: { + changeContent: function(e){ + publishController.draftDisabled = false; + publishController.publishDisabled = false; + publishController.draftResult = ""; + publishController.publishResult = ""; + }, + } }); \ No newline at end of file diff --git a/system/author/js/vue-navi.js b/system/author/js/vue-navi.js index 572421a..458a697 100644 --- a/system/author/js/vue-navi.js +++ b/system/author/js/vue-navi.js @@ -1,280 +1,280 @@ -const navcomponent = Vue.component('navigation', { - template: '#navigation-template', - props: ['homepage', 'name', 'newItem', 'parent', 'active', 'filetype', 'status', 'elementtype', 'element', 'folder', 'level', 'url', 'root', 'freeze'], - data: function () { - return { - showForm: false, - } - }, - methods: { - checkMove : function(evt) - { - if(evt.dragged.classList.contains('folder') && evt.from.parentNode.id != evt.to.parentNode.id) - { - return false; - } - if(evt.dragged.firstChild.className == 'active' && !editor.draftDisabled) - { - publishController.errors.message = "Please save your changes before you move the file"; - return false; - } - return true; - }, - onStart : function(evt) - { - /* delete error messages if exist */ - publishController.errors.message = false; - }, - onEnd : function(evt) - { - var locator = { - 'item_id': evt.item.id, - 'parent_id_from': evt.from.parentNode.id, - 'parent_id_to': evt.to.parentNode.id, - 'index_old': evt.oldIndex, - 'index_new': evt.newIndex, - 'active': evt.item.getElementsByTagName('a')[0].className, - 'url': document.getElementById("path").value, - 'csrf_name': document.getElementById("csrf_name").value, - 'csrf_value': document.getElementById("csrf_value").value, - }; - - if(locator.parent_id_from == locator.parent_id_to && locator.index_old == locator.index_new) - { - return - } - - evt.item.classList.add("load"); - - var self = this; - - self.$root.$data.freeze = true; - self.errors = {title: false, content: false, message: false}; - - var url = this.root + '/api/v1/article/sort'; - var method = 'POST'; - - sendJson(function(response, httpStatus) - { - if(response) - { - self.$root.$data.freeze = false; - var result = JSON.parse(response); - - if(result.errors) - { - publishController.errors.message = result.errors; - } - if(result.url) - { - window.location.replace(result.url); - } - if(result.data) - { - evt.item.classList.remove("load"); - self.$root.$data.items = result.data; - } - } - }, method, url, locator ); - }, - getUrl : function(root, url) - { - return root + '/tm/content/' + this.$root.$data.editormode + url - }, - getLevel : function(level) - { - level = level.toString(); - level = level.split('.').length; - return 'level-' + level; - }, - getIcon : function(elementtype, filetype) - { - if(elementtype == 'file') - { - return 'icon-doc-text ' + filetype - } - if(elementtype == 'folder') - { - return 'icon-folder-empty ' + filetype - } - }, - checkActive : function(active,parent) - { - if(active && !parent) - { - return 'active'; - } - return 'inactive'; - }, - toggleForm : function() - { - this.showForm = !this.showForm; - }, - addFile : function(type) - { - publishController.errors.message = false; - - if(this.$root.$data.format.test(this.newItem) || !this.newItem || this.newItem.length > 40) - { - publishController.errors.message = 'Special Characters are not allowed. Length between 1 and 40.'; - return; - } - - var newItem = { - 'folder_id': this.$el.id, - 'item_name': this.newItem, - 'type': type, - 'url': document.getElementById("path").value, - 'csrf_name': document.getElementById("csrf_name").value, - 'csrf_value': document.getElementById("csrf_value").value, - }; - - /* evt.item.classList.add("load"); */ - - var self = this; - - self.$root.$data.freeze = true; - self.errors = {title: false, content: false, message: false}; - - var url = this.root + '/api/v1/article'; - var method = 'POST'; - - sendJson(function(response, httpStatus) - { - if(response) - { - self.$root.$data.freeze = false; - var result = JSON.parse(response); - - if(result.errors) - { - publishController.errors.message = result.errors; - } - if(result.url) - { - window.location.replace(result.url); - } - if(result.data) - { - // evt.item.classList.remove("load"); - self.$root.$data.items = result.data; - self.showForm = false; - } - } - }, method, url, newItem ); - }, - } -}) - -let navi = new Vue({ - el: "#navi", - components: { - 'navcomponent': navcomponent, - }, - data: function () { - return { - title: "Navigation", - items: JSON.parse(document.getElementById("data-navi").dataset.navi), - homepage: JSON.parse(document.getElementById("data-navi").dataset.homepage), - editormode: document.getElementById("data-navi").dataset.editormode, - root: document.getElementById("main").dataset.url, - freeze: false, - modalWindow: false, - format: /[!@#$%^&*()_+=\[\]{};':"\\|,.<>\/?]/, - folderName: '', - } - }, - methods:{ - onStart: function(evt){ - this.$refs.draggit[0].onStart(evt); - }, - onEnd: function(evt){ - this.$refs.draggit[0].onEnd(evt); - }, - showModal: function(e){ - this.modalWindow = true; - }, - hideModal: function(e){ - this.modalWindow = false; - }, - addFolder: function() - { - publishController.errors.message = false; - - if(this.format.test(this.folderName) || this.folderName < 1 || this.folderName.length > 20) - { - publishController.errors.message = 'Special Characters are not allowed. Length between 1 and 20.'; - return; - } - - var newFolder = { - 'item_name': this.folderName, - 'url': document.getElementById("path").value, - 'csrf_name': document.getElementById("csrf_name").value, - 'csrf_value': document.getElementById("csrf_value").value, - }; - - var self = this; - - self.freeze = true; - self.errors = {title: false, content: false, message: false}; - - var url = this.root + '/api/v1/basefolder'; - var method = 'POST'; - - sendJson(function(response, httpStatus) - { - if(response) - { - self.freeze = false; - var result = JSON.parse(response); - - if(result.errors) - { - publishController.errors.message = result.errors; - } - if(result.url) - { - window.location.replace(result.url); - } - if(result.data) - { - self.items = result.data; - } - } - }, method, url, newFolder ); - }, - getNavi: function() - { - publishController.errors.message = false; - - var self = this; - - self.freeze = true; - self.errors = {title: false, content: false, message: false}; - - var activeItem = document.getElementById("path").value; - var url = this.root + '/api/v1/navigation?url=' + activeItem; - var method = 'GET'; - - sendJson(function(response, httpStatus) - { - if(response) - { - self.freeze = false; - var result = JSON.parse(response); - - if(result.errors) - { - publishController.errors.message = result.errors; - } - if(result.data) - { - self.items = result.data; - self.homepage = result.homepage; - } - } - }, method, url, activeItem ); - } - } +const navcomponent = Vue.component('navigation', { + template: '#navigation-template', + props: ['homepage', 'name', 'newItem', 'parent', 'active', 'filetype', 'status', 'elementtype', 'element', 'folder', 'level', 'url', 'root', 'freeze'], + data: function () { + return { + showForm: false, + } + }, + methods: { + checkMove : function(evt) + { + if(evt.dragged.classList.contains('folder') && evt.from.parentNode.id != evt.to.parentNode.id) + { + return false; + } + if(evt.dragged.firstChild.className == 'active' && !editor.draftDisabled) + { + publishController.errors.message = "Please save your changes before you move the file"; + return false; + } + return true; + }, + onStart : function(evt) + { + /* delete error messages if exist */ + publishController.errors.message = false; + }, + onEnd : function(evt) + { + var locator = { + 'item_id': evt.item.id, + 'parent_id_from': evt.from.parentNode.id, + 'parent_id_to': evt.to.parentNode.id, + 'index_old': evt.oldIndex, + 'index_new': evt.newIndex, + 'active': evt.item.getElementsByTagName('a')[0].className, + 'url': document.getElementById("path").value, + 'csrf_name': document.getElementById("csrf_name").value, + 'csrf_value': document.getElementById("csrf_value").value, + }; + + if(locator.parent_id_from == locator.parent_id_to && locator.index_old == locator.index_new) + { + return + } + + evt.item.classList.add("load"); + + var self = this; + + self.$root.$data.freeze = true; + self.errors = {title: false, content: false, message: false}; + + var url = this.root + '/api/v1/article/sort'; + var method = 'POST'; + + sendJson(function(response, httpStatus) + { + if(response) + { + self.$root.$data.freeze = false; + var result = JSON.parse(response); + + if(result.errors) + { + publishController.errors.message = result.errors; + } + if(result.url) + { + window.location.replace(result.url); + } + if(result.data) + { + evt.item.classList.remove("load"); + self.$root.$data.items = result.data; + } + } + }, method, url, locator ); + }, + getUrl : function(root, url) + { + return root + '/tm/content/' + this.$root.$data.editormode + url + }, + getLevel : function(level) + { + level = level.toString(); + level = level.split('.').length; + return 'level-' + level; + }, + getIcon : function(elementtype, filetype) + { + if(elementtype == 'file') + { + return 'icon-doc-text ' + filetype + } + if(elementtype == 'folder') + { + return 'icon-folder-empty ' + filetype + } + }, + checkActive : function(active,parent) + { + if(active && !parent) + { + return 'active'; + } + return 'inactive'; + }, + toggleForm : function() + { + this.showForm = !this.showForm; + }, + addFile : function(type) + { + publishController.errors.message = false; + + if(this.$root.$data.format.test(this.newItem) || !this.newItem || this.newItem.length > 40) + { + publishController.errors.message = 'Special Characters are not allowed. Length between 1 and 40.'; + return; + } + + var newItem = { + 'folder_id': this.$el.id, + 'item_name': this.newItem, + 'type': type, + 'url': document.getElementById("path").value, + 'csrf_name': document.getElementById("csrf_name").value, + 'csrf_value': document.getElementById("csrf_value").value, + }; + + /* evt.item.classList.add("load"); */ + + var self = this; + + self.$root.$data.freeze = true; + self.errors = {title: false, content: false, message: false}; + + var url = this.root + '/api/v1/article'; + var method = 'POST'; + + sendJson(function(response, httpStatus) + { + if(response) + { + self.$root.$data.freeze = false; + var result = JSON.parse(response); + + if(result.errors) + { + publishController.errors.message = result.errors; + } + if(result.url) + { + window.location.replace(result.url); + } + if(result.data) + { + // evt.item.classList.remove("load"); + self.$root.$data.items = result.data; + self.showForm = false; + } + } + }, method, url, newItem ); + }, + } +}) + +let navi = new Vue({ + el: "#navi", + components: { + 'navcomponent': navcomponent, + }, + data: function () { + return { + title: "Navigation", + items: JSON.parse(document.getElementById("data-navi").dataset.navi), + homepage: JSON.parse(document.getElementById("data-navi").dataset.homepage), + editormode: document.getElementById("data-navi").dataset.editormode, + root: document.getElementById("main").dataset.url, + freeze: false, + modalWindow: false, + format: /[!@#$%^&*()_+=\[\]{};':"\\|,.<>\/?]/, + folderName: '', + } + }, + methods:{ + onStart: function(evt){ + this.$refs.draggit[0].onStart(evt); + }, + onEnd: function(evt){ + this.$refs.draggit[0].onEnd(evt); + }, + showModal: function(e){ + this.modalWindow = true; + }, + hideModal: function(e){ + this.modalWindow = false; + }, + addFolder: function() + { + publishController.errors.message = false; + + if(this.format.test(this.folderName) || this.folderName < 1 || this.folderName.length > 20) + { + publishController.errors.message = 'Special Characters are not allowed. Length between 1 and 20.'; + return; + } + + var newFolder = { + 'item_name': this.folderName, + 'url': document.getElementById("path").value, + 'csrf_name': document.getElementById("csrf_name").value, + 'csrf_value': document.getElementById("csrf_value").value, + }; + + var self = this; + + self.freeze = true; + self.errors = {title: false, content: false, message: false}; + + var url = this.root + '/api/v1/basefolder'; + var method = 'POST'; + + sendJson(function(response, httpStatus) + { + if(response) + { + self.freeze = false; + var result = JSON.parse(response); + + if(result.errors) + { + publishController.errors.message = result.errors; + } + if(result.url) + { + window.location.replace(result.url); + } + if(result.data) + { + self.items = result.data; + } + } + }, method, url, newFolder ); + }, + getNavi: function() + { + publishController.errors.message = false; + + var self = this; + + self.freeze = true; + self.errors = {title: false, content: false, message: false}; + + var activeItem = document.getElementById("path").value; + var url = this.root + '/api/v1/navigation?url=' + activeItem; + var method = 'GET'; + + sendJson(function(response, httpStatus) + { + if(response) + { + self.freeze = false; + var result = JSON.parse(response); + + if(result.errors) + { + publishController.errors.message = result.errors; + } + if(result.data) + { + self.items = result.data; + self.homepage = result.homepage; + } + } + }, method, url, activeItem ); + } + } }) \ No newline at end of file diff --git a/system/author/js/vue-publishcontroller.js b/system/author/js/vue-publishcontroller.js index b4ecdfe..e2bdb5f 100644 --- a/system/author/js/vue-publishcontroller.js +++ b/system/author/js/vue-publishcontroller.js @@ -1,236 +1,236 @@ -let publishController = new Vue({ - delimiters: ['${', '}'], - el: '#publishController', - data: { - root: document.getElementById("main").dataset.url, - form: { - title: false, - content: false, - url: document.getElementById("path").value, - csrf_name: document.getElementById("csrf_name").value, - csrf_value: document.getElementById("csrf_value").value, - }, - errors:{ - message: false, - }, - modalWindow: false, - draftDisabled: true, - publishDisabled: document.getElementById("publishController").dataset.drafted ? false : true, - deleteDisabled: false, - draftResult: "", - publishResult: "", - deleteResult: "", - publishStatus: document.getElementById("publishController").dataset.published ? false : true, - publishLabel: document.getElementById("publishController").dataset.published ? "online" : "offline", - publishLabelMobile: document.getElementById("publishController").dataset.published ? "ON" : "OFF", - raw: false, - visual: false, - }, - methods: { - publishDraft: function(e){ - var self = this; - self.errors.message = false; - editor.errors = {title: false, content: false}; - - self.publishResult = "load"; - self.publishDisabled = "disabled"; - - var url = this.root + '/api/v1/article/publish'; - var method = 'POST'; - this.form.raw = this.raw; - if(this.form.raw) - { - this.form.title = editor.form.title; - this.form.content = editor.form.content; - } - - sendJson(function(response, httpStatus) - { - if(httpStatus == 400) - { - self.publishDisabled = false; - self.publishResult = "fail"; - self.errors.message = "You are probably logged out. Please backup your changes, login and then try again." - } - else if(response) - { - var result = JSON.parse(response); - - if(result.errors) - { - self.publishDisabled = false; - self.publishResult = "fail"; - - if(result.errors.title){ editor.errors.title = result.errors.title[0] }; - if(result.errors.content){ editor.errors.content = result.errors.content[0] }; - if(result.errors.message){ self.errors.message = result.errors.message }; - } - else - { - self.draftDisabled = "disabled"; - self.publishResult = "success"; - self.publishStatus = false; - self.publishLabel = "online"; - self.publishLabelMobile = "ON"; - navi.getNavi(); - } - } - else if(httpStatus != 200) - { - self.publishDisabled = false; - self.publishResult = "fail"; - self.errors.message = "Something went wrong, please refresh the page and try again." - } - }, method, url, this.form ); - }, - saveDraft: function(e){ - - var self = this; - self.errors.message = false; - editor.errors = {title: false, content: false}; - - self.draftDisabled = "disabled"; - self.draftResult = "load"; - - var url = this.root + '/api/v1/article'; - var method = 'PUT'; - - this.form.title = editor.form.title; - this.form.content = editor.form.content; - - sendJson(function(response, httpStatus) - { - if(httpStatus == 400) - { - self.publishDisabled = false; - self.publishResult = "fail"; - self.errors.message = "You are probably logged out. Please backup your changes, login and then try again." - } - else if(response) - { - var result = JSON.parse(response); - - if(result.errors) - { - self.draftDisabled = false; - self.draftResult = 'fail'; - - if(result.errors.title){ editor.errors.title = result.errors.title[0]; }; - if(result.errors.content){ editor.errors.content = result.errors.content[0] }; - if(result.errors.message){ self.errors.message = result.errors.message; }; - } - else - { - self.draftResult = 'success'; - navi.getNavi(); - } - } - else if(httpStatus != 200) - { - self.publishDisabled = false; - self.publishResult = "fail"; - self.errors.message = "Something went wrong, please refresh the page and try again." - } - }, method, url, this.form ); - }, - depublishArticle: function(e){ - - if(this.draftDisabled == false) - { - this.errors.message = 'Please save your changes as draft first.'; - return; - } - - var self = this; - self.errors.message = false; - editor.errors = {title: false, content: false}; - - self.publishStatus = "disabled"; - - var url = this.root + '/api/v1/article/unpublish'; - var method = 'DELETE'; - - sendJson(function(response, httpStatus) - { - if(httpStatus == 400) - { - self.publishDisabled = false; - self.publishResult = "fail"; - self.errors.message = "You are probably logged out. Please backup your changes, login and then try again." - } - else if(httpStatus != 200) - { - self.publishDisabled = false; - self.publishResult = "fail"; - self.errors.message = "Something went wrong, please refresh the page and try again." - } - else if(response) - { - var result = JSON.parse(response); - - if(result.errors) - { - self.publishStatus = false; - if(result.errors.message){ self.errors.message = result.errors.message }; - } - else - { - self.publishResult = ""; - self.publishLabel = "offline"; - self.publishLabelMobile = "OFF"; - self.publishDisabled = false; - navi.getNavi(); - } - } - }, method, url, this.form ); - }, - deleteArticle: function(e){ - var self = this; - self.errors.message = false; - editor.errors = {title: false, content: false}; - - self.deleteDisabled = "disabled"; - self.deleteResult = "load"; - - var url = this.root + '/api/v1/article'; - var method = 'DELETE'; - - sendJson(function(response, httpStatus) - { - if(httpStatus == 400) - { - self.publishDisabled = false; - self.publishResult = "fail"; - self.errors.message = "You are probably logged out. Please backup your changes, login and then try again." - } - else if(httpStatus != 200) - { - self.publishDisabled = false; - self.publishResult = "fail"; - self.errors.message = "Something went wrong, please refresh the page and try again." - } - else if(response) - { - var result = JSON.parse(response); - - if(result.errors) - { - self.modalWindow = "modal"; - if(result.errors.message){ self.errors.message = result.errors.message }; - } - else if(result.url) - { - self.modalWindow = "modal"; - window.location.replace(result.url); - } - } - }, method, url, this.form ); - }, - showModal: function(e){ - this.modalWindow = true; - }, - hideModal: function(e){ - this.modalWindow = false; - }, - } +let publishController = new Vue({ + delimiters: ['${', '}'], + el: '#publishController', + data: { + root: document.getElementById("main").dataset.url, + form: { + title: false, + content: false, + url: document.getElementById("path").value, + csrf_name: document.getElementById("csrf_name").value, + csrf_value: document.getElementById("csrf_value").value, + }, + errors:{ + message: false, + }, + modalWindow: false, + draftDisabled: true, + publishDisabled: document.getElementById("publishController").dataset.drafted ? false : true, + deleteDisabled: false, + draftResult: "", + publishResult: "", + deleteResult: "", + publishStatus: document.getElementById("publishController").dataset.published ? false : true, + publishLabel: document.getElementById("publishController").dataset.published ? "online" : "offline", + publishLabelMobile: document.getElementById("publishController").dataset.published ? "ON" : "OFF", + raw: false, + visual: false, + }, + methods: { + publishDraft: function(e){ + var self = this; + self.errors.message = false; + editor.errors = {title: false, content: false}; + + self.publishResult = "load"; + self.publishDisabled = "disabled"; + + var url = this.root + '/api/v1/article/publish'; + var method = 'POST'; + this.form.raw = this.raw; + if(this.form.raw) + { + this.form.title = editor.form.title; + this.form.content = editor.form.content; + } + + sendJson(function(response, httpStatus) + { + if(httpStatus == 400) + { + self.publishDisabled = false; + self.publishResult = "fail"; + self.errors.message = "You are probably logged out. Please backup your changes, login and then try again." + } + else if(response) + { + var result = JSON.parse(response); + + if(result.errors) + { + self.publishDisabled = false; + self.publishResult = "fail"; + + if(result.errors.title){ editor.errors.title = result.errors.title[0] }; + if(result.errors.content){ editor.errors.content = result.errors.content[0] }; + if(result.errors.message){ self.errors.message = result.errors.message }; + } + else + { + self.draftDisabled = "disabled"; + self.publishResult = "success"; + self.publishStatus = false; + self.publishLabel = "online"; + self.publishLabelMobile = "ON"; + navi.getNavi(); + } + } + else if(httpStatus != 200) + { + self.publishDisabled = false; + self.publishResult = "fail"; + self.errors.message = "Something went wrong, please refresh the page and try again." + } + }, method, url, this.form ); + }, + saveDraft: function(e){ + + var self = this; + self.errors.message = false; + editor.errors = {title: false, content: false}; + + self.draftDisabled = "disabled"; + self.draftResult = "load"; + + var url = this.root + '/api/v1/article'; + var method = 'PUT'; + + this.form.title = editor.form.title; + this.form.content = editor.form.content; + + sendJson(function(response, httpStatus) + { + if(httpStatus == 400) + { + self.publishDisabled = false; + self.publishResult = "fail"; + self.errors.message = "You are probably logged out. Please backup your changes, login and then try again." + } + else if(response) + { + var result = JSON.parse(response); + + if(result.errors) + { + self.draftDisabled = false; + self.draftResult = 'fail'; + + if(result.errors.title){ editor.errors.title = result.errors.title[0]; }; + if(result.errors.content){ editor.errors.content = result.errors.content[0] }; + if(result.errors.message){ self.errors.message = result.errors.message; }; + } + else + { + self.draftResult = 'success'; + navi.getNavi(); + } + } + else if(httpStatus != 200) + { + self.publishDisabled = false; + self.publishResult = "fail"; + self.errors.message = "Something went wrong, please refresh the page and try again." + } + }, method, url, this.form ); + }, + depublishArticle: function(e){ + + if(this.draftDisabled == false) + { + this.errors.message = 'Please save your changes as draft first.'; + return; + } + + var self = this; + self.errors.message = false; + editor.errors = {title: false, content: false}; + + self.publishStatus = "disabled"; + + var url = this.root + '/api/v1/article/unpublish'; + var method = 'DELETE'; + + sendJson(function(response, httpStatus) + { + if(httpStatus == 400) + { + self.publishDisabled = false; + self.publishResult = "fail"; + self.errors.message = "You are probably logged out. Please backup your changes, login and then try again." + } + else if(httpStatus != 200) + { + self.publishDisabled = false; + self.publishResult = "fail"; + self.errors.message = "Something went wrong, please refresh the page and try again." + } + else if(response) + { + var result = JSON.parse(response); + + if(result.errors) + { + self.publishStatus = false; + if(result.errors.message){ self.errors.message = result.errors.message }; + } + else + { + self.publishResult = ""; + self.publishLabel = "offline"; + self.publishLabelMobile = "OFF"; + self.publishDisabled = false; + navi.getNavi(); + } + } + }, method, url, this.form ); + }, + deleteArticle: function(e){ + var self = this; + self.errors.message = false; + editor.errors = {title: false, content: false}; + + self.deleteDisabled = "disabled"; + self.deleteResult = "load"; + + var url = this.root + '/api/v1/article'; + var method = 'DELETE'; + + sendJson(function(response, httpStatus) + { + if(httpStatus == 400) + { + self.publishDisabled = false; + self.publishResult = "fail"; + self.errors.message = "You are probably logged out. Please backup your changes, login and then try again." + } + else if(httpStatus != 200) + { + self.publishDisabled = false; + self.publishResult = "fail"; + self.errors.message = "Something went wrong, please refresh the page and try again." + } + else if(response) + { + var result = JSON.parse(response); + + if(result.errors) + { + self.modalWindow = "modal"; + if(result.errors.message){ self.errors.message = result.errors.message }; + } + else if(result.url) + { + self.modalWindow = "modal"; + window.location.replace(result.url); + } + } + }, method, url, this.form ); + }, + showModal: function(e){ + this.modalWindow = true; + }, + hideModal: function(e){ + this.modalWindow = false; + }, + } }); \ No newline at end of file diff --git a/system/author/js/vue.min.js b/system/author/js/vue.min.js index 37febff..ffe47d0 100644 --- a/system/author/js/vue.min.js +++ b/system/author/js/vue.min.js @@ -1,6 +1,6 @@ -/*! - * Vue.js v2.5.16 - * (c) 2014-2018 Evan You - * Released under the MIT License. - */ +/*! + * Vue.js v2.5.16 + * (c) 2014-2018 Evan You + * Released under the MIT License. + */ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):e.Vue=t()}(this,function(){"use strict";var y=Object.freeze({});function M(e){return null==e}function D(e){return null!=e}function S(e){return!0===e}function T(e){return"string"==typeof e||"number"==typeof e||"symbol"==typeof e||"boolean"==typeof e}function P(e){return null!==e&&"object"==typeof e}var r=Object.prototype.toString;function l(e){return"[object Object]"===r.call(e)}function i(e){var t=parseFloat(String(e));return 0<=t&&Math.floor(t)===t&&isFinite(e)}function t(e){return null==e?"":"object"==typeof e?JSON.stringify(e,null,2):String(e)}function F(e){var t=parseFloat(e);return isNaN(t)?e:t}function s(e,t){for(var n=Object.create(null),r=e.split(","),i=0;ie.id;)n--;bt.splice(n+1,0,e)}else bt.push(e);Ct||(Ct=!0,Ze(At))}}(this)},St.prototype.run=function(){if(this.active){var e=this.get();if(e!==this.value||P(e)||this.deep){var t=this.value;if(this.value=e,this.user)try{this.cb.call(this.vm,e,t)}catch(e){Fe(e,this.vm,'callback for watcher "'+this.expression+'"')}else this.cb.call(this.vm,e,t)}}},St.prototype.evaluate=function(){this.value=this.get(),this.dirty=!1},St.prototype.depend=function(){for(var e=this.deps.length;e--;)this.deps[e].depend()},St.prototype.teardown=function(){if(this.active){this.vm._isBeingDestroyed||f(this.vm._watchers,this);for(var e=this.deps.length;e--;)this.deps[e].removeSub(this);this.active=!1}};var Tt={enumerable:!0,configurable:!0,get:$,set:$};function Et(e,t,n){Tt.get=function(){return this[t][n]},Tt.set=function(e){this[t][n]=e},Object.defineProperty(e,n,Tt)}function jt(e){e._watchers=[];var t=e.$options;t.props&&function(n,r){var i=n.$options.propsData||{},o=n._props={},a=n.$options._propKeys=[];n.$parent&&ge(!1);var e=function(e){a.push(e);var t=Ie(e,r,i,n);Ce(o,e,t),e in n||Et(n,"_props",e)};for(var t in r)e(t);ge(!0)}(e,t.props),t.methods&&function(e,t){e.$options.props;for(var n in t)e[n]=null==t[n]?$:v(t[n],e)}(e,t.methods),t.data?function(e){var t=e.$options.data;l(t=e._data="function"==typeof t?function(e,t){se();try{return e.call(t,t)}catch(e){return Fe(e,t,"data()"),{}}finally{ce()}}(t,e):t||{})||(t={});var n=Object.keys(t),r=e.$options.props,i=(e.$options.methods,n.length);for(;i--;){var o=n[i];r&&p(r,o)||(void 0,36!==(a=(o+"").charCodeAt(0))&&95!==a&&Et(e,"_data",o))}var a;we(t,!0)}(e):we(e._data={},!0),t.computed&&function(e,t){var n=e._computedWatchers=Object.create(null),r=Y();for(var i in t){var o=t[i],a="function"==typeof o?o:o.get;r||(n[i]=new St(e,a||$,$,Nt)),i in e||Lt(e,i,o)}}(e,t.computed),t.watch&&t.watch!==G&&function(e,t){for(var n in t){var r=t[n];if(Array.isArray(r))for(var i=0;iparseInt(this.max)&&bn(a,s[0],s,this._vnode)),t.data.keepAlive=!0}return t||e&&e[0]}}};$n=hn,Cn={get:function(){return j}},Object.defineProperty($n,"config",Cn),$n.util={warn:re,extend:m,mergeOptions:Ne,defineReactive:Ce},$n.set=xe,$n.delete=ke,$n.nextTick=Ze,$n.options=Object.create(null),k.forEach(function(e){$n.options[e+"s"]=Object.create(null)}),m(($n.options._base=$n).options.components,kn),$n.use=function(e){var t=this._installedPlugins||(this._installedPlugins=[]);if(-1=a&&l()};setTimeout(function(){c\/=]+)(?:\s*(=)\s*(?:"([^"]*)"+|'([^']*)'+|([^\s"'=<>`]+)))?/,oo="[a-zA-Z_][\\w\\-\\.]*",ao="((?:"+oo+"\\:)?"+oo+")",so=new RegExp("^<"+ao),co=/^\s*(\/?)>/,lo=new RegExp("^<\\/"+ao+"[^>]*>"),uo=/^]+>/i,fo=/^",""":'"',"&":"&"," ":"\n"," ":"\t"},go=/&(?:lt|gt|quot|amp);/g,_o=/&(?:lt|gt|quot|amp|#10|#9);/g,bo=s("pre,textarea",!0),$o=function(e,t){return e&&bo(e)&&"\n"===t[0]};var wo,Co,xo,ko,Ao,Oo,So,To,Eo=/^@|^v-on:/,jo=/^v-|^@|^:/,No=/([^]*?)\s+(?:in|of)\s+([^]*)/,Lo=/,([^,\}\]]*)(?:,([^,\}\]]*))?$/,Io=/^\(|\)$/g,Mo=/:(.*)$/,Do=/^:|^v-bind:/,Po=/\.[^.]+/g,Fo=e(eo);function Ro(e,t,n){return{type:1,tag:e,attrsList:t,attrsMap:function(e){for(var t={},n=0,r=e.length;n]*>)","i")),n=i.replace(t,function(e,t,n){return r=n.length,ho(o)||"noscript"===o||(t=t.replace(//g,"$1").replace(//g,"$1")),$o(o,t)&&(t=t.slice(1)),d.chars&&d.chars(t),""});a+=i.length-n.length,i=n,A(o,a-r,a)}else{var s=i.indexOf("<");if(0===s){if(fo.test(i)){var c=i.indexOf("--\x3e");if(0<=c){d.shouldKeepComment&&d.comment(i.substring(4,c)),C(c+3);continue}}if(po.test(i)){var l=i.indexOf("]>");if(0<=l){C(l+2);continue}}var u=i.match(uo);if(u){C(u[0].length);continue}var f=i.match(lo);if(f){var p=a;C(f[0].length),A(f[1],p,a);continue}var _=x();if(_){k(_),$o(v,i)&&C(1);continue}}var b=void 0,$=void 0,w=void 0;if(0<=s){for($=i.slice(s);!(lo.test($)||so.test($)||fo.test($)||po.test($)||(w=$.indexOf("<",1))<0);)s+=w,$=i.slice(s);b=i.substring(0,s),C(s)}s<0&&(b=i,i=""),d.chars&&b&&d.chars(b)}if(i===e){d.chars&&d.chars(i);break}}function C(e){a+=e,i=i.substring(e)}function x(){var e=i.match(so);if(e){var t,n,r={tagName:e[1],attrs:[],start:a};for(C(e[0].length);!(t=i.match(co))&&(n=i.match(io));)C(n[0].length),r.attrs.push(n);if(t)return r.unarySlash=t[1],C(t[0].length),r.end=a,r}}function k(e){var t=e.tagName,n=e.unarySlash;m&&("p"===v&&ro(t)&&A(v),g(t)&&v===t&&A(t));for(var r,i,o,a=y(t)||!!n,s=e.attrs.length,c=new Array(s),l=0;l-1"+("true"===d?":("+l+")":":_q("+l+","+d+")")),Ar(c,"change","var $$a="+l+",$$el=$event.target,$$c=$$el.checked?("+d+"):("+v+");if(Array.isArray($$a)){var $$v="+(f?"_n("+p+")":p)+",$$i=_i($$a,$$v);if($$el.checked){$$i<0&&("+Er(l,"$$a.concat([$$v])")+")}else{$$i>-1&&("+Er(l,"$$a.slice(0,$$i).concat($$a.slice($$i+1))")+")}}else{"+Er(l,"$$c")+"}",null,!0);else if("input"===$&&"radio"===w)r=e,i=_,a=(o=b)&&o.number,s=Or(r,"value")||"null",Cr(r,"checked","_q("+i+","+(s=a?"_n("+s+")":s)+")"),Ar(r,"change",Er(i,s),null,!0);else if("input"===$||"textarea"===$)!function(e,t,n){var r=e.attrsMap.type,i=n||{},o=i.lazy,a=i.number,s=i.trim,c=!o&&"range"!==r,l=o?"change":"range"===r?Pr:"input",u="$event.target.value";s&&(u="$event.target.value.trim()"),a&&(u="_n("+u+")");var f=Er(t,u);c&&(f="if($event.target.composing)return;"+f),Cr(e,"value","("+t+")"),Ar(e,l,f,null,!0),(s||a)&&Ar(e,"blur","$forceUpdate()")}(e,_,b);else if(!j.isReservedTag($))return Tr(e,_,b),!1;return!0},text:function(e,t){t.value&&Cr(e,"textContent","_s("+t.value+")")},html:function(e,t){t.value&&Cr(e,"innerHTML","_s("+t.value+")")}},isPreTag:function(e){return"pre"===e},isUnaryTag:to,mustUseProp:Sn,canBeLeftOpenTag:no,isReservedTag:Un,getTagNamespace:Vn,staticKeys:(Go=Wo,Go.reduce(function(e,t){return e.concat(t.staticKeys||[])},[]).join(","))},Qo=e(function(e){return s("type,tag,attrsList,attrsMap,plain,parent,children,attrs"+(e?","+e:""))});function ea(e,t){e&&(Zo=Qo(t.staticKeys||""),Xo=t.isReservedTag||O,function e(t){t.static=function(e){if(2===e.type)return!1;if(3===e.type)return!0;return!(!e.pre&&(e.hasBindings||e.if||e.for||c(e.tag)||!Xo(e.tag)||function(e){for(;e.parent;){if("template"!==(e=e.parent).tag)return!1;if(e.for)return!0}return!1}(e)||!Object.keys(e).every(Zo)))}(t);if(1===t.type){if(!Xo(t.tag)&&"slot"!==t.tag&&null==t.attrsMap["inline-template"])return;for(var n=0,r=t.children.length;n|^function\s*\(/,na=/^[A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*|\['[^']*?']|\["[^"]*?"]|\[\d+]|\[[A-Za-z_$][\w$]*])*$/,ra={esc:27,tab:9,enter:13,space:32,up:38,left:37,right:39,down:40,delete:[8,46]},ia={esc:"Escape",tab:"Tab",enter:"Enter",space:" ",up:["Up","ArrowUp"],left:["Left","ArrowLeft"],right:["Right","ArrowRight"],down:["Down","ArrowDown"],delete:["Backspace","Delete"]},oa=function(e){return"if("+e+")return null;"},aa={stop:"$event.stopPropagation();",prevent:"$event.preventDefault();",self:oa("$event.target !== $event.currentTarget"),ctrl:oa("!$event.ctrlKey"),shift:oa("!$event.shiftKey"),alt:oa("!$event.altKey"),meta:oa("!$event.metaKey"),left:oa("'button' in $event && $event.button !== 0"),middle:oa("'button' in $event && $event.button !== 1"),right:oa("'button' in $event && $event.button !== 2")};function sa(e,t,n){var r=t?"nativeOn:{":"on:{";for(var i in e)r+='"'+i+'":'+ca(i,e[i])+",";return r.slice(0,-1)+"}"}function ca(t,e){if(!e)return"function(){}";if(Array.isArray(e))return"["+e.map(function(e){return ca(t,e)}).join(",")+"]";var n=na.test(e.value),r=ta.test(e.value);if(e.modifiers){var i="",o="",a=[];for(var s in e.modifiers)if(aa[s])o+=aa[s],ra[s]&&a.push(s);else if("exact"===s){var c=e.modifiers;o+=oa(["ctrl","shift","alt","meta"].filter(function(e){return!c[e]}).map(function(e){return"$event."+e+"Key"}).join("||"))}else a.push(s);return a.length&&(i+="if(!('button' in $event)&&"+a.map(la).join("&&")+")return null;"),o&&(i+=o),"function($event){"+i+(n?"return "+e.value+"($event)":r?"return ("+e.value+")($event)":e.value)+"}"}return n||r?e.value:"function($event){"+e.value+"}"}function la(e){var t=parseInt(e,10);if(t)return"$event.keyCode!=="+t;var n=ra[e],r=ia[e];return"_k($event.keyCode,"+JSON.stringify(e)+","+JSON.stringify(n)+",$event.key,"+JSON.stringify(r)+")"}var ua={on:function(e,t){e.wrapListeners=function(e){return"_g("+e+","+t.value+")"}},bind:function(t,n){t.wrapData=function(e){return"_b("+e+",'"+t.tag+"',"+n.value+","+(n.modifiers&&n.modifiers.prop?"true":"false")+(n.modifiers&&n.modifiers.sync?",true":"")+")"}},cloak:$},fa=function(e){this.options=e,this.warn=e.warn||$r,this.transforms=wr(e.modules,"transformCode"),this.dataGenFns=wr(e.modules,"genData"),this.directives=m(m({},ua),e.directives);var t=e.isReservedTag||O;this.maybeComponent=function(e){return!t(e.tag)},this.onceId=0,this.staticRenderFns=[]};function pa(e,t){var n=new fa(t);return{render:"with(this){return "+(e?da(e,n):'_c("div")')+"}",staticRenderFns:n.staticRenderFns}}function da(e,t){if(e.staticRoot&&!e.staticProcessed)return va(e,t);if(e.once&&!e.onceProcessed)return ha(e,t);if(e.for&&!e.forProcessed)return f=t,v=(u=e).for,h=u.alias,m=u.iterator1?","+u.iterator1:"",y=u.iterator2?","+u.iterator2:"",u.forProcessed=!0,(d||"_l")+"(("+v+"),function("+h+m+y+"){return "+(p||da)(u,f)+"})";if(e.if&&!e.ifProcessed)return ma(e,t);if("template"!==e.tag||e.slotTarget){if("slot"===e.tag)return function(e,t){var n=e.slotName||'"default"',r=_a(e,t),i="_t("+n+(r?","+r:""),o=e.attrs&&"{"+e.attrs.map(function(e){return g(e.name)+":"+e.value}).join(",")+"}",a=e.attrsMap["v-bind"];!o&&!a||r||(i+=",null");o&&(i+=","+o);a&&(i+=(o?"":",null")+","+a);return i+")"}(e,t);var n;if(e.component)a=e.component,c=t,l=(s=e).inlineTemplate?null:_a(s,c,!0),n="_c("+a+","+ya(s,c)+(l?","+l:"")+")";else{var r=e.plain?void 0:ya(e,t),i=e.inlineTemplate?null:_a(e,t,!0);n="_c('"+e.tag+"'"+(r?","+r:"")+(i?","+i:"")+")"}for(var o=0;o':'
',0=f?t?"":void 0:(i=c.charCodeAt(a),i<55296||i>56319||a+1===f||(u=c.charCodeAt(a+1))<56320||u>57343?t?c.charAt(a):i:t?c.slice(a,a+2):u-56320+(i-55296<<10)+65536)}}},"0390":function(t,n,e){"use strict";var r=e("02f4")(!0);t.exports=function(t,n,e){return n+(e?r(t,n).length:1)}},"07e3":function(t,n){var e={}.hasOwnProperty;t.exports=function(t,n){return e.call(t,n)}},"0bfb":function(t,n,e){"use strict";var r=e("cb7c");t.exports=function(){var t=r(this),n="";return t.global&&(n+="g"),t.ignoreCase&&(n+="i"),t.multiline&&(n+="m"),t.unicode&&(n+="u"),t.sticky&&(n+="y"),n}},"0fc9":function(t,n,e){var r=e("3a38"),o=Math.max,i=Math.min;t.exports=function(t,n){return t=r(t),t<0?o(t+n,0):i(t,n)}},1654:function(t,n,e){"use strict";var r=e("71c1")(!0);e("30f1")(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,n=this._t,e=this._i;return e>=n.length?{value:void 0,done:!0}:(t=r(n,e),this._i+=t.length,{value:t,done:!1})})},1691:function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},"1af6":function(t,n,e){var r=e("63b6");r(r.S,"Array",{isArray:e("9003")})},"1bc3":function(t,n,e){var r=e("f772");t.exports=function(t,n){if(!r(t))return t;var e,o;if(n&&"function"==typeof(e=t.toString)&&!r(o=e.call(t)))return o;if("function"==typeof(e=t.valueOf)&&!r(o=e.call(t)))return o;if(!n&&"function"==typeof(e=t.toString)&&!r(o=e.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},"1ec9":function(t,n,e){var r=e("f772"),o=e("e53d").document,i=r(o)&&r(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},"20fd":function(t,n,e){"use strict";var r=e("d9f6"),o=e("aebd");t.exports=function(t,n,e){n in t?r.f(t,n,o(0,e)):t[n]=e}},"214f":function(t,n,e){"use strict";e("b0c5");var r=e("2aba"),o=e("32e9"),i=e("79e5"),u=e("be13"),c=e("2b4c"),a=e("520a"),f=c("species"),s=!i(function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$")}),l=function(){var t=/(?:)/,n=t.exec;t.exec=function(){return n.apply(this,arguments)};var e="ab".split(t);return 2===e.length&&"a"===e[0]&&"b"===e[1]}();t.exports=function(t,n,e){var p=c(t),d=!i(function(){var n={};return n[p]=function(){return 7},7!=""[t](n)}),v=d?!i(function(){var n=!1,e=/a/;return e.exec=function(){return n=!0,null},"split"===t&&(e.constructor={},e.constructor[f]=function(){return e}),e[p](""),!n}):void 0;if(!d||!v||"replace"===t&&!s||"split"===t&&!l){var h=/./[p],b=e(u,p,""[t],function(t,n,e,r,o){return n.exec===a?d&&!o?{done:!0,value:h.call(n,e,r)}:{done:!0,value:t.call(e,n,r)}:{done:!1}}),g=b[0],y=b[1];r(String.prototype,t,g),o(RegExp.prototype,p,2==n?function(t,n){return y.call(t,this,n)}:function(t){return y.call(t,this)})}}},"230e":function(t,n,e){var r=e("d3f4"),o=e("7726").document,i=r(o)&&r(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},"23c6":function(t,n,e){var r=e("2d95"),o=e("2b4c")("toStringTag"),i="Arguments"==r(function(){return arguments}()),u=function(t,n){try{return t[n]}catch(e){}};t.exports=function(t){var n,e,c;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(e=u(n=Object(t),o))?e:i?r(n):"Object"==(c=r(n))&&"function"==typeof n.callee?"Arguments":c}},"241e":function(t,n,e){var r=e("25eb");t.exports=function(t){return Object(r(t))}},"25eb":function(t,n){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},"294c":function(t,n){t.exports=function(t){try{return!!t()}catch(n){return!0}}},"2aba":function(t,n,e){var r=e("7726"),o=e("32e9"),i=e("69a8"),u=e("ca5a")("src"),c=e("fa5b"),a="toString",f=(""+c).split(a);e("8378").inspectSource=function(t){return c.call(t)},(t.exports=function(t,n,e,c){var a="function"==typeof e;a&&(i(e,"name")||o(e,"name",n)),t[n]!==e&&(a&&(i(e,u)||o(e,u,t[n]?""+t[n]:f.join(String(n)))),t===r?t[n]=e:c?t[n]?t[n]=e:o(t,n,e):(delete t[n],o(t,n,e)))})(Function.prototype,a,function(){return"function"==typeof this&&this[u]||c.call(this)})},"2b4c":function(t,n,e){var r=e("5537")("wks"),o=e("ca5a"),i=e("7726").Symbol,u="function"==typeof i,c=t.exports=function(t){return r[t]||(r[t]=u&&i[t]||(u?i:o)("Symbol."+t))};c.store=r},"2d00":function(t,n){t.exports=!1},"2d95":function(t,n){var e={}.toString;t.exports=function(t){return e.call(t).slice(8,-1)}},"2fdb":function(t,n,e){"use strict";var r=e("5ca1"),o=e("d2c8"),i="includes";r(r.P+r.F*e("5147")(i),"String",{includes:function(t){return!!~o(this,t,i).indexOf(t,arguments.length>1?arguments[1]:void 0)}})},"30f1":function(t,n,e){"use strict";var r=e("b8e3"),o=e("63b6"),i=e("9138"),u=e("35e8"),c=e("481b"),a=e("8f60"),f=e("45f2"),s=e("53e2"),l=e("5168")("iterator"),p=!([].keys&&"next"in[].keys()),d="@@iterator",v="keys",h="values",b=function(){return this};t.exports=function(t,n,e,g,y,x,m){a(e,n,g);var w,O,S,j=function(t){if(!p&&t in C)return C[t];switch(t){case v:return function(){return new e(this,t)};case h:return function(){return new e(this,t)}}return function(){return new e(this,t)}},_=n+" Iterator",M=y==h,T=!1,C=t.prototype,E=C[l]||C[d]||y&&C[y],A=E||j(y),P=y?M?j("entries"):A:void 0,I="Array"==n&&C.entries||E;if(I&&(S=s(I.call(new t)),S!==Object.prototype&&S.next&&(f(S,_,!0),r||"function"==typeof S[l]||u(S,l,b))),M&&E&&E.name!==h&&(T=!0,A=function(){return E.call(this)}),r&&!m||!p&&!T&&C[l]||u(C,l,A),c[n]=A,c[_]=b,y)if(w={values:M?A:j(h),keys:x?A:j(v),entries:P},m)for(O in w)O in C||i(C,O,w[O]);else o(o.P+o.F*(p||T),n,w);return w}},"32a6":function(t,n,e){var r=e("241e"),o=e("c3a1");e("ce7e")("keys",function(){return function(t){return o(r(t))}})},"32e9":function(t,n,e){var r=e("86cc"),o=e("4630");t.exports=e("9e1e")?function(t,n,e){return r.f(t,n,o(1,e))}:function(t,n,e){return t[n]=e,t}},"32fc":function(t,n,e){var r=e("e53d").document;t.exports=r&&r.documentElement},"335c":function(t,n,e){var r=e("6b4c");t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},"355d":function(t,n){n.f={}.propertyIsEnumerable},"35e8":function(t,n,e){var r=e("d9f6"),o=e("aebd");t.exports=e("8e60")?function(t,n,e){return r.f(t,n,o(1,e))}:function(t,n,e){return t[n]=e,t}},"36c3":function(t,n,e){var r=e("335c"),o=e("25eb");t.exports=function(t){return r(o(t))}},3702:function(t,n,e){var r=e("481b"),o=e("5168")("iterator"),i=Array.prototype;t.exports=function(t){return void 0!==t&&(r.Array===t||i[o]===t)}},"3a38":function(t,n){var e=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:e)(t)}},"40c3":function(t,n,e){var r=e("6b4c"),o=e("5168")("toStringTag"),i="Arguments"==r(function(){return arguments}()),u=function(t,n){try{return t[n]}catch(e){}};t.exports=function(t){var n,e,c;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(e=u(n=Object(t),o))?e:i?r(n):"Object"==(c=r(n))&&"function"==typeof n.callee?"Arguments":c}},4588:function(t,n){var e=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:e)(t)}},"45f2":function(t,n,e){var r=e("d9f6").f,o=e("07e3"),i=e("5168")("toStringTag");t.exports=function(t,n,e){t&&!o(t=e?t:t.prototype,i)&&r(t,i,{configurable:!0,value:n})}},4630:function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},"469f":function(t,n,e){e("6c1c"),e("1654"),t.exports=e("7d7b")},"481b":function(t,n){t.exports={}},"4aa6":function(t,n,e){t.exports=e("dc62")},"4bf8":function(t,n,e){var r=e("be13");t.exports=function(t){return Object(r(t))}},"4ee1":function(t,n,e){var r=e("5168")("iterator"),o=!1;try{var i=[7][r]();i["return"]=function(){o=!0},Array.from(i,function(){throw 2})}catch(u){}t.exports=function(t,n){if(!n&&!o)return!1;var e=!1;try{var i=[7],c=i[r]();c.next=function(){return{done:e=!0}},i[r]=function(){return c},t(i)}catch(u){}return e}},"50ed":function(t,n){t.exports=function(t,n){return{value:n,done:!!t}}},5147:function(t,n,e){var r=e("2b4c")("match");t.exports=function(t){var n=/./;try{"/./"[t](n)}catch(e){try{return n[r]=!1,!"/./"[t](n)}catch(o){}}return!0}},5168:function(t,n,e){var r=e("dbdb")("wks"),o=e("62a0"),i=e("e53d").Symbol,u="function"==typeof i,c=t.exports=function(t){return r[t]||(r[t]=u&&i[t]||(u?i:o)("Symbol."+t))};c.store=r},5176:function(t,n,e){t.exports=e("51b6")},"51b6":function(t,n,e){e("a3c3"),t.exports=e("584a").Object.assign},"520a":function(t,n,e){"use strict";var r=e("0bfb"),o=RegExp.prototype.exec,i=String.prototype.replace,u=o,c="lastIndex",a=function(){var t=/a/,n=/b*/g;return o.call(t,"a"),o.call(n,"a"),0!==t[c]||0!==n[c]}(),f=void 0!==/()??/.exec("")[1],s=a||f;s&&(u=function(t){var n,e,u,s,l=this;return f&&(e=new RegExp("^"+l.source+"$(?!\\s)",r.call(l))),a&&(n=l[c]),u=o.call(l,t),a&&u&&(l[c]=l.global?u.index+u[0].length:n),f&&u&&u.length>1&&i.call(u[0],e,function(){for(s=1;s1?arguments[1]:void 0,b=void 0!==h,g=0,y=s(p);if(b&&(h=r(h,v>2?arguments[2]:void 0,2)),void 0==y||d==Array&&c(y))for(n=a(p.length),e=new d(n);n>g;g++)f(e,g,b?h(p[g],g):p[g]);else for(l=y.call(p),e=new d;!(o=l.next()).done;g++)f(e,g,b?u(l,h,[o.value,g],!0):o.value);return e.length=g,e}})},"54a1":function(t,n,e){e("6c1c"),e("1654"),t.exports=e("95d5")},5537:function(t,n,e){var r=e("8378"),o=e("7726"),i="__core-js_shared__",u=o[i]||(o[i]={});(t.exports=function(t,n){return u[t]||(u[t]=void 0!==n?n:{})})("versions",[]).push({version:r.version,mode:e("2d00")?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},5559:function(t,n,e){var r=e("dbdb")("keys"),o=e("62a0");t.exports=function(t){return r[t]||(r[t]=o(t))}},"584a":function(t,n){var e=t.exports={version:"2.6.5"};"number"==typeof __e&&(__e=e)},"5b4e":function(t,n,e){var r=e("36c3"),o=e("b447"),i=e("0fc9");t.exports=function(t){return function(n,e,u){var c,a=r(n),f=o(a.length),s=i(u,f);if(t&&e!=e){while(f>s)if(c=a[s++],c!=c)return!0}else for(;f>s;s++)if((t||s in a)&&a[s]===e)return t||s||0;return!t&&-1}}},"5ca1":function(t,n,e){var r=e("7726"),o=e("8378"),i=e("32e9"),u=e("2aba"),c=e("9b43"),a="prototype",f=function(t,n,e){var s,l,p,d,v=t&f.F,h=t&f.G,b=t&f.S,g=t&f.P,y=t&f.B,x=h?r:b?r[n]||(r[n]={}):(r[n]||{})[a],m=h?o:o[n]||(o[n]={}),w=m[a]||(m[a]={});for(s in h&&(e=n),e)l=!v&&x&&void 0!==x[s],p=(l?x:e)[s],d=y&&l?c(p,r):g&&"function"==typeof p?c(Function.call,p):p,x&&u(x,s,p,t&f.U),m[s]!=p&&i(m,s,d),g&&w[s]!=p&&(w[s]=p)};r.core=o,f.F=1,f.G=2,f.S=4,f.P=8,f.B=16,f.W=32,f.U=64,f.R=128,t.exports=f},"5d73":function(t,n,e){t.exports=e("469f")},"5f1b":function(t,n,e){"use strict";var r=e("23c6"),o=RegExp.prototype.exec;t.exports=function(t,n){var e=t.exec;if("function"===typeof e){var i=e.call(t,n);if("object"!==typeof i)throw new TypeError("RegExp exec method returned something other than an Object or null");return i}if("RegExp"!==r(t))throw new TypeError("RegExp#exec called on incompatible receiver");return o.call(t,n)}},"626a":function(t,n,e){var r=e("2d95");t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},"62a0":function(t,n){var e=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++e+r).toString(36))}},"63b6":function(t,n,e){var r=e("e53d"),o=e("584a"),i=e("d864"),u=e("35e8"),c=e("07e3"),a="prototype",f=function(t,n,e){var s,l,p,d=t&f.F,v=t&f.G,h=t&f.S,b=t&f.P,g=t&f.B,y=t&f.W,x=v?o:o[n]||(o[n]={}),m=x[a],w=v?r:h?r[n]:(r[n]||{})[a];for(s in v&&(e=n),e)l=!d&&w&&void 0!==w[s],l&&c(x,s)||(p=l?w[s]:e[s],x[s]=v&&"function"!=typeof w[s]?e[s]:g&&l?i(p,r):y&&w[s]==p?function(t){var n=function(n,e,r){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(n);case 2:return new t(n,e)}return new t(n,e,r)}return t.apply(this,arguments)};return n[a]=t[a],n}(p):b&&"function"==typeof p?i(Function.call,p):p,b&&((x.virtual||(x.virtual={}))[s]=p,t&f.R&&m&&!m[s]&&u(m,s,p)))};f.F=1,f.G=2,f.S=4,f.P=8,f.B=16,f.W=32,f.U=64,f.R=128,t.exports=f},6762:function(t,n,e){"use strict";var r=e("5ca1"),o=e("c366")(!0);r(r.P,"Array",{includes:function(t){return o(this,t,arguments.length>1?arguments[1]:void 0)}}),e("9c6c")("includes")},6821:function(t,n,e){var r=e("626a"),o=e("be13");t.exports=function(t){return r(o(t))}},"69a8":function(t,n){var e={}.hasOwnProperty;t.exports=function(t,n){return e.call(t,n)}},"6a99":function(t,n,e){var r=e("d3f4");t.exports=function(t,n){if(!r(t))return t;var e,o;if(n&&"function"==typeof(e=t.toString)&&!r(o=e.call(t)))return o;if("function"==typeof(e=t.valueOf)&&!r(o=e.call(t)))return o;if(!n&&"function"==typeof(e=t.toString)&&!r(o=e.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},"6b4c":function(t,n){var e={}.toString;t.exports=function(t){return e.call(t).slice(8,-1)}},"6c1c":function(t,n,e){e("c367");for(var r=e("e53d"),o=e("35e8"),i=e("481b"),u=e("5168")("toStringTag"),c="CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","),a=0;a=f?t?"":void 0:(i=c.charCodeAt(a),i<55296||i>56319||a+1===f||(u=c.charCodeAt(a+1))<56320||u>57343?t?c.charAt(a):i:t?c.slice(a,a+2):u-56320+(i-55296<<10)+65536)}}},7726:function(t,n){var e=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=e)},"774e":function(t,n,e){t.exports=e("d2d5")},"77f1":function(t,n,e){var r=e("4588"),o=Math.max,i=Math.min;t.exports=function(t,n){return t=r(t),t<0?o(t+n,0):i(t,n)}},"794b":function(t,n,e){t.exports=!e("8e60")&&!e("294c")(function(){return 7!=Object.defineProperty(e("1ec9")("div"),"a",{get:function(){return 7}}).a})},"79aa":function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},"79e5":function(t,n){t.exports=function(t){try{return!!t()}catch(n){return!0}}},"7cd6":function(t,n,e){var r=e("40c3"),o=e("5168")("iterator"),i=e("481b");t.exports=e("584a").getIteratorMethod=function(t){if(void 0!=t)return t[o]||t["@@iterator"]||i[r(t)]}},"7d7b":function(t,n,e){var r=e("e4ae"),o=e("7cd6");t.exports=e("584a").getIterator=function(t){var n=o(t);if("function"!=typeof n)throw TypeError(t+" is not iterable!");return r(n.call(t))}},"7e90":function(t,n,e){var r=e("d9f6"),o=e("e4ae"),i=e("c3a1");t.exports=e("8e60")?Object.defineProperties:function(t,n){o(t);var e,u=i(n),c=u.length,a=0;while(c>a)r.f(t,e=u[a++],n[e]);return t}},8378:function(t,n){var e=t.exports={version:"2.6.5"};"number"==typeof __e&&(__e=e)},8436:function(t,n){t.exports=function(){}},"86cc":function(t,n,e){var r=e("cb7c"),o=e("c69a"),i=e("6a99"),u=Object.defineProperty;n.f=e("9e1e")?Object.defineProperty:function(t,n,e){if(r(t),n=i(n,!0),r(e),o)try{return u(t,n,e)}catch(c){}if("get"in e||"set"in e)throw TypeError("Accessors not supported!");return"value"in e&&(t[n]=e.value),t}},"8aae":function(t,n,e){e("32a6"),t.exports=e("584a").Object.keys},"8e60":function(t,n,e){t.exports=!e("294c")(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},"8f60":function(t,n,e){"use strict";var r=e("a159"),o=e("aebd"),i=e("45f2"),u={};e("35e8")(u,e("5168")("iterator"),function(){return this}),t.exports=function(t,n,e){t.prototype=r(u,{next:o(1,e)}),i(t,n+" Iterator")}},9003:function(t,n,e){var r=e("6b4c");t.exports=Array.isArray||function(t){return"Array"==r(t)}},9138:function(t,n,e){t.exports=e("35e8")},9306:function(t,n,e){"use strict";var r=e("c3a1"),o=e("9aa9"),i=e("355d"),u=e("241e"),c=e("335c"),a=Object.assign;t.exports=!a||e("294c")(function(){var t={},n={},e=Symbol(),r="abcdefghijklmnopqrst";return t[e]=7,r.split("").forEach(function(t){n[t]=t}),7!=a({},t)[e]||Object.keys(a({},n)).join("")!=r})?function(t,n){var e=u(t),a=arguments.length,f=1,s=o.f,l=i.f;while(a>f){var p,d=c(arguments[f++]),v=s?r(d).concat(s(d)):r(d),h=v.length,b=0;while(h>b)l.call(d,p=v[b++])&&(e[p]=d[p])}return e}:a},9427:function(t,n,e){var r=e("63b6");r(r.S,"Object",{create:e("a159")})},"95d5":function(t,n,e){var r=e("40c3"),o=e("5168")("iterator"),i=e("481b");t.exports=e("584a").isIterable=function(t){var n=Object(t);return void 0!==n[o]||"@@iterator"in n||i.hasOwnProperty(r(n))}},"9aa9":function(t,n){n.f=Object.getOwnPropertySymbols},"9b43":function(t,n,e){var r=e("d8e8");t.exports=function(t,n,e){if(r(t),void 0===n)return t;switch(e){case 1:return function(e){return t.call(n,e)};case 2:return function(e,r){return t.call(n,e,r)};case 3:return function(e,r,o){return t.call(n,e,r,o)}}return function(){return t.apply(n,arguments)}}},"9c6c":function(t,n,e){var r=e("2b4c")("unscopables"),o=Array.prototype;void 0==o[r]&&e("32e9")(o,r,{}),t.exports=function(t){o[r][t]=!0}},"9def":function(t,n,e){var r=e("4588"),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},"9e1e":function(t,n,e){t.exports=!e("79e5")(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},a159:function(t,n,e){var r=e("e4ae"),o=e("7e90"),i=e("1691"),u=e("5559")("IE_PROTO"),c=function(){},a="prototype",f=function(){var t,n=e("1ec9")("iframe"),r=i.length,o="<",u=">";n.style.display="none",e("32fc").appendChild(n),n.src="javascript:",t=n.contentWindow.document,t.open(),t.write(o+"script"+u+"document.F=Object"+o+"/script"+u),t.close(),f=t.F;while(r--)delete f[a][i[r]];return f()};t.exports=Object.create||function(t,n){var e;return null!==t?(c[a]=r(t),e=new c,c[a]=null,e[u]=t):e=f(),void 0===n?e:o(e,n)}},a352:function(n,e){n.exports=t},a3c3:function(t,n,e){var r=e("63b6");r(r.S+r.F,"Object",{assign:e("9306")})},a481:function(t,n,e){"use strict";var r=e("cb7c"),o=e("4bf8"),i=e("9def"),u=e("4588"),c=e("0390"),a=e("5f1b"),f=Math.max,s=Math.min,l=Math.floor,p=/\$([$&`']|\d\d?|<[^>]*>)/g,d=/\$([$&`']|\d\d?)/g,v=function(t){return void 0===t?t:String(t)};e("214f")("replace",2,function(t,n,e,h){return[function(r,o){var i=t(this),u=void 0==r?void 0:r[n];return void 0!==u?u.call(r,i,o):e.call(String(i),r,o)},function(t,n){var o=h(e,t,this,n);if(o.done)return o.value;var l=r(t),p=String(this),d="function"===typeof n;d||(n=String(n));var g=l.global;if(g){var y=l.unicode;l.lastIndex=0}var x=[];while(1){var m=a(l,p);if(null===m)break;if(x.push(m),!g)break;var w=String(m[0]);""===w&&(l.lastIndex=c(p,i(l.lastIndex),y))}for(var O="",S=0,j=0;j=S&&(O+=p.slice(S,M)+P,S=M+_.length)}return O+p.slice(S)}];function b(t,n,r,i,u,c){var a=r+t.length,f=i.length,s=d;return void 0!==u&&(u=o(u),s=p),e.call(c,s,function(e,o){var c;switch(o.charAt(0)){case"$":return"$";case"&":return t;case"`":return n.slice(0,r);case"'":return n.slice(a);case"<":c=u[o.slice(1,-1)];break;default:var s=+o;if(0===s)return e;if(s>f){var p=l(s/10);return 0===p?e:p<=f?void 0===i[p-1]?o.charAt(1):i[p-1]+o.charAt(1):e}c=i[s-1]}return void 0===c?"":c})}})},a4bb:function(t,n,e){t.exports=e("8aae")},a745:function(t,n,e){t.exports=e("f410")},aae3:function(t,n,e){var r=e("d3f4"),o=e("2d95"),i=e("2b4c")("match");t.exports=function(t){var n;return r(t)&&(void 0!==(n=t[i])?!!n:"RegExp"==o(t))}},aebd:function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},b0c5:function(t,n,e){"use strict";var r=e("520a");e("5ca1")({target:"RegExp",proto:!0,forced:r!==/./.exec},{exec:r})},b0dc:function(t,n,e){var r=e("e4ae");t.exports=function(t,n,e,o){try{return o?n(r(e)[0],e[1]):n(e)}catch(u){var i=t["return"];throw void 0!==i&&r(i.call(t)),u}}},b447:function(t,n,e){var r=e("3a38"),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},b8e3:function(t,n){t.exports=!0},be13:function(t,n){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},c366:function(t,n,e){var r=e("6821"),o=e("9def"),i=e("77f1");t.exports=function(t){return function(n,e,u){var c,a=r(n),f=o(a.length),s=i(u,f);if(t&&e!=e){while(f>s)if(c=a[s++],c!=c)return!0}else for(;f>s;s++)if((t||s in a)&&a[s]===e)return t||s||0;return!t&&-1}}},c367:function(t,n,e){"use strict";var r=e("8436"),o=e("50ed"),i=e("481b"),u=e("36c3");t.exports=e("30f1")(Array,"Array",function(t,n){this._t=u(t),this._i=0,this._k=n},function(){var t=this._t,n=this._k,e=this._i++;return!t||e>=t.length?(this._t=void 0,o(1)):o(0,"keys"==n?e:"values"==n?t[e]:[e,t[e]])},"values"),i.Arguments=i.Array,r("keys"),r("values"),r("entries")},c3a1:function(t,n,e){var r=e("e6f3"),o=e("1691");t.exports=Object.keys||function(t){return r(t,o)}},c649:function(t,n,e){"use strict";(function(t){e.d(n,"c",function(){return l}),e.d(n,"a",function(){return f}),e.d(n,"b",function(){return u}),e.d(n,"d",function(){return s});e("a481");var r=e("4aa6"),o=e.n(r);function i(){return"undefined"!==typeof window?window.console:t.console}var u=i();function c(t){var n=o()(null);return function(e){var r=n[e];return r||(n[e]=t(e))}}var a=/-(\w)/g,f=c(function(t){return t.replace(a,function(t,n){return n?n.toUpperCase():""})});function s(t){null!==t.parentElement&&t.parentElement.removeChild(t)}function l(t,n,e){var r=0===e?t.children[0]:t.children[e-1].nextSibling;t.insertBefore(n,r)}}).call(this,e("c8ba"))},c69a:function(t,n,e){t.exports=!e("9e1e")&&!e("79e5")(function(){return 7!=Object.defineProperty(e("230e")("div"),"a",{get:function(){return 7}}).a})},c8ba:function(t,n){var e;e=function(){return this}();try{e=e||new Function("return this")()}catch(r){"object"===typeof window&&(e=window)}t.exports=e},c8bb:function(t,n,e){t.exports=e("54a1")},ca5a:function(t,n){var e=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++e+r).toString(36))}},cb7c:function(t,n,e){var r=e("d3f4");t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},ce7e:function(t,n,e){var r=e("63b6"),o=e("584a"),i=e("294c");t.exports=function(t,n){var e=(o.Object||{})[t]||Object[t],u={};u[t]=n(e),r(r.S+r.F*i(function(){e(1)}),"Object",u)}},d2c8:function(t,n,e){var r=e("aae3"),o=e("be13");t.exports=function(t,n,e){if(r(n))throw TypeError("String#"+e+" doesn't accept regex!");return String(o(t))}},d2d5:function(t,n,e){e("1654"),e("549b"),t.exports=e("584a").Array.from},d3f4:function(t,n){t.exports=function(t){return"object"===typeof t?null!==t:"function"===typeof t}},d864:function(t,n,e){var r=e("79aa");t.exports=function(t,n,e){if(r(t),void 0===n)return t;switch(e){case 1:return function(e){return t.call(n,e)};case 2:return function(e,r){return t.call(n,e,r)};case 3:return function(e,r,o){return t.call(n,e,r,o)}}return function(){return t.apply(n,arguments)}}},d8e8:function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},d9f6:function(t,n,e){var r=e("e4ae"),o=e("794b"),i=e("1bc3"),u=Object.defineProperty;n.f=e("8e60")?Object.defineProperty:function(t,n,e){if(r(t),n=i(n,!0),r(e),o)try{return u(t,n,e)}catch(c){}if("get"in e||"set"in e)throw TypeError("Accessors not supported!");return"value"in e&&(t[n]=e.value),t}},dbdb:function(t,n,e){var r=e("584a"),o=e("e53d"),i="__core-js_shared__",u=o[i]||(o[i]={});(t.exports=function(t,n){return u[t]||(u[t]=void 0!==n?n:{})})("versions",[]).push({version:r.version,mode:e("b8e3")?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},dc62:function(t,n,e){e("9427");var r=e("584a").Object;t.exports=function(t,n){return r.create(t,n)}},e4ae:function(t,n,e){var r=e("f772");t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},e53d:function(t,n){var e=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=e)},e6f3:function(t,n,e){var r=e("07e3"),o=e("36c3"),i=e("5b4e")(!1),u=e("5559")("IE_PROTO");t.exports=function(t,n){var e,c=o(t),a=0,f=[];for(e in c)e!=u&&r(c,e)&&f.push(e);while(n.length>a)r(c,e=n[a++])&&(~i(f,e)||f.push(e));return f}},f410:function(t,n,e){e("1af6"),t.exports=e("584a").Array.isArray},f559:function(t,n,e){"use strict";var r=e("5ca1"),o=e("9def"),i=e("d2c8"),u="startsWith",c=""[u];r(r.P+r.F*e("5147")(u),"String",{startsWith:function(t){var n=i(this,t,u),e=o(Math.min(arguments.length>1?arguments[1]:void 0,n.length)),r=String(t);return c?c.call(n,r,e):n.slice(e,e+r.length)===r}})},f772:function(t,n){t.exports=function(t){return"object"===typeof t?null!==t:"function"===typeof t}},fa5b:function(t,n,e){t.exports=e("5537")("native-function-to-string",Function.toString)},fb15:function(t,n,e){"use strict";var r;(e.r(n),"undefined"!==typeof window)&&((r=window.document.currentScript)&&(r=r.src.match(/(.+\/)[^\/]+\.js(\?.*)?$/))&&(e.p=r[1]));var o=e("5176"),i=e.n(o),u=(e("f559"),e("a4bb")),c=e.n(u),a=(e("6762"),e("2fdb"),e("a745")),f=e.n(a);function s(t){if(f()(t))return t}var l=e("5d73"),p=e.n(l);function d(t,n){var e=[],r=!0,o=!1,i=void 0;try{for(var u,c=p()(t);!(r=(u=c.next()).done);r=!0)if(e.push(u.value),n&&e.length===n)break}catch(a){o=!0,i=a}finally{try{r||null==c["return"]||c["return"]()}finally{if(o)throw i}}return e}function v(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}function h(t,n){return s(t)||d(t,n)||v()}function b(t){if(f()(t)){for(var n=0,e=new Array(t.length);n=i?o.length:o.indexOf(t)});return e?u.filter(function(t){return-1!==t}):u}function A(t,n){var e=this;this.$nextTick(function(){return e.$emit(t.toLowerCase(),n)})}function P(t){var n=this;return function(e){null!==n.realList&&n["onDrag"+t](e),A.call(n,t,e)}}function I(t){if(!t||1!==t.length)return!1;var n=h(t,1),e=n[0].componentOptions;return!!e&&["transition-group","TransitionGroup"].includes(e.tag)}function L(t,n){var e=n.header,r=n.footer,o=0,i=0;return e&&(o=e.length,t=t?[].concat(S(e),S(t)):S(e)),r&&(i=r.length,t=t?[].concat(S(t),S(r)):S(r)),{children:t,headerOffset:o,footerOffset:i}}function F(t,n){var e=null,r=function(t,n){e=T(e,t,n)},o=c()(t).filter(function(t){return"id"===t||t.startsWith("data-")}).reduce(function(n,e){return n[e]=t[e],n},{});if(r("attrs",o),!n)return e;var u=n.on,a=n.props,f=n.attrs;return r("on",u),r("props",a),i()(e.attrs,f),e}var $=["Start","Add","Remove","Update","End"],k=["Choose","Sort","Filter","Clone"],D=["Move"].concat($,k).map(function(t){return"on"+t}),R=null,V={options:Object,list:{type:Array,required:!1,default:null},value:{type:Array,required:!1,default:null},noTransitionOnDrag:{type:Boolean,default:!1},clone:{type:Function,default:function(t){return t}},element:{type:String,default:"div"},tag:{type:String,default:null},move:{type:Function,default:null},componentData:{type:Object,required:!1,default:null}},N={name:"draggable",inheritAttrs:!1,props:V,data:function(){return{transitionMode:!1,noneFunctionalComponentMode:!1,init:!1}},render:function(t){var n=this.$slots.default;this.transitionMode=I(n);var e=L(n,this.$slots),r=e.children,o=e.headerOffset,i=e.footerOffset;this.headerOffset=o,this.footerOffset=i;var u=F(this.$attrs,this.componentData);return t(this.getTag(),u,r)},created:function(){null!==this.list&&null!==this.value&&M["b"].error("Value and list props are mutually exclusive! Please set one or another."),"div"!==this.element&&M["b"].warn("Element props is deprecated please use tag props instead. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#element-props"),void 0!==this.options&&M["b"].warn("Options props is deprecated, add sortable options directly as vue.draggable item, or use v-bind. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#options-props")},mounted:function(){var t=this;if(this.noneFunctionalComponentMode=this.getTag().toLowerCase()!==this.$el.nodeName.toLowerCase(),this.noneFunctionalComponentMode&&this.transitionMode)throw new Error("Transition-group inside component is not supported. Please alter tag value or remove transition-group. Current tag value: ".concat(this.getTag()));var n={};$.forEach(function(e){n["on"+e]=P.call(t,e)}),k.forEach(function(e){n["on"+e]=A.bind(t,e)});var e=c()(this.$attrs).reduce(function(n,e){return n[Object(M["a"])(e)]=t.$attrs[e],n},{}),r=i()({},this.options,e,n,{onMove:function(n,e){return t.onDragMove(n,e)}});!("draggable"in r)&&(r.draggable=">*"),this._sortable=new _.a(this.rootContainer,r),this.computeIndexes()},beforeDestroy:function(){void 0!==this._sortable&&this._sortable.destroy()},computed:{rootContainer:function(){return this.transitionMode?this.$el.children[0]:this.$el},realList:function(){return this.list?this.list:this.value}},watch:{options:{handler:function(t){this.updateOptions(t)},deep:!0},$attrs:{handler:function(t){this.updateOptions(t)},deep:!0},realList:function(){this.computeIndexes()}},methods:{getTag:function(){return this.tag||this.element},updateOptions:function(t){for(var n in t){var e=Object(M["a"])(n);-1===D.indexOf(e)&&this._sortable.option(e,t[n])}},getChildrenNodes:function(){if(this.init||(this.noneFunctionalComponentMode=this.noneFunctionalComponentMode&&1===this.$children.length,this.init=!0),this.noneFunctionalComponentMode)return this.$children[0].$slots.default;var t=this.$slots.default;return this.transitionMode?t[0].child.$slots.default:t},computeIndexes:function(){var t=this;this.$nextTick(function(){t.visibleIndexes=E(t.getChildrenNodes(),t.rootContainer.children,t.transitionMode,t.footerOffset)})},getUnderlyingVm:function(t){var n=C(this.getChildrenNodes()||[],t);if(-1===n)return null;var e=this.realList[n];return{index:n,element:e}},getUnderlyingPotencialDraggableComponent:function(t){var n=t.__vue__;return n&&n.$options&&"transition-group"===n.$options._componentTag?n.$parent:n},emitChanges:function(t){var n=this;this.$nextTick(function(){n.$emit("change",t)})},alterList:function(t){if(this.list)t(this.list);else{var n=S(this.value);t(n),this.$emit("input",n)}},spliceList:function(){var t=arguments,n=function(n){return n.splice.apply(n,S(t))};this.alterList(n)},updatePosition:function(t,n){var e=function(e){return e.splice(n,0,e.splice(t,1)[0])};this.alterList(e)},getRelatedContextFromMoveEvent:function(t){var n=t.to,e=t.related,r=this.getUnderlyingPotencialDraggableComponent(n);if(!r)return{component:r};var o=r.realList,u={list:o,component:r};if(n!==e&&o&&r.getUnderlyingVm){var c=r.getUnderlyingVm(e);if(c)return i()(c,u)}return u},getVmIndex:function(t){var n=this.visibleIndexes,e=n.length;return t>e-1?e:n[t]},getComponent:function(){return this.$slots.default[0].componentInstance},resetTransitionData:function(t){if(this.noTransitionOnDrag&&this.transitionMode){var n=this.getChildrenNodes();n[t].data=null;var e=this.getComponent();e.children=[],e.kept=void 0}},onDragStart:function(t){this.context=this.getUnderlyingVm(t.item),t.item._underlying_vm_=this.clone(this.context.element),R=t.item},onDragAdd:function(t){var n=t.item._underlying_vm_;if(void 0!==n){Object(M["d"])(t.item);var e=this.getVmIndex(t.newIndex);this.spliceList(e,0,n),this.computeIndexes();var r={element:n,newIndex:e};this.emitChanges({added:r})}},onDragRemove:function(t){if(Object(M["c"])(this.rootContainer,t.item,t.oldIndex),"clone"!==t.pullMode){var n=this.context.index;this.spliceList(n,1);var e={element:this.context.element,oldIndex:n};this.resetTransitionData(n),this.emitChanges({removed:e})}else Object(M["d"])(t.clone)},onDragUpdate:function(t){Object(M["d"])(t.item),Object(M["c"])(t.from,t.item,t.oldIndex);var n=this.context.index,e=this.getVmIndex(t.newIndex);this.updatePosition(n,e);var r={element:this.context.element,oldIndex:n,newIndex:e};this.emitChanges({moved:r})},updateProperty:function(t,n){t.hasOwnProperty(n)&&(t[n]+=this.headerOffset)},computeFutureIndex:function(t,n){if(!t.element)return 0;var e=S(n.to.children).filter(function(t){return"none"!==t.style["display"]}),r=e.indexOf(n.related),o=t.component.getVmIndex(r),i=-1!==e.indexOf(R);return i||!n.willInsertAfter?o:o+1},onDragMove:function(t,n){var e=this.move;if(!e||!this.realList)return!0;var r=this.getRelatedContextFromMoveEvent(t),o=this.context,u=this.computeFutureIndex(r,t);i()(o,{futureIndex:u});var c=i()({},t,{relatedContext:r,draggedContext:o});return e(c,n)},onDragEnd:function(){this.computeIndexes(),R=null}}};"undefined"!==typeof window&&"Vue"in window&&window.Vue.component("draggable",N);var U=N;n["default"]=U}})["default"]}); +(function(t,n){"object"===typeof exports&&"object"===typeof module?module.exports=n(require("sortablejs")):"function"===typeof define&&define.amd?define(["sortablejs"],n):"object"===typeof exports?exports["vuedraggable"]=n(require("sortablejs")):t["vuedraggable"]=n(t["Sortable"])})("undefined"!==typeof self?self:this,function(t){return function(t){var n={};function e(r){if(n[r])return n[r].exports;var o=n[r]={i:r,l:!1,exports:{}};return t[r].call(o.exports,o,o.exports,e),o.l=!0,o.exports}return e.m=t,e.c=n,e.d=function(t,n,r){e.o(t,n)||Object.defineProperty(t,n,{enumerable:!0,get:r})},e.r=function(t){"undefined"!==typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},e.t=function(t,n){if(1&n&&(t=e(t)),8&n)return t;if(4&n&&"object"===typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(e.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&n&&"string"!=typeof t)for(var o in t)e.d(r,o,function(n){return t[n]}.bind(null,o));return r},e.n=function(t){var n=t&&t.__esModule?function(){return t["default"]}:function(){return t};return e.d(n,"a",n),n},e.o=function(t,n){return Object.prototype.hasOwnProperty.call(t,n)},e.p="",e(e.s="fb15")}({"02f4":function(t,n,e){var r=e("4588"),o=e("be13");t.exports=function(t){return function(n,e){var i,u,c=String(o(n)),a=r(e),f=c.length;return a<0||a>=f?t?"":void 0:(i=c.charCodeAt(a),i<55296||i>56319||a+1===f||(u=c.charCodeAt(a+1))<56320||u>57343?t?c.charAt(a):i:t?c.slice(a,a+2):u-56320+(i-55296<<10)+65536)}}},"0390":function(t,n,e){"use strict";var r=e("02f4")(!0);t.exports=function(t,n,e){return n+(e?r(t,n).length:1)}},"07e3":function(t,n){var e={}.hasOwnProperty;t.exports=function(t,n){return e.call(t,n)}},"0bfb":function(t,n,e){"use strict";var r=e("cb7c");t.exports=function(){var t=r(this),n="";return t.global&&(n+="g"),t.ignoreCase&&(n+="i"),t.multiline&&(n+="m"),t.unicode&&(n+="u"),t.sticky&&(n+="y"),n}},"0fc9":function(t,n,e){var r=e("3a38"),o=Math.max,i=Math.min;t.exports=function(t,n){return t=r(t),t<0?o(t+n,0):i(t,n)}},1654:function(t,n,e){"use strict";var r=e("71c1")(!0);e("30f1")(String,"String",function(t){this._t=String(t),this._i=0},function(){var t,n=this._t,e=this._i;return e>=n.length?{value:void 0,done:!0}:(t=r(n,e),this._i+=t.length,{value:t,done:!1})})},1691:function(t,n){t.exports="constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf".split(",")},"1af6":function(t,n,e){var r=e("63b6");r(r.S,"Array",{isArray:e("9003")})},"1bc3":function(t,n,e){var r=e("f772");t.exports=function(t,n){if(!r(t))return t;var e,o;if(n&&"function"==typeof(e=t.toString)&&!r(o=e.call(t)))return o;if("function"==typeof(e=t.valueOf)&&!r(o=e.call(t)))return o;if(!n&&"function"==typeof(e=t.toString)&&!r(o=e.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},"1ec9":function(t,n,e){var r=e("f772"),o=e("e53d").document,i=r(o)&&r(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},"20fd":function(t,n,e){"use strict";var r=e("d9f6"),o=e("aebd");t.exports=function(t,n,e){n in t?r.f(t,n,o(0,e)):t[n]=e}},"214f":function(t,n,e){"use strict";e("b0c5");var r=e("2aba"),o=e("32e9"),i=e("79e5"),u=e("be13"),c=e("2b4c"),a=e("520a"),f=c("species"),s=!i(function(){var t=/./;return t.exec=function(){var t=[];return t.groups={a:"7"},t},"7"!=="".replace(t,"$")}),l=function(){var t=/(?:)/,n=t.exec;t.exec=function(){return n.apply(this,arguments)};var e="ab".split(t);return 2===e.length&&"a"===e[0]&&"b"===e[1]}();t.exports=function(t,n,e){var p=c(t),d=!i(function(){var n={};return n[p]=function(){return 7},7!=""[t](n)}),v=d?!i(function(){var n=!1,e=/a/;return e.exec=function(){return n=!0,null},"split"===t&&(e.constructor={},e.constructor[f]=function(){return e}),e[p](""),!n}):void 0;if(!d||!v||"replace"===t&&!s||"split"===t&&!l){var h=/./[p],b=e(u,p,""[t],function(t,n,e,r,o){return n.exec===a?d&&!o?{done:!0,value:h.call(n,e,r)}:{done:!0,value:t.call(e,n,r)}:{done:!1}}),g=b[0],y=b[1];r(String.prototype,t,g),o(RegExp.prototype,p,2==n?function(t,n){return y.call(t,this,n)}:function(t){return y.call(t,this)})}}},"230e":function(t,n,e){var r=e("d3f4"),o=e("7726").document,i=r(o)&&r(o.createElement);t.exports=function(t){return i?o.createElement(t):{}}},"23c6":function(t,n,e){var r=e("2d95"),o=e("2b4c")("toStringTag"),i="Arguments"==r(function(){return arguments}()),u=function(t,n){try{return t[n]}catch(e){}};t.exports=function(t){var n,e,c;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(e=u(n=Object(t),o))?e:i?r(n):"Object"==(c=r(n))&&"function"==typeof n.callee?"Arguments":c}},"241e":function(t,n,e){var r=e("25eb");t.exports=function(t){return Object(r(t))}},"25eb":function(t,n){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},"294c":function(t,n){t.exports=function(t){try{return!!t()}catch(n){return!0}}},"2aba":function(t,n,e){var r=e("7726"),o=e("32e9"),i=e("69a8"),u=e("ca5a")("src"),c=e("fa5b"),a="toString",f=(""+c).split(a);e("8378").inspectSource=function(t){return c.call(t)},(t.exports=function(t,n,e,c){var a="function"==typeof e;a&&(i(e,"name")||o(e,"name",n)),t[n]!==e&&(a&&(i(e,u)||o(e,u,t[n]?""+t[n]:f.join(String(n)))),t===r?t[n]=e:c?t[n]?t[n]=e:o(t,n,e):(delete t[n],o(t,n,e)))})(Function.prototype,a,function(){return"function"==typeof this&&this[u]||c.call(this)})},"2b4c":function(t,n,e){var r=e("5537")("wks"),o=e("ca5a"),i=e("7726").Symbol,u="function"==typeof i,c=t.exports=function(t){return r[t]||(r[t]=u&&i[t]||(u?i:o)("Symbol."+t))};c.store=r},"2d00":function(t,n){t.exports=!1},"2d95":function(t,n){var e={}.toString;t.exports=function(t){return e.call(t).slice(8,-1)}},"2fdb":function(t,n,e){"use strict";var r=e("5ca1"),o=e("d2c8"),i="includes";r(r.P+r.F*e("5147")(i),"String",{includes:function(t){return!!~o(this,t,i).indexOf(t,arguments.length>1?arguments[1]:void 0)}})},"30f1":function(t,n,e){"use strict";var r=e("b8e3"),o=e("63b6"),i=e("9138"),u=e("35e8"),c=e("481b"),a=e("8f60"),f=e("45f2"),s=e("53e2"),l=e("5168")("iterator"),p=!([].keys&&"next"in[].keys()),d="@@iterator",v="keys",h="values",b=function(){return this};t.exports=function(t,n,e,g,y,x,m){a(e,n,g);var w,O,S,j=function(t){if(!p&&t in C)return C[t];switch(t){case v:return function(){return new e(this,t)};case h:return function(){return new e(this,t)}}return function(){return new e(this,t)}},_=n+" Iterator",M=y==h,T=!1,C=t.prototype,E=C[l]||C[d]||y&&C[y],A=E||j(y),P=y?M?j("entries"):A:void 0,I="Array"==n&&C.entries||E;if(I&&(S=s(I.call(new t)),S!==Object.prototype&&S.next&&(f(S,_,!0),r||"function"==typeof S[l]||u(S,l,b))),M&&E&&E.name!==h&&(T=!0,A=function(){return E.call(this)}),r&&!m||!p&&!T&&C[l]||u(C,l,A),c[n]=A,c[_]=b,y)if(w={values:M?A:j(h),keys:x?A:j(v),entries:P},m)for(O in w)O in C||i(C,O,w[O]);else o(o.P+o.F*(p||T),n,w);return w}},"32a6":function(t,n,e){var r=e("241e"),o=e("c3a1");e("ce7e")("keys",function(){return function(t){return o(r(t))}})},"32e9":function(t,n,e){var r=e("86cc"),o=e("4630");t.exports=e("9e1e")?function(t,n,e){return r.f(t,n,o(1,e))}:function(t,n,e){return t[n]=e,t}},"32fc":function(t,n,e){var r=e("e53d").document;t.exports=r&&r.documentElement},"335c":function(t,n,e){var r=e("6b4c");t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},"355d":function(t,n){n.f={}.propertyIsEnumerable},"35e8":function(t,n,e){var r=e("d9f6"),o=e("aebd");t.exports=e("8e60")?function(t,n,e){return r.f(t,n,o(1,e))}:function(t,n,e){return t[n]=e,t}},"36c3":function(t,n,e){var r=e("335c"),o=e("25eb");t.exports=function(t){return r(o(t))}},3702:function(t,n,e){var r=e("481b"),o=e("5168")("iterator"),i=Array.prototype;t.exports=function(t){return void 0!==t&&(r.Array===t||i[o]===t)}},"3a38":function(t,n){var e=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:e)(t)}},"40c3":function(t,n,e){var r=e("6b4c"),o=e("5168")("toStringTag"),i="Arguments"==r(function(){return arguments}()),u=function(t,n){try{return t[n]}catch(e){}};t.exports=function(t){var n,e,c;return void 0===t?"Undefined":null===t?"Null":"string"==typeof(e=u(n=Object(t),o))?e:i?r(n):"Object"==(c=r(n))&&"function"==typeof n.callee?"Arguments":c}},4588:function(t,n){var e=Math.ceil,r=Math.floor;t.exports=function(t){return isNaN(t=+t)?0:(t>0?r:e)(t)}},"45f2":function(t,n,e){var r=e("d9f6").f,o=e("07e3"),i=e("5168")("toStringTag");t.exports=function(t,n,e){t&&!o(t=e?t:t.prototype,i)&&r(t,i,{configurable:!0,value:n})}},4630:function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},"469f":function(t,n,e){e("6c1c"),e("1654"),t.exports=e("7d7b")},"481b":function(t,n){t.exports={}},"4aa6":function(t,n,e){t.exports=e("dc62")},"4bf8":function(t,n,e){var r=e("be13");t.exports=function(t){return Object(r(t))}},"4ee1":function(t,n,e){var r=e("5168")("iterator"),o=!1;try{var i=[7][r]();i["return"]=function(){o=!0},Array.from(i,function(){throw 2})}catch(u){}t.exports=function(t,n){if(!n&&!o)return!1;var e=!1;try{var i=[7],c=i[r]();c.next=function(){return{done:e=!0}},i[r]=function(){return c},t(i)}catch(u){}return e}},"50ed":function(t,n){t.exports=function(t,n){return{value:n,done:!!t}}},5147:function(t,n,e){var r=e("2b4c")("match");t.exports=function(t){var n=/./;try{"/./"[t](n)}catch(e){try{return n[r]=!1,!"/./"[t](n)}catch(o){}}return!0}},5168:function(t,n,e){var r=e("dbdb")("wks"),o=e("62a0"),i=e("e53d").Symbol,u="function"==typeof i,c=t.exports=function(t){return r[t]||(r[t]=u&&i[t]||(u?i:o)("Symbol."+t))};c.store=r},5176:function(t,n,e){t.exports=e("51b6")},"51b6":function(t,n,e){e("a3c3"),t.exports=e("584a").Object.assign},"520a":function(t,n,e){"use strict";var r=e("0bfb"),o=RegExp.prototype.exec,i=String.prototype.replace,u=o,c="lastIndex",a=function(){var t=/a/,n=/b*/g;return o.call(t,"a"),o.call(n,"a"),0!==t[c]||0!==n[c]}(),f=void 0!==/()??/.exec("")[1],s=a||f;s&&(u=function(t){var n,e,u,s,l=this;return f&&(e=new RegExp("^"+l.source+"$(?!\\s)",r.call(l))),a&&(n=l[c]),u=o.call(l,t),a&&u&&(l[c]=l.global?u.index+u[0].length:n),f&&u&&u.length>1&&i.call(u[0],e,function(){for(s=1;s1?arguments[1]:void 0,b=void 0!==h,g=0,y=s(p);if(b&&(h=r(h,v>2?arguments[2]:void 0,2)),void 0==y||d==Array&&c(y))for(n=a(p.length),e=new d(n);n>g;g++)f(e,g,b?h(p[g],g):p[g]);else for(l=y.call(p),e=new d;!(o=l.next()).done;g++)f(e,g,b?u(l,h,[o.value,g],!0):o.value);return e.length=g,e}})},"54a1":function(t,n,e){e("6c1c"),e("1654"),t.exports=e("95d5")},5537:function(t,n,e){var r=e("8378"),o=e("7726"),i="__core-js_shared__",u=o[i]||(o[i]={});(t.exports=function(t,n){return u[t]||(u[t]=void 0!==n?n:{})})("versions",[]).push({version:r.version,mode:e("2d00")?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},5559:function(t,n,e){var r=e("dbdb")("keys"),o=e("62a0");t.exports=function(t){return r[t]||(r[t]=o(t))}},"584a":function(t,n){var e=t.exports={version:"2.6.5"};"number"==typeof __e&&(__e=e)},"5b4e":function(t,n,e){var r=e("36c3"),o=e("b447"),i=e("0fc9");t.exports=function(t){return function(n,e,u){var c,a=r(n),f=o(a.length),s=i(u,f);if(t&&e!=e){while(f>s)if(c=a[s++],c!=c)return!0}else for(;f>s;s++)if((t||s in a)&&a[s]===e)return t||s||0;return!t&&-1}}},"5ca1":function(t,n,e){var r=e("7726"),o=e("8378"),i=e("32e9"),u=e("2aba"),c=e("9b43"),a="prototype",f=function(t,n,e){var s,l,p,d,v=t&f.F,h=t&f.G,b=t&f.S,g=t&f.P,y=t&f.B,x=h?r:b?r[n]||(r[n]={}):(r[n]||{})[a],m=h?o:o[n]||(o[n]={}),w=m[a]||(m[a]={});for(s in h&&(e=n),e)l=!v&&x&&void 0!==x[s],p=(l?x:e)[s],d=y&&l?c(p,r):g&&"function"==typeof p?c(Function.call,p):p,x&&u(x,s,p,t&f.U),m[s]!=p&&i(m,s,d),g&&w[s]!=p&&(w[s]=p)};r.core=o,f.F=1,f.G=2,f.S=4,f.P=8,f.B=16,f.W=32,f.U=64,f.R=128,t.exports=f},"5d73":function(t,n,e){t.exports=e("469f")},"5f1b":function(t,n,e){"use strict";var r=e("23c6"),o=RegExp.prototype.exec;t.exports=function(t,n){var e=t.exec;if("function"===typeof e){var i=e.call(t,n);if("object"!==typeof i)throw new TypeError("RegExp exec method returned something other than an Object or null");return i}if("RegExp"!==r(t))throw new TypeError("RegExp#exec called on incompatible receiver");return o.call(t,n)}},"626a":function(t,n,e){var r=e("2d95");t.exports=Object("z").propertyIsEnumerable(0)?Object:function(t){return"String"==r(t)?t.split(""):Object(t)}},"62a0":function(t,n){var e=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++e+r).toString(36))}},"63b6":function(t,n,e){var r=e("e53d"),o=e("584a"),i=e("d864"),u=e("35e8"),c=e("07e3"),a="prototype",f=function(t,n,e){var s,l,p,d=t&f.F,v=t&f.G,h=t&f.S,b=t&f.P,g=t&f.B,y=t&f.W,x=v?o:o[n]||(o[n]={}),m=x[a],w=v?r:h?r[n]:(r[n]||{})[a];for(s in v&&(e=n),e)l=!d&&w&&void 0!==w[s],l&&c(x,s)||(p=l?w[s]:e[s],x[s]=v&&"function"!=typeof w[s]?e[s]:g&&l?i(p,r):y&&w[s]==p?function(t){var n=function(n,e,r){if(this instanceof t){switch(arguments.length){case 0:return new t;case 1:return new t(n);case 2:return new t(n,e)}return new t(n,e,r)}return t.apply(this,arguments)};return n[a]=t[a],n}(p):b&&"function"==typeof p?i(Function.call,p):p,b&&((x.virtual||(x.virtual={}))[s]=p,t&f.R&&m&&!m[s]&&u(m,s,p)))};f.F=1,f.G=2,f.S=4,f.P=8,f.B=16,f.W=32,f.U=64,f.R=128,t.exports=f},6762:function(t,n,e){"use strict";var r=e("5ca1"),o=e("c366")(!0);r(r.P,"Array",{includes:function(t){return o(this,t,arguments.length>1?arguments[1]:void 0)}}),e("9c6c")("includes")},6821:function(t,n,e){var r=e("626a"),o=e("be13");t.exports=function(t){return r(o(t))}},"69a8":function(t,n){var e={}.hasOwnProperty;t.exports=function(t,n){return e.call(t,n)}},"6a99":function(t,n,e){var r=e("d3f4");t.exports=function(t,n){if(!r(t))return t;var e,o;if(n&&"function"==typeof(e=t.toString)&&!r(o=e.call(t)))return o;if("function"==typeof(e=t.valueOf)&&!r(o=e.call(t)))return o;if(!n&&"function"==typeof(e=t.toString)&&!r(o=e.call(t)))return o;throw TypeError("Can't convert object to primitive value")}},"6b4c":function(t,n){var e={}.toString;t.exports=function(t){return e.call(t).slice(8,-1)}},"6c1c":function(t,n,e){e("c367");for(var r=e("e53d"),o=e("35e8"),i=e("481b"),u=e("5168")("toStringTag"),c="CSSRuleList,CSSStyleDeclaration,CSSValueList,ClientRectList,DOMRectList,DOMStringList,DOMTokenList,DataTransferItemList,FileList,HTMLAllCollection,HTMLCollection,HTMLFormElement,HTMLSelectElement,MediaList,MimeTypeArray,NamedNodeMap,NodeList,PaintRequestList,Plugin,PluginArray,SVGLengthList,SVGNumberList,SVGPathSegList,SVGPointList,SVGStringList,SVGTransformList,SourceBufferList,StyleSheetList,TextTrackCueList,TextTrackList,TouchList".split(","),a=0;a=f?t?"":void 0:(i=c.charCodeAt(a),i<55296||i>56319||a+1===f||(u=c.charCodeAt(a+1))<56320||u>57343?t?c.charAt(a):i:t?c.slice(a,a+2):u-56320+(i-55296<<10)+65536)}}},7726:function(t,n){var e=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=e)},"774e":function(t,n,e){t.exports=e("d2d5")},"77f1":function(t,n,e){var r=e("4588"),o=Math.max,i=Math.min;t.exports=function(t,n){return t=r(t),t<0?o(t+n,0):i(t,n)}},"794b":function(t,n,e){t.exports=!e("8e60")&&!e("294c")(function(){return 7!=Object.defineProperty(e("1ec9")("div"),"a",{get:function(){return 7}}).a})},"79aa":function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},"79e5":function(t,n){t.exports=function(t){try{return!!t()}catch(n){return!0}}},"7cd6":function(t,n,e){var r=e("40c3"),o=e("5168")("iterator"),i=e("481b");t.exports=e("584a").getIteratorMethod=function(t){if(void 0!=t)return t[o]||t["@@iterator"]||i[r(t)]}},"7d7b":function(t,n,e){var r=e("e4ae"),o=e("7cd6");t.exports=e("584a").getIterator=function(t){var n=o(t);if("function"!=typeof n)throw TypeError(t+" is not iterable!");return r(n.call(t))}},"7e90":function(t,n,e){var r=e("d9f6"),o=e("e4ae"),i=e("c3a1");t.exports=e("8e60")?Object.defineProperties:function(t,n){o(t);var e,u=i(n),c=u.length,a=0;while(c>a)r.f(t,e=u[a++],n[e]);return t}},8378:function(t,n){var e=t.exports={version:"2.6.5"};"number"==typeof __e&&(__e=e)},8436:function(t,n){t.exports=function(){}},"86cc":function(t,n,e){var r=e("cb7c"),o=e("c69a"),i=e("6a99"),u=Object.defineProperty;n.f=e("9e1e")?Object.defineProperty:function(t,n,e){if(r(t),n=i(n,!0),r(e),o)try{return u(t,n,e)}catch(c){}if("get"in e||"set"in e)throw TypeError("Accessors not supported!");return"value"in e&&(t[n]=e.value),t}},"8aae":function(t,n,e){e("32a6"),t.exports=e("584a").Object.keys},"8e60":function(t,n,e){t.exports=!e("294c")(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},"8f60":function(t,n,e){"use strict";var r=e("a159"),o=e("aebd"),i=e("45f2"),u={};e("35e8")(u,e("5168")("iterator"),function(){return this}),t.exports=function(t,n,e){t.prototype=r(u,{next:o(1,e)}),i(t,n+" Iterator")}},9003:function(t,n,e){var r=e("6b4c");t.exports=Array.isArray||function(t){return"Array"==r(t)}},9138:function(t,n,e){t.exports=e("35e8")},9306:function(t,n,e){"use strict";var r=e("c3a1"),o=e("9aa9"),i=e("355d"),u=e("241e"),c=e("335c"),a=Object.assign;t.exports=!a||e("294c")(function(){var t={},n={},e=Symbol(),r="abcdefghijklmnopqrst";return t[e]=7,r.split("").forEach(function(t){n[t]=t}),7!=a({},t)[e]||Object.keys(a({},n)).join("")!=r})?function(t,n){var e=u(t),a=arguments.length,f=1,s=o.f,l=i.f;while(a>f){var p,d=c(arguments[f++]),v=s?r(d).concat(s(d)):r(d),h=v.length,b=0;while(h>b)l.call(d,p=v[b++])&&(e[p]=d[p])}return e}:a},9427:function(t,n,e){var r=e("63b6");r(r.S,"Object",{create:e("a159")})},"95d5":function(t,n,e){var r=e("40c3"),o=e("5168")("iterator"),i=e("481b");t.exports=e("584a").isIterable=function(t){var n=Object(t);return void 0!==n[o]||"@@iterator"in n||i.hasOwnProperty(r(n))}},"9aa9":function(t,n){n.f=Object.getOwnPropertySymbols},"9b43":function(t,n,e){var r=e("d8e8");t.exports=function(t,n,e){if(r(t),void 0===n)return t;switch(e){case 1:return function(e){return t.call(n,e)};case 2:return function(e,r){return t.call(n,e,r)};case 3:return function(e,r,o){return t.call(n,e,r,o)}}return function(){return t.apply(n,arguments)}}},"9c6c":function(t,n,e){var r=e("2b4c")("unscopables"),o=Array.prototype;void 0==o[r]&&e("32e9")(o,r,{}),t.exports=function(t){o[r][t]=!0}},"9def":function(t,n,e){var r=e("4588"),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},"9e1e":function(t,n,e){t.exports=!e("79e5")(function(){return 7!=Object.defineProperty({},"a",{get:function(){return 7}}).a})},a159:function(t,n,e){var r=e("e4ae"),o=e("7e90"),i=e("1691"),u=e("5559")("IE_PROTO"),c=function(){},a="prototype",f=function(){var t,n=e("1ec9")("iframe"),r=i.length,o="<",u=">";n.style.display="none",e("32fc").appendChild(n),n.src="javascript:",t=n.contentWindow.document,t.open(),t.write(o+"script"+u+"document.F=Object"+o+"/script"+u),t.close(),f=t.F;while(r--)delete f[a][i[r]];return f()};t.exports=Object.create||function(t,n){var e;return null!==t?(c[a]=r(t),e=new c,c[a]=null,e[u]=t):e=f(),void 0===n?e:o(e,n)}},a352:function(n,e){n.exports=t},a3c3:function(t,n,e){var r=e("63b6");r(r.S+r.F,"Object",{assign:e("9306")})},a481:function(t,n,e){"use strict";var r=e("cb7c"),o=e("4bf8"),i=e("9def"),u=e("4588"),c=e("0390"),a=e("5f1b"),f=Math.max,s=Math.min,l=Math.floor,p=/\$([$&`']|\d\d?|<[^>]*>)/g,d=/\$([$&`']|\d\d?)/g,v=function(t){return void 0===t?t:String(t)};e("214f")("replace",2,function(t,n,e,h){return[function(r,o){var i=t(this),u=void 0==r?void 0:r[n];return void 0!==u?u.call(r,i,o):e.call(String(i),r,o)},function(t,n){var o=h(e,t,this,n);if(o.done)return o.value;var l=r(t),p=String(this),d="function"===typeof n;d||(n=String(n));var g=l.global;if(g){var y=l.unicode;l.lastIndex=0}var x=[];while(1){var m=a(l,p);if(null===m)break;if(x.push(m),!g)break;var w=String(m[0]);""===w&&(l.lastIndex=c(p,i(l.lastIndex),y))}for(var O="",S=0,j=0;j=S&&(O+=p.slice(S,M)+P,S=M+_.length)}return O+p.slice(S)}];function b(t,n,r,i,u,c){var a=r+t.length,f=i.length,s=d;return void 0!==u&&(u=o(u),s=p),e.call(c,s,function(e,o){var c;switch(o.charAt(0)){case"$":return"$";case"&":return t;case"`":return n.slice(0,r);case"'":return n.slice(a);case"<":c=u[o.slice(1,-1)];break;default:var s=+o;if(0===s)return e;if(s>f){var p=l(s/10);return 0===p?e:p<=f?void 0===i[p-1]?o.charAt(1):i[p-1]+o.charAt(1):e}c=i[s-1]}return void 0===c?"":c})}})},a4bb:function(t,n,e){t.exports=e("8aae")},a745:function(t,n,e){t.exports=e("f410")},aae3:function(t,n,e){var r=e("d3f4"),o=e("2d95"),i=e("2b4c")("match");t.exports=function(t){var n;return r(t)&&(void 0!==(n=t[i])?!!n:"RegExp"==o(t))}},aebd:function(t,n){t.exports=function(t,n){return{enumerable:!(1&t),configurable:!(2&t),writable:!(4&t),value:n}}},b0c5:function(t,n,e){"use strict";var r=e("520a");e("5ca1")({target:"RegExp",proto:!0,forced:r!==/./.exec},{exec:r})},b0dc:function(t,n,e){var r=e("e4ae");t.exports=function(t,n,e,o){try{return o?n(r(e)[0],e[1]):n(e)}catch(u){var i=t["return"];throw void 0!==i&&r(i.call(t)),u}}},b447:function(t,n,e){var r=e("3a38"),o=Math.min;t.exports=function(t){return t>0?o(r(t),9007199254740991):0}},b8e3:function(t,n){t.exports=!0},be13:function(t,n){t.exports=function(t){if(void 0==t)throw TypeError("Can't call method on "+t);return t}},c366:function(t,n,e){var r=e("6821"),o=e("9def"),i=e("77f1");t.exports=function(t){return function(n,e,u){var c,a=r(n),f=o(a.length),s=i(u,f);if(t&&e!=e){while(f>s)if(c=a[s++],c!=c)return!0}else for(;f>s;s++)if((t||s in a)&&a[s]===e)return t||s||0;return!t&&-1}}},c367:function(t,n,e){"use strict";var r=e("8436"),o=e("50ed"),i=e("481b"),u=e("36c3");t.exports=e("30f1")(Array,"Array",function(t,n){this._t=u(t),this._i=0,this._k=n},function(){var t=this._t,n=this._k,e=this._i++;return!t||e>=t.length?(this._t=void 0,o(1)):o(0,"keys"==n?e:"values"==n?t[e]:[e,t[e]])},"values"),i.Arguments=i.Array,r("keys"),r("values"),r("entries")},c3a1:function(t,n,e){var r=e("e6f3"),o=e("1691");t.exports=Object.keys||function(t){return r(t,o)}},c649:function(t,n,e){"use strict";(function(t){e.d(n,"c",function(){return l}),e.d(n,"a",function(){return f}),e.d(n,"b",function(){return u}),e.d(n,"d",function(){return s});e("a481");var r=e("4aa6"),o=e.n(r);function i(){return"undefined"!==typeof window?window.console:t.console}var u=i();function c(t){var n=o()(null);return function(e){var r=n[e];return r||(n[e]=t(e))}}var a=/-(\w)/g,f=c(function(t){return t.replace(a,function(t,n){return n?n.toUpperCase():""})});function s(t){null!==t.parentElement&&t.parentElement.removeChild(t)}function l(t,n,e){var r=0===e?t.children[0]:t.children[e-1].nextSibling;t.insertBefore(n,r)}}).call(this,e("c8ba"))},c69a:function(t,n,e){t.exports=!e("9e1e")&&!e("79e5")(function(){return 7!=Object.defineProperty(e("230e")("div"),"a",{get:function(){return 7}}).a})},c8ba:function(t,n){var e;e=function(){return this}();try{e=e||new Function("return this")()}catch(r){"object"===typeof window&&(e=window)}t.exports=e},c8bb:function(t,n,e){t.exports=e("54a1")},ca5a:function(t,n){var e=0,r=Math.random();t.exports=function(t){return"Symbol(".concat(void 0===t?"":t,")_",(++e+r).toString(36))}},cb7c:function(t,n,e){var r=e("d3f4");t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},ce7e:function(t,n,e){var r=e("63b6"),o=e("584a"),i=e("294c");t.exports=function(t,n){var e=(o.Object||{})[t]||Object[t],u={};u[t]=n(e),r(r.S+r.F*i(function(){e(1)}),"Object",u)}},d2c8:function(t,n,e){var r=e("aae3"),o=e("be13");t.exports=function(t,n,e){if(r(n))throw TypeError("String#"+e+" doesn't accept regex!");return String(o(t))}},d2d5:function(t,n,e){e("1654"),e("549b"),t.exports=e("584a").Array.from},d3f4:function(t,n){t.exports=function(t){return"object"===typeof t?null!==t:"function"===typeof t}},d864:function(t,n,e){var r=e("79aa");t.exports=function(t,n,e){if(r(t),void 0===n)return t;switch(e){case 1:return function(e){return t.call(n,e)};case 2:return function(e,r){return t.call(n,e,r)};case 3:return function(e,r,o){return t.call(n,e,r,o)}}return function(){return t.apply(n,arguments)}}},d8e8:function(t,n){t.exports=function(t){if("function"!=typeof t)throw TypeError(t+" is not a function!");return t}},d9f6:function(t,n,e){var r=e("e4ae"),o=e("794b"),i=e("1bc3"),u=Object.defineProperty;n.f=e("8e60")?Object.defineProperty:function(t,n,e){if(r(t),n=i(n,!0),r(e),o)try{return u(t,n,e)}catch(c){}if("get"in e||"set"in e)throw TypeError("Accessors not supported!");return"value"in e&&(t[n]=e.value),t}},dbdb:function(t,n,e){var r=e("584a"),o=e("e53d"),i="__core-js_shared__",u=o[i]||(o[i]={});(t.exports=function(t,n){return u[t]||(u[t]=void 0!==n?n:{})})("versions",[]).push({version:r.version,mode:e("b8e3")?"pure":"global",copyright:"© 2019 Denis Pushkarev (zloirock.ru)"})},dc62:function(t,n,e){e("9427");var r=e("584a").Object;t.exports=function(t,n){return r.create(t,n)}},e4ae:function(t,n,e){var r=e("f772");t.exports=function(t){if(!r(t))throw TypeError(t+" is not an object!");return t}},e53d:function(t,n){var e=t.exports="undefined"!=typeof window&&window.Math==Math?window:"undefined"!=typeof self&&self.Math==Math?self:Function("return this")();"number"==typeof __g&&(__g=e)},e6f3:function(t,n,e){var r=e("07e3"),o=e("36c3"),i=e("5b4e")(!1),u=e("5559")("IE_PROTO");t.exports=function(t,n){var e,c=o(t),a=0,f=[];for(e in c)e!=u&&r(c,e)&&f.push(e);while(n.length>a)r(c,e=n[a++])&&(~i(f,e)||f.push(e));return f}},f410:function(t,n,e){e("1af6"),t.exports=e("584a").Array.isArray},f559:function(t,n,e){"use strict";var r=e("5ca1"),o=e("9def"),i=e("d2c8"),u="startsWith",c=""[u];r(r.P+r.F*e("5147")(u),"String",{startsWith:function(t){var n=i(this,t,u),e=o(Math.min(arguments.length>1?arguments[1]:void 0,n.length)),r=String(t);return c?c.call(n,r,e):n.slice(e,e+r.length)===r}})},f772:function(t,n){t.exports=function(t){return"object"===typeof t?null!==t:"function"===typeof t}},fa5b:function(t,n,e){t.exports=e("5537")("native-function-to-string",Function.toString)},fb15:function(t,n,e){"use strict";var r;(e.r(n),"undefined"!==typeof window)&&((r=window.document.currentScript)&&(r=r.src.match(/(.+\/)[^\/]+\.js(\?.*)?$/))&&(e.p=r[1]));var o=e("5176"),i=e.n(o),u=(e("f559"),e("a4bb")),c=e.n(u),a=(e("6762"),e("2fdb"),e("a745")),f=e.n(a);function s(t){if(f()(t))return t}var l=e("5d73"),p=e.n(l);function d(t,n){var e=[],r=!0,o=!1,i=void 0;try{for(var u,c=p()(t);!(r=(u=c.next()).done);r=!0)if(e.push(u.value),n&&e.length===n)break}catch(a){o=!0,i=a}finally{try{r||null==c["return"]||c["return"]()}finally{if(o)throw i}}return e}function v(){throw new TypeError("Invalid attempt to destructure non-iterable instance")}function h(t,n){return s(t)||d(t,n)||v()}function b(t){if(f()(t)){for(var n=0,e=new Array(t.length);n=i?o.length:o.indexOf(t)});return e?u.filter(function(t){return-1!==t}):u}function A(t,n){var e=this;this.$nextTick(function(){return e.$emit(t.toLowerCase(),n)})}function P(t){var n=this;return function(e){null!==n.realList&&n["onDrag"+t](e),A.call(n,t,e)}}function I(t){if(!t||1!==t.length)return!1;var n=h(t,1),e=n[0].componentOptions;return!!e&&["transition-group","TransitionGroup"].includes(e.tag)}function L(t,n){var e=n.header,r=n.footer,o=0,i=0;return e&&(o=e.length,t=t?[].concat(S(e),S(t)):S(e)),r&&(i=r.length,t=t?[].concat(S(t),S(r)):S(r)),{children:t,headerOffset:o,footerOffset:i}}function F(t,n){var e=null,r=function(t,n){e=T(e,t,n)},o=c()(t).filter(function(t){return"id"===t||t.startsWith("data-")}).reduce(function(n,e){return n[e]=t[e],n},{});if(r("attrs",o),!n)return e;var u=n.on,a=n.props,f=n.attrs;return r("on",u),r("props",a),i()(e.attrs,f),e}var $=["Start","Add","Remove","Update","End"],k=["Choose","Sort","Filter","Clone"],D=["Move"].concat($,k).map(function(t){return"on"+t}),R=null,V={options:Object,list:{type:Array,required:!1,default:null},value:{type:Array,required:!1,default:null},noTransitionOnDrag:{type:Boolean,default:!1},clone:{type:Function,default:function(t){return t}},element:{type:String,default:"div"},tag:{type:String,default:null},move:{type:Function,default:null},componentData:{type:Object,required:!1,default:null}},N={name:"draggable",inheritAttrs:!1,props:V,data:function(){return{transitionMode:!1,noneFunctionalComponentMode:!1,init:!1}},render:function(t){var n=this.$slots.default;this.transitionMode=I(n);var e=L(n,this.$slots),r=e.children,o=e.headerOffset,i=e.footerOffset;this.headerOffset=o,this.footerOffset=i;var u=F(this.$attrs,this.componentData);return t(this.getTag(),u,r)},created:function(){null!==this.list&&null!==this.value&&M["b"].error("Value and list props are mutually exclusive! Please set one or another."),"div"!==this.element&&M["b"].warn("Element props is deprecated please use tag props instead. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#element-props"),void 0!==this.options&&M["b"].warn("Options props is deprecated, add sortable options directly as vue.draggable item, or use v-bind. See https://github.com/SortableJS/Vue.Draggable/blob/master/documentation/migrate.md#options-props")},mounted:function(){var t=this;if(this.noneFunctionalComponentMode=this.getTag().toLowerCase()!==this.$el.nodeName.toLowerCase(),this.noneFunctionalComponentMode&&this.transitionMode)throw new Error("Transition-group inside component is not supported. Please alter tag value or remove transition-group. Current tag value: ".concat(this.getTag()));var n={};$.forEach(function(e){n["on"+e]=P.call(t,e)}),k.forEach(function(e){n["on"+e]=A.bind(t,e)});var e=c()(this.$attrs).reduce(function(n,e){return n[Object(M["a"])(e)]=t.$attrs[e],n},{}),r=i()({},this.options,e,n,{onMove:function(n,e){return t.onDragMove(n,e)}});!("draggable"in r)&&(r.draggable=">*"),this._sortable=new _.a(this.rootContainer,r),this.computeIndexes()},beforeDestroy:function(){void 0!==this._sortable&&this._sortable.destroy()},computed:{rootContainer:function(){return this.transitionMode?this.$el.children[0]:this.$el},realList:function(){return this.list?this.list:this.value}},watch:{options:{handler:function(t){this.updateOptions(t)},deep:!0},$attrs:{handler:function(t){this.updateOptions(t)},deep:!0},realList:function(){this.computeIndexes()}},methods:{getTag:function(){return this.tag||this.element},updateOptions:function(t){for(var n in t){var e=Object(M["a"])(n);-1===D.indexOf(e)&&this._sortable.option(e,t[n])}},getChildrenNodes:function(){if(this.init||(this.noneFunctionalComponentMode=this.noneFunctionalComponentMode&&1===this.$children.length,this.init=!0),this.noneFunctionalComponentMode)return this.$children[0].$slots.default;var t=this.$slots.default;return this.transitionMode?t[0].child.$slots.default:t},computeIndexes:function(){var t=this;this.$nextTick(function(){t.visibleIndexes=E(t.getChildrenNodes(),t.rootContainer.children,t.transitionMode,t.footerOffset)})},getUnderlyingVm:function(t){var n=C(this.getChildrenNodes()||[],t);if(-1===n)return null;var e=this.realList[n];return{index:n,element:e}},getUnderlyingPotencialDraggableComponent:function(t){var n=t.__vue__;return n&&n.$options&&"transition-group"===n.$options._componentTag?n.$parent:n},emitChanges:function(t){var n=this;this.$nextTick(function(){n.$emit("change",t)})},alterList:function(t){if(this.list)t(this.list);else{var n=S(this.value);t(n),this.$emit("input",n)}},spliceList:function(){var t=arguments,n=function(n){return n.splice.apply(n,S(t))};this.alterList(n)},updatePosition:function(t,n){var e=function(e){return e.splice(n,0,e.splice(t,1)[0])};this.alterList(e)},getRelatedContextFromMoveEvent:function(t){var n=t.to,e=t.related,r=this.getUnderlyingPotencialDraggableComponent(n);if(!r)return{component:r};var o=r.realList,u={list:o,component:r};if(n!==e&&o&&r.getUnderlyingVm){var c=r.getUnderlyingVm(e);if(c)return i()(c,u)}return u},getVmIndex:function(t){var n=this.visibleIndexes,e=n.length;return t>e-1?e:n[t]},getComponent:function(){return this.$slots.default[0].componentInstance},resetTransitionData:function(t){if(this.noTransitionOnDrag&&this.transitionMode){var n=this.getChildrenNodes();n[t].data=null;var e=this.getComponent();e.children=[],e.kept=void 0}},onDragStart:function(t){this.context=this.getUnderlyingVm(t.item),t.item._underlying_vm_=this.clone(this.context.element),R=t.item},onDragAdd:function(t){var n=t.item._underlying_vm_;if(void 0!==n){Object(M["d"])(t.item);var e=this.getVmIndex(t.newIndex);this.spliceList(e,0,n),this.computeIndexes();var r={element:n,newIndex:e};this.emitChanges({added:r})}},onDragRemove:function(t){if(Object(M["c"])(this.rootContainer,t.item,t.oldIndex),"clone"!==t.pullMode){var n=this.context.index;this.spliceList(n,1);var e={element:this.context.element,oldIndex:n};this.resetTransitionData(n),this.emitChanges({removed:e})}else Object(M["d"])(t.clone)},onDragUpdate:function(t){Object(M["d"])(t.item),Object(M["c"])(t.from,t.item,t.oldIndex);var n=this.context.index,e=this.getVmIndex(t.newIndex);this.updatePosition(n,e);var r={element:this.context.element,oldIndex:n,newIndex:e};this.emitChanges({moved:r})},updateProperty:function(t,n){t.hasOwnProperty(n)&&(t[n]+=this.headerOffset)},computeFutureIndex:function(t,n){if(!t.element)return 0;var e=S(n.to.children).filter(function(t){return"none"!==t.style["display"]}),r=e.indexOf(n.related),o=t.component.getVmIndex(r),i=-1!==e.indexOf(R);return i||!n.willInsertAfter?o:o+1},onDragMove:function(t,n){var e=this.move;if(!e||!this.realList)return!0;var r=this.getRelatedContextFromMoveEvent(t),o=this.context,u=this.computeFutureIndex(r,t);i()(o,{futureIndex:u});var c=i()({},t,{relatedContext:r,draggedContext:o});return e(c,n)},onDragEnd:function(){this.computeIndexes(),R=null}}};"undefined"!==typeof window&&"Vue"in window&&window.Vue.component("draggable",N);var U=N;n["default"]=U}})["default"]}); //# sourceMappingURL=vuedraggable.umd.min.js.map \ No newline at end of file diff --git a/system/author/layouts/layout.twig b/system/author/layouts/layout.twig index da71813..574093b 100644 --- a/system/author/layouts/layout.twig +++ b/system/author/layouts/layout.twig @@ -1,41 +1,41 @@ - - - - - {% block title %}{% endblock %} - - - - - - - - - - - - - - - - - - - -
- {% include 'partials/navi.twig' %} -
- {% include 'partials/flash.twig' %} -
- -
- {% block content %}{% endblock %} -
-
-
- - - + + + + + {% block title %}{% endblock %} + + + + + + + + + + + + + + + + + + + +
+ {% include 'partials/navi.twig' %} +
+ {% include 'partials/flash.twig' %} +
+ +
+ {% block content %}{% endblock %} +
+
+
+ + + \ No newline at end of file diff --git a/system/author/layouts/layoutAuth.twig b/system/author/layouts/layoutAuth.twig index 89e79e1..6aeea32 100644 --- a/system/author/layouts/layoutAuth.twig +++ b/system/author/layouts/layoutAuth.twig @@ -1,34 +1,34 @@ - - - - - {% block title %}{% endblock %} - - - - - - - - - - - - - - - - - - - - - {% include 'partials/flash.twig' %} -
- - {% block content %}{% endblock %} - -
- - + + + + + {% block title %}{% endblock %} + + + + + + + + + + + + + + + + + + + + + {% include 'partials/flash.twig' %} +
+ + {% block content %}{% endblock %} + +
+ + \ No newline at end of file diff --git a/system/author/layouts/layoutBlank.twig b/system/author/layouts/layoutBlank.twig index 8c35820..1e70399 100644 --- a/system/author/layouts/layoutBlank.twig +++ b/system/author/layouts/layoutBlank.twig @@ -1,36 +1,36 @@ - - - - - {% block title %}{% endblock %} - - - - - - - - - - - - - - - - - - -
- {% include 'partials/navi.twig' %} -
- {% include 'partials/flash.twig' %} -
- -
- {% block content %}{% endblock %} -
-
-
- + + + + + {% block title %}{% endblock %} + + + + + + + + + + + + + + + + + + +
+ {% include 'partials/navi.twig' %} +
+ {% include 'partials/flash.twig' %} +
+ +
+ {% block content %}{% endblock %} +
+
+
+ \ No newline at end of file diff --git a/system/author/layouts/layoutBlox.twig b/system/author/layouts/layoutBlox.twig index 7cf00f4..f5ba31b 100644 --- a/system/author/layouts/layoutBlox.twig +++ b/system/author/layouts/layoutBlox.twig @@ -1,55 +1,55 @@ - - - - - {% block title %}{% endblock %} - - - - - - - - - - - - - - - - - - - - {{ assets.renderCSS() }} - - - -
- {% include 'partials/navi.twig' %} -
- {% include 'partials/flash.twig' %} -
- -
- {% block content %}{% endblock %} -
-
-
- - - - - - - - - - - {{ assets.renderJS() }} - - + + + + + {% block title %}{% endblock %} + + + + + + + + + + + + + + + + + + + + {{ assets.renderCSS() }} + + + +
+ {% include 'partials/navi.twig' %} +
+ {% include 'partials/flash.twig' %} +
+ +
+ {% block content %}{% endblock %} +
+
+
+ + + + + + + + + + + {{ assets.renderJS() }} + + \ No newline at end of file diff --git a/system/author/layouts/layoutEditor.twig b/system/author/layouts/layoutEditor.twig index 4a90b45..6de515f 100644 --- a/system/author/layouts/layoutEditor.twig +++ b/system/author/layouts/layoutEditor.twig @@ -1,48 +1,48 @@ - - - - - {% block title %}{% endblock %} - - - - - - - - - - - - - - - - - - - - -
- {% include 'partials/navi.twig' %} -
- {% include 'partials/flash.twig' %} -
- -
- {% block content %}{% endblock %} -
-
-
- - - - - - - - - + + + + + {% block title %}{% endblock %} + + + + + + + + + + + + + + + + + + + + +
+ {% include 'partials/navi.twig' %} +
+ {% include 'partials/flash.twig' %} +
+ +
+ {% block content %}{% endblock %} +
+
+
+ + + + + + + + + \ No newline at end of file diff --git a/system/author/partials/aside.twig b/system/author/partials/aside.twig index 0a71e54..5723cc5 100644 --- a/system/author/partials/aside.twig +++ b/system/author/partials/aside.twig @@ -1,19 +1,19 @@ -