Jelajahi Sumber

Version 1.2.13: Add Content-Blocks Everywhere

Sebastian 6 tahun lalu
induk
melakukan
b8196122d8
37 mengubah file dengan 3255 tambahan dan 330 penghapusan
  1. 1 1
      cache/lastCache.txt
  2. 3 2
      content/00-Welcome/03-Markdown-Test.md
  3. 0 2
      content/index.md
  4. TEMPAT SAMPAH
      media/live/5c4ccd43e5ac8-live.png
  5. TEMPAT SAMPAH
      media/live/5c4ccd43e5ac8-mlibrary.png
  6. TEMPAT SAMPAH
      media/original/5c4ccd43e5ac8-original.png
  7. 1 1
      system/Controllers/AuthController.php
  8. 155 13
      system/Controllers/ContentApiController.php
  9. 4 1
      system/Controllers/ContentController.php
  10. 8 2
      system/Controllers/Controller.php
  11. 5 5
      system/Controllers/SettingsController.php
  12. 2 0
      system/Models/Field.php
  13. 2 0
      system/Routes/Api.php
  14. 1 1
      system/author/auth/login.twig
  15. 1 1
      system/author/auth/setup.twig
  16. 1 1
      system/author/auth/welcome.twig
  17. 68 68
      system/author/css/fontello/config.json
  18. 11 11
      system/author/css/fontello/css/fontello-codes.css
  19. 3 3
      system/author/css/fontello/css/fontello-embedded.css
  20. 11 11
      system/author/css/fontello/css/fontello-ie7-codes.css
  21. 11 11
      system/author/css/fontello/css/fontello-ie7.css
  22. 18 18
      system/author/css/fontello/css/fontello.css
  23. 19 19
      system/author/css/fontello/demo.html
  24. TEMPAT SAMPAH
      system/author/css/fontello/font/fontello.eot
  25. 13 13
      system/author/css/fontello/font/fontello.svg
  26. TEMPAT SAMPAH
      system/author/css/fontello/font/fontello.ttf
  27. TEMPAT SAMPAH
      system/author/css/fontello/font/fontello.woff
  28. TEMPAT SAMPAH
      system/author/css/fontello/font/fontello.woff2
  29. 143 72
      system/author/css/style.css
  30. 54 0
      system/author/editor/editor-blox-orig.twig
  31. 31 12
      system/author/editor/editor-blox.twig
  32. 1312 0
      system/author/js/vue-blox-inline.js
  33. 1228 0
      system/author/js/vue-blox-orig.js
  34. 142 55
      system/author/js/vue-blox.js
  35. 2 2
      system/system.php
  36. 4 4
      themes/typemill/page.twig
  37. 1 1
      themes/typemill/typemill.yaml

+ 1 - 1
cache/lastCache.txt

@@ -1 +1 @@
-1550594676
+1555084348

+ 3 - 2
content/00-Welcome/03-Markdown-Test.md

@@ -6,7 +6,7 @@ Developers love markdown, because it is much cleaner and saver than HTML. And th
 
 If you develop a theme for TYPEMILL, please take care that all elements on this page are designed properly.
 
-##Table of Contents
+## 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.
 
@@ -291,4 +291,5 @@ x = \int_{0^1}^1(-b \pm \sqrt{b^2-4ac})/(2a)
 ​````
 
 [^1]: Thank you for scrolling.
-[^2]: This is the end of the page.
+[^2]: This is the end of the page.
+

+ 0 - 2
content/index.md

@@ -1,6 +1,4 @@
 # Typemill
 
-**MODERN WEB PUBLISHING FOR WRITERS**
-
 *Typemill is a user-friendly and lightweight open source CMS for publishing text-works like prosa, lyrics, manuals, documentations, studies and more. Just download and start.*
 

TEMPAT SAMPAH
media/live/5c4ccd43e5ac8-live.png


TEMPAT SAMPAH
media/live/5c4ccd43e5ac8-mlibrary.png


TEMPAT SAMPAH
media/original/5c4ccd43e5ac8-original.png


+ 1 - 1
system/Controllers/AuthController.php

@@ -63,7 +63,7 @@ class AuthController extends Controller
 			}
 		}
 
-		$this->render($response, '/auth/login.twig', $data);
+		return $this->render($response, '/auth/login.twig', $data);
 	}
 	
 	/**

+ 155 - 13
system/Controllers/ContentApiController.php

@@ -550,7 +550,60 @@ class ContentApiController extends ContentController
 		return $response->withJson(array('data' => $content, 'errors' => false));
 	}
 	
-	public function updateBlock(Request $request, Response $response, $args)
+	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;
+		
+		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);
+		}
+				
+		return $response->withJson(array('data' => $content, 'errors' => false));
+	}
+
+	public function addBlock(Request $request, Response $response, $args)
 	{
 		/* get params from call */
 		$this->params 	= $request->getParams();
@@ -605,14 +658,105 @@ class ContentApiController extends ContentController
 		{
 			# set the id of the markdown-block (it will be one more than the actual array, so count is perfect) 
 			$id = count($pageMarkdown);
-			
-			# set the id with prefix "blox-"
-			$blockId = 'blox-' . $id;
-			
+						
 			# add the new markdown block to the page content
 			$pageMarkdown[] = $blockMarkdown;			
 		}
-		elseif(!isset($pageMarkdown[$this->params['block_id']]))
+		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);
+		}
+		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);
+		
+		# needed for ToC links
+		$relurl 		= '/tm/content/' . $this->settings['editor'] . '/' . $this->item->urlRel;
+		
+		/* parse markdown-content-array to content-string */
+		$blockHTML		= $parsedown->markup($blockArray, $relurl);
+
+		return $response->withJson(array('content' => $blockHTML, 'markdown' => $blockMarkdown, 'id' => $id, 'errors' => false));
+	}
+	
+	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);
@@ -628,14 +772,12 @@ class ContentApiController extends ContentController
 			# add the markdown-headline to the page-markdown
 			$pageMarkdown[0] = $blockMarkdownTitle;
 			$id = 0;
-			$blockId = 0;
 		}
 		else
 		{
 			# update the markdown block in the page content
 			$pageMarkdown[$this->params['block_id']] = $blockMarkdown;
 			$id = $this->params['block_id'];
-			$blockId = $this->params['block_id'];
 		}
 	
 		# encode the content into json
@@ -674,7 +816,7 @@ class ContentApiController extends ContentController
 		/* parse markdown-content-array to content-string */
 		$blockHTML		= $parsedown->markup($blockArray, $relurl);
 
-		return $response->withJson(array('content' => $blockHTML, 'markdown' => $blockMarkdown, 'blockId' => $blockId, 'id' => $id, 'errors' => false));
+		return $response->withJson(array('content' => $blockHTML, 'markdown' => $blockMarkdown, 'id' => $id, 'errors' => false));
 	}
 	
 	public function moveBlock(Request $request, Response $response, $args)
@@ -877,10 +1019,10 @@ class ContentApiController extends ContentController
 						
 			$request 	= $request->withParsedBody($params);
 			
-			return $this->updateBlock($request, $response, $args);
+			return $this->addBlock($request, $response, $args);
 		}
 
-		return $response->withJson(array('errors' => 'could not store image to temporary folder'));	
+		return $response->withJson(array('errors' => 'could not store image to media folder'));	
 	}
 
 	public function saveVideoImage(Request $request, Response $response, $args)
@@ -945,9 +1087,9 @@ class ContentApiController extends ContentController
 
 			$request 	= $request->withParsedBody($this->params);
 			
-			return $this->updateBlock($request, $response, $args);
+			return $this->addBlock($request, $response, $args);
 		}
-
+		
 		return $response->withJson(array('errors' => 'could not store the preview image'));	
 	}
 }

+ 4 - 1
system/Controllers/ContentController.php

@@ -62,12 +62,15 @@ abstract class ContentController
 		{
 			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');

+ 8 - 2
system/Controllers/Controller.php

@@ -3,6 +3,9 @@
 namespace Typemill\Controllers;
 
 /* Use the slim-container */
+use Slim\Http\Request;
+use Slim\Http\Response;
+use Slim\Views\Twig;
 use Interop\Container\ContainerInterface;
 use Typemill\Events\OnPageReady;
 
@@ -17,18 +20,21 @@ abstract class Controller
 	
 	protected function render($response, $route, $data)
 	{
-		$data = $this->c->dispatcher->dispatch('onPageReady', new OnPageReady($data))->getData();
+		// $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');

+ 5 - 5
system/Controllers/SettingsController.php

@@ -23,7 +23,7 @@ class SettingsController extends Controller
 		$users		= $user->getUsers();
 		$route 		= $request->getAttribute('route');
 		
-		$this->render($response, 'settings/system.twig', array('settings' => $settings, 'copyright' => $copyright, 'languages' => $languages, 'locale' => $locale, 'users' => $users, 'route' => $route->getName() ));
+		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)
@@ -132,7 +132,7 @@ class SettingsController extends Controller
 		$users		= $user->getUsers();
 		$route 		= $request->getAttribute('route');
 		
-		$this->render($response, 'settings/themes.twig', array('settings' => $userSettings, 'themes' => $themedata, 'users' => $users, 'route' => $route->getName() ));
+		return $this->render($response, 'settings/themes.twig', array('settings' => $userSettings, 'themes' => $themedata, 'users' => $users, 'route' => $route->getName() ));
 	}
 	
 	public function showPlugins($request, $response, $args)
@@ -201,7 +201,7 @@ class SettingsController extends Controller
 		$users 	= $user->getUsers();
 		$route 	= $request->getAttribute('route');
 		
-		$this->render($response, 'settings/plugins.twig', array('settings' => $userSettings, 'plugins' => $plugins, 'users' => $users, 'route' => $route->getName() ));
+		return $this->render($response, 'settings/plugins.twig', array('settings' => $userSettings, 'plugins' => $plugins, 'users' => $users, 'route' => $route->getName() ));
 	}
 
 	/*************************************
@@ -420,7 +420,7 @@ class SettingsController extends Controller
 			$userdata[] = $user->getUser($username);
 		}
 		
-		$this->render($response, 'settings/userlist.twig', array('settings' => $settings, 'users' => $users, 'userdata' => $userdata, 'route' => $route->getName() ));		
+		return $this->render($response, 'settings/userlist.twig', array('settings' => $settings, 'users' => $users, 'userdata' => $userdata, 'route' => $route->getName() ));		
 	}
 	
 	public function newUser($request, $response, $args)
@@ -431,7 +431,7 @@ class SettingsController extends Controller
 		$route 		= $request->getAttribute('route');
 		$settings 	= $this->c->get('settings');
 
-		$this->render($response, 'settings/usernew.twig', array('settings' => $settings, 'users' => $users, 'userrole' => $userrole, 'route' => $route->getName() ));
+		return $this->render($response, 'settings/usernew.twig', array('settings' => $settings, 'users' => $users, 'userrole' => $userrole, 'route' => $route->getName() ));
 	}
 		
 	public function createUser($request, $response, $args)

+ 2 - 0
system/Models/Field.php

@@ -67,6 +67,8 @@ class Field
 									'size',
 									'rows',
 									'cols',
+									'min',
+									'max',
 									'class',
 									'pattern'
 								);

+ 2 - 0
system/Routes/Api.php

@@ -8,6 +8,7 @@ use Typemill\Middleware\RestrictApiAccess;
 $app->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']));
@@ -16,6 +17,7 @@ $app->delete('/api/v1/article', ContentApiController::class . ':deleteArticle')-
 $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->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']));

+ 1 - 1
system/author/auth/login.twig

@@ -17,7 +17,7 @@
 				</div>
 				<div class="formElement{{ errors.password ? ' errors' : '' }}">
 					<label for="password">Password <abbr title="required">*</abbr></label>
-					<input type="password" name="password" required>
+					<input type="password" name="password" required autoomplete="off">
 					{% if errors.password %}
 						<span class="error">{{ errors.password | first }}</span>
 					{% endif %}

+ 1 - 1
system/author/auth/setup.twig

@@ -24,7 +24,7 @@
 				</div>
 				<div class="formElement{{ errors.password ? ' errors' : '' }}">
 					<label for="password">Password <abbr title="required">*</abbr></label>
-					<input type="password" name="password" required>
+					<input type="password" name="password" required autocomplete="off">
 					{% if errors.password %}
 						<span class="error">{{ errors.password | first }}</span>
 					{% endif %}

+ 1 - 1
system/author/auth/welcome.twig

@@ -11,7 +11,7 @@
 				<h1>Hurra!</h1>
 				<p>Your account has been created and you are logged in now.</p>
 				<p><strong>Next step:</strong> Visit the author panel and setup your new website. You can configure the system, choose themes and add plugins.</p>
-				<p><strong>New:</strong> Markdown is cool. But editing tables with markdown is a nightmare! With Typemill it becomes easy again, because we recently added a visual table editor. Enjoy!!</p>
+				<p><strong>New:</strong>With this latest version you can add a new content block with the visual editor everywhere on a page, not just at the end of the text. Sounds natural, but was pretty hard to code :)</p>
 				<p><strong>Get help:</strong> If you have any questions, please consult the <a target="_blank" href="https://typemill.net/typemill"><i class="icon-link-ext"></i> docs</a> or open a new issue on <a target="_blank" href="https://github.com/typemill/typemill"><i class="icon-link-ext"></i> github</a>.</p>
 			</div>
 			<a class="button" href="{{ path_for('settings.show') }}">Configure your website</a>

+ 68 - 68
system/author/css/fontello/config.json

@@ -7,10 +7,10 @@
   "ascent": 850,
   "glyphs": [
     {
-      "uid": "47a1f80457068fbeab69fdb83d7d0817",
-      "css": "youtube-play",
-      "code": 61802,
-      "src": "fontawesome"
+      "uid": "8663320a860b00f26e94d3d15c9ba99a",
+      "css": "clock",
+      "code": 59393,
+      "src": "entypo"
     },
     {
       "uid": "381da2c2f7fd51f8de877c044d7f439d",
@@ -19,45 +19,27 @@
       "src": "fontawesome"
     },
     {
-      "uid": "c709da589c923ba3c2ad48d9fc563e93",
+      "uid": "5211af474d3a9848f67f945e2ccaf143",
       "css": "cancel",
-      "code": 59393,
-      "src": "entypo"
-    },
-    {
-      "uid": "f9cbf7508cd04145ade2800169959eef",
-      "css": "font",
       "code": 59394,
       "src": "fontawesome"
     },
     {
-      "uid": "e99461abfef3923546da8d745372c995",
-      "css": "cog",
+      "uid": "44e04715aecbca7f266a17d5a7863c68",
+      "css": "plus",
       "code": 59395,
       "src": "fontawesome"
     },
-    {
-      "uid": "8b9e6a8dd8f67f7c003ed8e7e5ee0857",
-      "css": "off",
-      "code": 59396,
-      "src": "fontawesome"
-    },
     {
       "uid": "d7271d490b71df4311e32cdacae8b331",
       "css": "home",
-      "code": 59397,
-      "src": "fontawesome"
-    },
-    {
-      "uid": "44e04715aecbca7f266a17d5a7863c68",
-      "css": "plus",
-      "code": 59398,
+      "code": 59396,
       "src": "fontawesome"
     },
     {
-      "uid": "6605ee6441bf499ffa3c63d3c7409471",
-      "css": "move",
-      "code": 61511,
+      "uid": "0ddd3e8201ccc7d41f7b7c9d27eca6c1",
+      "css": "link",
+      "code": 59397,
       "src": "fontawesome"
     },
     {
@@ -67,15 +49,15 @@
       "src": "fontawesome"
     },
     {
-      "uid": "b013f6403e5ab0326614e68d1850fd6b",
-      "css": "resize-full-alt",
-      "code": 61618,
+      "uid": "ab95e1351ebaec5850101097cbf7097f",
+      "css": "quote-left",
+      "code": 61709,
       "src": "fontawesome"
     },
     {
-      "uid": "872d9516df93eb6b776cc4d94bd97dac",
-      "css": "video",
-      "code": 59399,
+      "uid": "7034e4d22866af82bef811f52fb1ba46",
+      "css": "code",
+      "code": 61729,
       "src": "fontawesome"
     },
     {
@@ -91,39 +73,45 @@
       "src": "fontawesome"
     },
     {
-      "uid": "7034e4d22866af82bef811f52fb1ba46",
-      "css": "code",
-      "code": 61729,
+      "uid": "e99461abfef3923546da8d745372c995",
+      "css": "cog",
+      "code": 59398,
       "src": "fontawesome"
     },
     {
-      "uid": "8fb55fd696d9a0f58f3b27c1d8633750",
-      "css": "table",
-      "code": 61646,
+      "uid": "6605ee6441bf499ffa3c63d3c7409471",
+      "css": "move",
+      "code": 61511,
       "src": "fontawesome"
     },
     {
-      "uid": "ab95e1351ebaec5850101097cbf7097f",
-      "css": "quote-left",
-      "code": 61709,
+      "uid": "c5845105a87df2ee1999826d90622f6a",
+      "css": "paragraph",
+      "code": 61917,
       "src": "fontawesome"
     },
     {
-      "uid": "a2a74f5e7b7d9ba054897d8c795a326a",
-      "css": "list-bullet",
-      "code": 61642,
+      "uid": "0c708edd8fae2376b3370aa56d40cf9e",
+      "css": "header",
+      "code": 61916,
       "src": "fontawesome"
     },
     {
-      "uid": "f6766a8b042c2453a4e153af03294383",
-      "css": "list-numbered",
-      "code": 61643,
+      "uid": "f9cbf7508cd04145ade2800169959eef",
+      "css": "font",
+      "code": 59399,
       "src": "fontawesome"
     },
     {
-      "uid": "0c708edd8fae2376b3370aa56d40cf9e",
-      "css": "header",
-      "code": 61916,
+      "uid": "a8cb1c217f02b073db3670c061cc54d2",
+      "css": "italic",
+      "code": 59400,
+      "src": "fontawesome"
+    },
+    {
+      "uid": "02cca871bb69da75e8ee286b7055832c",
+      "css": "bold",
+      "code": 59401,
       "src": "fontawesome"
     },
     {
@@ -139,21 +127,39 @@
       "src": "fontawesome"
     },
     {
-      "uid": "61c242c9e2134d5864d7fdd57b3c9289",
-      "css": "strike",
-      "code": 61644,
+      "uid": "f6766a8b042c2453a4e153af03294383",
+      "css": "list-numbered",
+      "code": 61643,
       "src": "fontawesome"
     },
     {
-      "uid": "02cca871bb69da75e8ee286b7055832c",
-      "css": "bold",
-      "code": 59400,
+      "uid": "a2a74f5e7b7d9ba054897d8c795a326a",
+      "css": "list-bullet",
+      "code": 61642,
       "src": "fontawesome"
     },
     {
-      "uid": "a8cb1c217f02b073db3670c061cc54d2",
-      "css": "italic",
-      "code": 59401,
+      "uid": "8fb55fd696d9a0f58f3b27c1d8633750",
+      "css": "table",
+      "code": 61646,
+      "src": "fontawesome"
+    },
+    {
+      "uid": "8b9e6a8dd8f67f7c003ed8e7e5ee0857",
+      "css": "off",
+      "code": 59402,
+      "src": "fontawesome"
+    },
+    {
+      "uid": "872d9516df93eb6b776cc4d94bd97dac",
+      "css": "video",
+      "code": 59403,
+      "src": "fontawesome"
+    },
+    {
+      "uid": "47a1f80457068fbeab69fdb83d7d0817",
+      "css": "youtube-play",
+      "code": 61802,
       "src": "fontawesome"
     },
     {
@@ -161,12 +167,6 @@
       "css": "math",
       "code": 61466,
       "src": "mfglabs"
-    },
-    {
-      "uid": "c5845105a87df2ee1999826d90622f6a",
-      "css": "paragraph",
-      "code": 61917,
-      "src": "fontawesome"
     }
   ]
 }

+ 11 - 11
system/author/css/fontello/css/fontello-codes.css

@@ -1,21 +1,21 @@
 
 .icon-picture:before { content: '\e800'; } /* '' */
-.icon-cancel:before { content: '\e801'; } /* '' */
-.icon-font:before { content: '\e802'; } /* '' */
-.icon-cog:before { content: '\e803'; } /* '' */
-.icon-off:before { content: '\e804'; } /* '' */
-.icon-home:before { content: '\e805'; } /* '' */
-.icon-plus:before { content: '\e806'; } /* '' */
-.icon-video:before { content: '\e807'; } /* '' */
-.icon-bold:before { content: '\e808'; } /* '' */
-.icon-italic:before { content: '\e809'; } /* '' */
+.icon-clock:before { content: '\e801'; } /* '' */
+.icon-cancel:before { content: '\e802'; } /* '' */
+.icon-plus:before { content: '\e803'; } /* '' */
+.icon-home:before { content: '\e804'; } /* '' */
+.icon-link:before { content: '\e805'; } /* '' */
+.icon-cog:before { content: '\e806'; } /* '' */
+.icon-font:before { content: '\e807'; } /* '' */
+.icon-italic:before { content: '\e808'; } /* '' */
+.icon-bold:before { content: '\e809'; } /* '' */
+.icon-off:before { content: '\e80a'; } /* '' */
+.icon-video:before { content: '\e80b'; } /* '' */
 .icon-math:before { content: '\f01a'; } /* '' */
 .icon-move:before { content: '\f047'; } /* '' */
 .icon-link-ext:before { content: '\f08e'; } /* '' */
-.icon-resize-full-alt:before { content: '\f0b2'; } /* '' */
 .icon-list-bullet:before { content: '\f0ca'; } /* '' */
 .icon-list-numbered:before { content: '\f0cb'; } /* '' */
-.icon-strike:before { content: '\f0cc'; } /* '' */
 .icon-underline:before { content: '\f0cd'; } /* '' */
 .icon-table:before { content: '\f0ce'; } /* '' */
 .icon-doc-text:before { content: '\f0f6'; } /* '' */

File diff ditekan karena terlalu besar
+ 3 - 3
system/author/css/fontello/css/fontello-embedded.css


+ 11 - 11
system/author/css/fontello/css/fontello-ie7-codes.css

@@ -1,21 +1,21 @@
 
 .icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
-.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
-.icon-font { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
-.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
-.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe804;&nbsp;'); }
-.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe805;&nbsp;'); }
-.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe806;&nbsp;'); }
-.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe807;&nbsp;'); }
-.icon-bold { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe808;&nbsp;'); }
-.icon-italic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe809;&nbsp;'); }
+.icon-clock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
+.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
+.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
+.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe804;&nbsp;'); }
+.icon-link { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe805;&nbsp;'); }
+.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe806;&nbsp;'); }
+.icon-font { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe807;&nbsp;'); }
+.icon-italic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe808;&nbsp;'); }
+.icon-bold { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe809;&nbsp;'); }
+.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80a;&nbsp;'); }
+.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80b;&nbsp;'); }
 .icon-math { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf01a;&nbsp;'); }
 .icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf047;&nbsp;'); }
 .icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf08e;&nbsp;'); }
-.icon-resize-full-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0b2;&nbsp;'); }
 .icon-list-bullet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0ca;&nbsp;'); }
 .icon-list-numbered { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0cb;&nbsp;'); }
-.icon-strike { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0cc;&nbsp;'); }
 .icon-underline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0cd;&nbsp;'); }
 .icon-table { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0ce;&nbsp;'); }
 .icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0f6;&nbsp;'); }

+ 11 - 11
system/author/css/fontello/css/fontello-ie7.css

@@ -11,22 +11,22 @@
 }
  
 .icon-picture { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe800;&nbsp;'); }
-.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
-.icon-font { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
-.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
-.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe804;&nbsp;'); }
-.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe805;&nbsp;'); }
-.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe806;&nbsp;'); }
-.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe807;&nbsp;'); }
-.icon-bold { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe808;&nbsp;'); }
-.icon-italic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe809;&nbsp;'); }
+.icon-clock { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe801;&nbsp;'); }
+.icon-cancel { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe802;&nbsp;'); }
+.icon-plus { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe803;&nbsp;'); }
+.icon-home { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe804;&nbsp;'); }
+.icon-link { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe805;&nbsp;'); }
+.icon-cog { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe806;&nbsp;'); }
+.icon-font { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe807;&nbsp;'); }
+.icon-italic { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe808;&nbsp;'); }
+.icon-bold { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe809;&nbsp;'); }
+.icon-off { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80a;&nbsp;'); }
+.icon-video { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xe80b;&nbsp;'); }
 .icon-math { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf01a;&nbsp;'); }
 .icon-move { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf047;&nbsp;'); }
 .icon-link-ext { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf08e;&nbsp;'); }
-.icon-resize-full-alt { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0b2;&nbsp;'); }
 .icon-list-bullet { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0ca;&nbsp;'); }
 .icon-list-numbered { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0cb;&nbsp;'); }
-.icon-strike { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0cc;&nbsp;'); }
 .icon-underline { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0cd;&nbsp;'); }
 .icon-table { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0ce;&nbsp;'); }
 .icon-doc-text { *zoom: expression( this.runtimeStyle['zoom'] = '1', this.innerHTML = '&#xf0f6;&nbsp;'); }

+ 18 - 18
system/author/css/fontello/css/fontello.css

@@ -1,11 +1,11 @@
 @font-face {
   font-family: 'fontello';
-  src: url('../font/fontello.eot?8966449');
-  src: url('../font/fontello.eot?8966449#iefix') format('embedded-opentype'),
-       url('../font/fontello.woff2?8966449') format('woff2'),
-       url('../font/fontello.woff?8966449') format('woff'),
-       url('../font/fontello.ttf?8966449') format('truetype'),
-       url('../font/fontello.svg?8966449#fontello') format('svg');
+  src: url('../font/fontello.eot?18427545');
+  src: url('../font/fontello.eot?18427545#iefix') format('embedded-opentype'),
+       url('../font/fontello.woff2?18427545') format('woff2'),
+       url('../font/fontello.woff?18427545') format('woff'),
+       url('../font/fontello.ttf?18427545') format('truetype'),
+       url('../font/fontello.svg?18427545#fontello') format('svg');
   font-weight: normal;
   font-style: normal;
 }
@@ -15,7 +15,7 @@
 @media screen and (-webkit-min-device-pixel-ratio:0) {
   @font-face {
     font-family: 'fontello';
-    src: url('../font/fontello.svg?8966449#fontello') format('svg');
+    src: url('../font/fontello.svg?18427545#fontello') format('svg');
   }
 }
 */
@@ -56,22 +56,22 @@
 }
  
 .icon-picture:before { content: '\e800'; } /* '' */
-.icon-cancel:before { content: '\e801'; } /* '' */
-.icon-font:before { content: '\e802'; } /* '' */
-.icon-cog:before { content: '\e803'; } /* '' */
-.icon-off:before { content: '\e804'; } /* '' */
-.icon-home:before { content: '\e805'; } /* '' */
-.icon-plus:before { content: '\e806'; } /* '' */
-.icon-video:before { content: '\e807'; } /* '' */
-.icon-bold:before { content: '\e808'; } /* '' */
-.icon-italic:before { content: '\e809'; } /* '' */
+.icon-clock:before { content: '\e801'; } /* '' */
+.icon-cancel:before { content: '\e802'; } /* '' */
+.icon-plus:before { content: '\e803'; } /* '' */
+.icon-home:before { content: '\e804'; } /* '' */
+.icon-link:before { content: '\e805'; } /* '' */
+.icon-cog:before { content: '\e806'; } /* '' */
+.icon-font:before { content: '\e807'; } /* '' */
+.icon-italic:before { content: '\e808'; } /* '' */
+.icon-bold:before { content: '\e809'; } /* '' */
+.icon-off:before { content: '\e80a'; } /* '' */
+.icon-video:before { content: '\e80b'; } /* '' */
 .icon-math:before { content: '\f01a'; } /* '' */
 .icon-move:before { content: '\f047'; } /* '' */
 .icon-link-ext:before { content: '\f08e'; } /* '' */
-.icon-resize-full-alt:before { content: '\f0b2'; } /* '' */
 .icon-list-bullet:before { content: '\f0ca'; } /* '' */
 .icon-list-numbered:before { content: '\f0cb'; } /* '' */
-.icon-strike:before { content: '\f0cc'; } /* '' */
 .icon-underline:before { content: '\f0cd'; } /* '' */
 .icon-table:before { content: '\f0ce'; } /* '' */
 .icon-doc-text:before { content: '\f0f6'; } /* '' */

+ 19 - 19
system/author/css/fontello/demo.html

@@ -229,11 +229,11 @@ body {
 }
 @font-face {
       font-family: 'fontello';
-      src: url('./font/fontello.eot?53292437');
-      src: url('./font/fontello.eot?53292437#iefix') format('embedded-opentype'),
-           url('./font/fontello.woff?53292437') format('woff'),
-           url('./font/fontello.ttf?53292437') format('truetype'),
-           url('./font/fontello.svg?53292437#fontello') format('svg');
+      src: url('./font/fontello.eot?32679445');
+      src: url('./font/fontello.eot?32679445#iefix') format('embedded-opentype'),
+           url('./font/fontello.woff?32679445') format('woff'),
+           url('./font/fontello.ttf?32679445') format('truetype'),
+           url('./font/fontello.svg?32679445#fontello') format('svg');
       font-weight: normal;
       font-style: normal;
     }
@@ -299,30 +299,30 @@ body {
     <div class="container" id="icons">
       <div class="row">
         <div class="the-icons span3" title="Code: 0xe800"><i class="demo-icon icon-picture">&#xe800;</i> <span class="i-name">icon-picture</span><span class="i-code">0xe800</span></div>
-        <div class="the-icons span3" title="Code: 0xe801"><i class="demo-icon icon-cancel">&#xe801;</i> <span class="i-name">icon-cancel</span><span class="i-code">0xe801</span></div>
-        <div class="the-icons span3" title="Code: 0xe802"><i class="demo-icon icon-font">&#xe802;</i> <span class="i-name">icon-font</span><span class="i-code">0xe802</span></div>
-        <div class="the-icons span3" title="Code: 0xe803"><i class="demo-icon icon-cog">&#xe803;</i> <span class="i-name">icon-cog</span><span class="i-code">0xe803</span></div>
+        <div class="the-icons span3" title="Code: 0xe801"><i class="demo-icon icon-clock">&#xe801;</i> <span class="i-name">icon-clock</span><span class="i-code">0xe801</span></div>
+        <div class="the-icons span3" title="Code: 0xe802"><i class="demo-icon icon-cancel">&#xe802;</i> <span class="i-name">icon-cancel</span><span class="i-code">0xe802</span></div>
+        <div class="the-icons span3" title="Code: 0xe803"><i class="demo-icon icon-plus">&#xe803;</i> <span class="i-name">icon-plus</span><span class="i-code">0xe803</span></div>
       </div>
       <div class="row">
-        <div class="the-icons span3" title="Code: 0xe804"><i class="demo-icon icon-off">&#xe804;</i> <span class="i-name">icon-off</span><span class="i-code">0xe804</span></div>
-        <div class="the-icons span3" title="Code: 0xe805"><i class="demo-icon icon-home">&#xe805;</i> <span class="i-name">icon-home</span><span class="i-code">0xe805</span></div>
-        <div class="the-icons span3" title="Code: 0xe806"><i class="demo-icon icon-plus">&#xe806;</i> <span class="i-name">icon-plus</span><span class="i-code">0xe806</span></div>
-        <div class="the-icons span3" title="Code: 0xe807"><i class="demo-icon icon-video">&#xe807;</i> <span class="i-name">icon-video</span><span class="i-code">0xe807</span></div>
+        <div class="the-icons span3" title="Code: 0xe804"><i class="demo-icon icon-home">&#xe804;</i> <span class="i-name">icon-home</span><span class="i-code">0xe804</span></div>
+        <div class="the-icons span3" title="Code: 0xe805"><i class="demo-icon icon-link">&#xe805;</i> <span class="i-name">icon-link</span><span class="i-code">0xe805</span></div>
+        <div class="the-icons span3" title="Code: 0xe806"><i class="demo-icon icon-cog">&#xe806;</i> <span class="i-name">icon-cog</span><span class="i-code">0xe806</span></div>
+        <div class="the-icons span3" title="Code: 0xe807"><i class="demo-icon icon-font">&#xe807;</i> <span class="i-name">icon-font</span><span class="i-code">0xe807</span></div>
       </div>
       <div class="row">
-        <div class="the-icons span3" title="Code: 0xe808"><i class="demo-icon icon-bold">&#xe808;</i> <span class="i-name">icon-bold</span><span class="i-code">0xe808</span></div>
-        <div class="the-icons span3" title="Code: 0xe809"><i class="demo-icon icon-italic">&#xe809;</i> <span class="i-name">icon-italic</span><span class="i-code">0xe809</span></div>
-        <div class="the-icons span3" title="Code: 0xf01a"><i class="demo-icon icon-math">&#xf01a;</i> <span class="i-name">icon-math</span><span class="i-code">0xf01a</span></div>
-        <div class="the-icons span3" title="Code: 0xf047"><i class="demo-icon icon-move">&#xf047;</i> <span class="i-name">icon-move</span><span class="i-code">0xf047</span></div>
+        <div class="the-icons span3" title="Code: 0xe808"><i class="demo-icon icon-italic">&#xe808;</i> <span class="i-name">icon-italic</span><span class="i-code">0xe808</span></div>
+        <div class="the-icons span3" title="Code: 0xe809"><i class="demo-icon icon-bold">&#xe809;</i> <span class="i-name">icon-bold</span><span class="i-code">0xe809</span></div>
+        <div class="the-icons span3" title="Code: 0xe80a"><i class="demo-icon icon-off">&#xe80a;</i> <span class="i-name">icon-off</span><span class="i-code">0xe80a</span></div>
+        <div class="the-icons span3" title="Code: 0xe80b"><i class="demo-icon icon-video">&#xe80b;</i> <span class="i-name">icon-video</span><span class="i-code">0xe80b</span></div>
       </div>
       <div class="row">
+        <div class="the-icons span3" title="Code: 0xf01a"><i class="demo-icon icon-math">&#xf01a;</i> <span class="i-name">icon-math</span><span class="i-code">0xf01a</span></div>
+        <div class="the-icons span3" title="Code: 0xf047"><i class="demo-icon icon-move">&#xf047;</i> <span class="i-name">icon-move</span><span class="i-code">0xf047</span></div>
         <div class="the-icons span3" title="Code: 0xf08e"><i class="demo-icon icon-link-ext">&#xf08e;</i> <span class="i-name">icon-link-ext</span><span class="i-code">0xf08e</span></div>
-        <div class="the-icons span3" title="Code: 0xf0b2"><i class="demo-icon icon-resize-full-alt">&#xf0b2;</i> <span class="i-name">icon-resize-full-alt</span><span class="i-code">0xf0b2</span></div>
         <div class="the-icons span3" title="Code: 0xf0ca"><i class="demo-icon icon-list-bullet">&#xf0ca;</i> <span class="i-name">icon-list-bullet</span><span class="i-code">0xf0ca</span></div>
-        <div class="the-icons span3" title="Code: 0xf0cb"><i class="demo-icon icon-list-numbered">&#xf0cb;</i> <span class="i-name">icon-list-numbered</span><span class="i-code">0xf0cb</span></div>
       </div>
       <div class="row">
-        <div class="the-icons span3" title="Code: 0xf0cc"><i class="demo-icon icon-strike">&#xf0cc;</i> <span class="i-name">icon-strike</span><span class="i-code">0xf0cc</span></div>
+        <div class="the-icons span3" title="Code: 0xf0cb"><i class="demo-icon icon-list-numbered">&#xf0cb;</i> <span class="i-name">icon-list-numbered</span><span class="i-code">0xf0cb</span></div>
         <div class="the-icons span3" title="Code: 0xf0cd"><i class="demo-icon icon-underline">&#xf0cd;</i> <span class="i-name">icon-underline</span><span class="i-code">0xf0cd</span></div>
         <div class="the-icons span3" title="Code: 0xf0ce"><i class="demo-icon icon-table">&#xf0ce;</i> <span class="i-name">icon-table</span><span class="i-code">0xf0ce</span></div>
         <div class="the-icons span3" title="Code: 0xf0f6"><i class="demo-icon icon-doc-text">&#xf0f6;</i> <span class="i-name">icon-doc-text</span><span class="i-code">0xf0f6</span></div>

TEMPAT SAMPAH
system/author/css/fontello/font/fontello.eot


+ 13 - 13
system/author/css/fontello/font/fontello.svg

@@ -8,23 +8,27 @@
 <missing-glyph horiz-adv-x="1000" />
 <glyph glyph-name="picture" unicode="&#xe800;" d="M357 529q0-45-31-76t-76-32-76 32-31 76 31 76 76 31 76-31 31-76z m572-215v-250h-786v107l178 179 90-89 285 285z m53 393h-893q-7 0-12-5t-6-13v-678q0-7 6-13t12-5h893q7 0 13 5t5 13v678q0 8-5 13t-13 5z m89-18v-678q0-37-26-63t-63-27h-893q-36 0-63 27t-26 63v678q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
 
-<glyph glyph-name="cancel" unicode="&#xe801;" d="M452 194q18-18 18-43t-18-43q-18-16-43-16t-43 16l-132 152-132-152q-18-16-43-16t-43 16q-16 18-16 43t16 43l138 156-138 158q-16 18-16 43t16 43q18 16 43 16t43-16l132-152 132 152q18 16 43 16t43-16q18-18 18-43t-18-43l-138-158z" horiz-adv-x="470" />
+<glyph glyph-name="clock" unicode="&#xe801;" d="M460 810q190 0 325-135t135-325-135-325-325-135-325 135-135 325 135 325 325 135z m0-820q150 0 255 106t105 254q0 150-105 255t-255 105q-148 0-254-105t-106-255q0-148 106-254t254-106z m36 620l0-244 150-150-50-50-170 170 0 274 70 0z" horiz-adv-x="920" />
 
-<glyph glyph-name="font" unicode="&#xe802;" d="M405 538l-95-251q18 0 76-1t89-1q11 0 32 1-48 141-102 252z m-405-617l1 44q13 4 31 7t32 6 28 8 25 17 17 28l132 344 156 404h72q4-8 6-12l114-268q19-43 60-144t63-153q9-19 33-80t40-94q11-26 19-32 11-9 49-17t47-11q4-22 4-32 0-3-1-8t0-7q-35 0-106 5t-107 4q-42 0-120-4t-99-4q0 24 2 43l73 16q1 0 7 1t9 2 8 3 9 4 6 4 5 6 1 8q0 9-17 54t-40 99-24 56l-251 1q-14-32-43-109t-28-91q0-12 8-21t24-14 27-7 32-5 23-2q1-11 1-32 0-5-1-16-33 0-98 6t-97 6q-5 0-15-3t-12-2q-45-8-105-8z" horiz-adv-x="928.6" />
+<glyph glyph-name="cancel" unicode="&#xe802;" d="M724 112q0-22-15-38l-76-76q-16-15-38-15t-38 15l-164 165-164-165q-16-15-38-15t-38 15l-76 76q-16 16-16 38t16 38l164 164-164 164q-16 16-16 38t16 38l76 76q16 16 38 16t38-16l164-164 164 164q16 16 38 16t38-16l76-76q15-15 15-38t-15-38l-164-164 164-164q15-15 15-38z" horiz-adv-x="785.7" />
 
-<glyph glyph-name="cog" unicode="&#xe803;" d="M571 350q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 51-20l79 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q15-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" />
+<glyph glyph-name="plus" unicode="&#xe803;" d="M786 439v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" />
 
-<glyph glyph-name="off" unicode="&#xe804;" d="M857 350q0-87-34-166t-91-137-137-92-166-34-167 34-136 92-92 137-34 166q0 102 45 191t126 151q24 18 54 14t46-28q18-23 14-53t-28-47q-54-41-84-101t-30-127q0-58 23-111t61-91 91-61 111-23 110 23 92 61 61 91 22 111q0 68-30 127t-84 101q-23 18-28 47t14 53q17 24 47 28t53-14q81-61 126-151t45-191z m-357 429v-358q0-29-21-50t-50-21-51 21-21 50v358q0 29 21 50t51 21 50-21 21-50z" horiz-adv-x="857.1" />
+<glyph glyph-name="home" unicode="&#xe804;" d="M786 296v-267q0-15-11-25t-25-11h-214v214h-143v-214h-214q-15 0-25 11t-11 25v267q0 1 0 2t0 2l321 264 321-264q1-1 1-4z m124 39l-34-41q-5-5-12-6h-2q-7 0-12 3l-386 322-386-322q-7-4-13-3-7 1-12 6l-35 41q-4 6-3 13t6 12l401 334q18 15 42 15t43-15l136-113v108q0 8 5 13t13 5h107q8 0 13-5t5-13v-227l122-102q6-4 6-12t-4-13z" horiz-adv-x="928.6" />
 
-<glyph glyph-name="home" unicode="&#xe805;" d="M786 296v-267q0-15-11-25t-25-11h-214v214h-143v-214h-214q-15 0-25 11t-11 25v267q0 1 0 2t0 2l321 264 321-264q1-1 1-4z m124 39l-34-41q-5-5-12-6h-2q-7 0-12 3l-386 322-386-322q-7-4-13-3-7 1-12 6l-35 41q-4 6-3 13t6 12l401 334q18 15 42 15t43-15l136-113v108q0 8 5 13t13 5h107q8 0 13-5t5-13v-227l122-102q6-4 6-12t-4-13z" horiz-adv-x="928.6" />
+<glyph glyph-name="link" unicode="&#xe805;" d="M813 171q0 23-16 38l-116 116q-16 16-38 16-24 0-40-18 1-1 10-10t12-12 9-11 7-14 2-15q0-23-16-38t-38-16q-8 0-15 2t-14 7-11 9-12 12-10 10q-19-17-19-40 0-23 16-38l115-116q15-15 38-15 22 0 38 15l82 81q16 16 16 37z m-393 394q0 22-15 38l-115 115q-16 16-38 16-22 0-38-15l-82-82q-16-15-16-37 0-22 16-38l116-116q15-15 38-15 23 0 40 17-2 2-11 11t-12 12-8 10-7 14-2 16q0 22 15 38t38 15q9 0 16-2t14-7 11-8 12-12 10-11q18 17 18 41z m500-394q0-66-48-113l-82-81q-46-47-113-47-68 0-114 48l-115 115q-46 47-46 114 0 68 49 116l-49 49q-48-49-116-49-67 0-114 47l-116 116q-47 47-47 114t47 113l82 82q47 46 114 46 67 0 114-47l115-116q46-46 46-113 0-69-49-117l49-49q48 49 116 49 67 0 114-47l116-116q47-47 47-114z" horiz-adv-x="928.6" />
 
-<glyph glyph-name="plus" unicode="&#xe806;" d="M786 439v-107q0-22-16-38t-38-15h-232v-233q0-22-16-37t-38-16h-107q-22 0-38 16t-15 37v233h-232q-23 0-38 15t-16 38v107q0 23 16 38t38 16h232v232q0 22 15 38t38 16h107q23 0 38-16t16-38v-232h232q23 0 38-16t16-38z" horiz-adv-x="785.7" />
+<glyph glyph-name="cog" unicode="&#xe806;" d="M571 350q0 59-41 101t-101 42-101-42-42-101 42-101 101-42 101 42 41 101z m286 61v-124q0-7-4-13t-11-7l-104-16q-10-30-21-51 19-27 59-77 6-6 6-13t-5-13q-15-21-55-61t-53-39q-7 0-14 5l-77 60q-25-13-51-21-9-76-16-104-4-16-20-16h-124q-8 0-14 5t-6 12l-16 103q-27 9-50 21l-79-60q-6-5-14-5-8 0-14 6-70 64-92 94-4 5-4 13 0 6 5 12 8 12 28 37t30 40q-15 28-23 55l-102 15q-7 1-11 7t-5 13v124q0 7 5 13t10 7l104 16q8 25 22 51-23 32-60 77-6 7-6 14 0 5 5 12 15 20 55 60t53 40q7 0 15-5l77-60q24 13 50 21 9 76 17 104 3 16 20 16h124q7 0 13-5t7-12l15-103q28-9 51-20l79 59q5 5 13 5 7 0 14-5 72-67 92-95 4-5 4-12 0-7-4-13-9-12-29-37t-30-40q15-28 23-54l102-16q7-1 12-7t4-13z" horiz-adv-x="857.1" />
 
-<glyph glyph-name="video" unicode="&#xe807;" d="M214-43v72q0 14-10 25t-25 10h-72q-14 0-25-10t-11-25v-72q0-14 11-25t25-11h72q14 0 25 11t10 25z m0 214v72q0 14-10 25t-25 11h-72q-14 0-25-11t-11-25v-72q0-14 11-25t25-10h72q14 0 25 10t10 25z m0 215v71q0 15-10 25t-25 11h-72q-14 0-25-11t-11-25v-71q0-15 11-25t25-11h72q14 0 25 11t10 25z m572-429v286q0 14-11 25t-25 11h-429q-14 0-25-11t-10-25v-286q0-14 10-25t25-11h429q15 0 25 11t11 25z m-572 643v71q0 15-10 26t-25 10h-72q-14 0-25-10t-11-26v-71q0-14 11-25t25-11h72q14 0 25 11t10 25z m786-643v72q0 14-11 25t-25 10h-71q-15 0-25-10t-11-25v-72q0-14 11-25t25-11h71q15 0 25 11t11 25z m-214 429v285q0 15-11 26t-25 10h-429q-14 0-25-10t-10-26v-285q0-15 10-25t25-11h429q15 0 25 11t11 25z m214-215v72q0 14-11 25t-25 11h-71q-15 0-25-11t-11-25v-72q0-14 11-25t25-10h71q15 0 25 10t11 25z m0 215v71q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-71q0-15 11-25t25-11h71q15 0 25 11t11 25z m0 214v71q0 15-11 26t-25 10h-71q-15 0-25-10t-11-26v-71q0-14 11-25t25-11h71q15 0 25 11t11 25z m71 89v-750q0-37-26-63t-63-26h-893q-36 0-63 26t-26 63v750q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
+<glyph glyph-name="font" unicode="&#xe807;" d="M405 538l-95-251q18 0 76-1t89-1q11 0 32 1-48 141-102 252z m-405-617l1 44q13 4 31 7t32 6 28 8 25 17 17 28l132 344 156 404h72q4-8 6-12l114-268q19-43 60-144t63-153q9-19 33-80t40-94q11-26 19-32 11-9 49-17t47-11q4-22 4-32 0-3-1-8t0-7q-35 0-106 5t-107 4q-42 0-120-4t-99-4q0 24 2 43l73 16q1 0 7 1t9 2 8 3 9 4 6 4 5 6 1 8q0 9-17 54t-40 99-24 56l-251 1q-14-32-43-109t-28-91q0-12 8-21t24-14 27-7 32-5 23-2q1-11 1-32 0-5-1-16-33 0-98 6t-97 6q-5 0-15-3t-12-2q-45-8-105-8z" horiz-adv-x="928.6" />
 
-<glyph glyph-name="bold" unicode="&#xe808;" d="M310 1q41-18 78-18 210 0 210 187 0 64-23 101-15 24-34 41t-38 26-45 14-47 6-53 1q-40 0-56-6 0-29 0-88t-1-88q0-5 0-38t0-54 2-47 7-37z m-8 417q23-4 61-4 46 0 80 7t61 25 42 50 14 79q0 39-16 68t-45 46-60 24-69 8q-28 0-73-7 0-28 3-84t2-85q0-15 0-45t-1-44q0-26 1-38z m-302-497l1 53q9 2 48 9t59 15q4 7 7 15t4 19 4 18 1 21 0 19v36q0 548-12 572-2 5-12 8t-25 6-28 4-27 3-17 2l-2 46q55 1 190 6t208 6q13 0 38-1t38 0q39 0 76-7t72-24 60-39 41-59 16-76q0-29-9-54t-22-40-36-32-41-25-47-22q86-20 144-75t57-138q0-56-20-101t-52-72-77-48-91-27-98-8q-25 0-74 2t-74 1q-59 0-171-6t-129-7z" horiz-adv-x="785.7" />
+<glyph glyph-name="italic" unicode="&#xe808;" d="M0-78l10 48q12 4 34 9t40 11 33 13q16 19 23 56 1 4 35 162t63 303 29 165v14q-13 8-30 11t-39 4-32 3l10 58q19-1 67-4t84-4 67-1q27 0 55 1t68 4 54 4q-2-22-10-50-17-6-57-16t-60-19q-5-10-8-23t-5-23-4-25-4-24q-15-82-49-234t-43-198q-1-5-7-32t-11-51-9-46-4-32l1-10q9-3 103-18-2-24-9-55-6 0-18-1t-18-1q-16 0-49 6t-48 6q-77 1-115 1-28 0-79-5t-68-7z" horiz-adv-x="571.4" />
 
-<glyph glyph-name="italic" unicode="&#xe809;" d="M0-78l10 48q12 4 34 9t40 11 33 13q16 19 23 56 1 4 35 162t63 303 29 165v14q-13 8-30 11t-39 4-32 3l10 58q19-1 67-4t84-4 67-1q27 0 55 1t68 4 54 4q-2-22-10-50-17-6-57-16t-60-19q-5-10-8-23t-5-23-4-25-4-24q-15-82-49-234t-43-198q-1-5-7-32t-11-51-9-46-4-32l1-10q9-3 103-18-2-24-9-55-6 0-18-1t-18-1q-16 0-49 6t-48 6q-77 1-115 1-28 0-79-5t-68-7z" horiz-adv-x="571.4" />
+<glyph glyph-name="bold" unicode="&#xe809;" d="M310 1q41-18 78-18 210 0 210 187 0 64-23 101-15 24-34 41t-38 26-45 14-47 6-53 1q-40 0-56-6 0-29 0-88t-1-88q0-5 0-38t0-54 2-47 7-37z m-8 417q23-4 61-4 46 0 80 7t61 25 42 50 14 79q0 39-16 68t-45 46-60 24-69 8q-28 0-73-7 0-28 3-84t2-85q0-15 0-45t-1-44q0-26 1-38z m-302-497l1 53q9 2 48 9t59 15q4 7 7 15t4 19 4 18 1 21 0 19v36q0 548-12 572-2 5-12 8t-25 6-28 4-27 3-17 2l-2 46q55 1 190 6t208 6q13 0 38-1t38 0q39 0 76-7t72-24 60-39 41-59 16-76q0-29-9-54t-22-40-36-32-41-25-47-22q86-20 144-75t57-138q0-56-20-101t-52-72-77-48-91-27-98-8q-25 0-74 2t-74 1q-59 0-171-6t-129-7z" horiz-adv-x="785.7" />
+
+<glyph glyph-name="off" unicode="&#xe80a;" d="M857 350q0-87-34-166t-91-137-137-92-166-34-167 34-136 92-92 137-34 166q0 102 45 191t126 151q24 18 54 14t46-28q18-23 14-53t-28-47q-54-41-84-101t-30-127q0-58 23-111t61-91 91-61 111-23 110 23 92 61 61 91 22 111q0 68-30 127t-84 101q-23 18-28 47t14 53q17 24 47 28t53-14q81-61 126-151t45-191z m-357 429v-358q0-29-21-50t-50-21-51 21-21 50v358q0 29 21 50t51 21 50-21 21-50z" horiz-adv-x="857.1" />
+
+<glyph glyph-name="video" unicode="&#xe80b;" d="M214-43v72q0 14-10 25t-25 10h-72q-14 0-25-10t-11-25v-72q0-14 11-25t25-11h72q14 0 25 11t10 25z m0 214v72q0 14-10 25t-25 11h-72q-14 0-25-11t-11-25v-72q0-14 11-25t25-10h72q14 0 25 10t10 25z m0 215v71q0 15-10 25t-25 11h-72q-14 0-25-11t-11-25v-71q0-15 11-25t25-11h72q14 0 25 11t10 25z m572-429v286q0 14-11 25t-25 11h-429q-14 0-25-11t-10-25v-286q0-14 10-25t25-11h429q15 0 25 11t11 25z m-572 643v71q0 15-10 26t-25 10h-72q-14 0-25-10t-11-26v-71q0-14 11-25t25-11h72q14 0 25 11t10 25z m786-643v72q0 14-11 25t-25 10h-71q-15 0-25-10t-11-25v-72q0-14 11-25t25-11h71q15 0 25 11t11 25z m-214 429v285q0 15-11 26t-25 10h-429q-14 0-25-10t-10-26v-285q0-15 10-25t25-11h429q15 0 25 11t11 25z m214-215v72q0 14-11 25t-25 11h-71q-15 0-25-11t-11-25v-72q0-14 11-25t25-10h71q15 0 25 10t11 25z m0 215v71q0 15-11 25t-25 11h-71q-15 0-25-11t-11-25v-71q0-15 11-25t25-11h71q15 0 25 11t11 25z m0 214v71q0 15-11 26t-25 10h-71q-15 0-25-10t-11-26v-71q0-14 11-25t25-11h71q15 0 25 11t11 25z m71 89v-750q0-37-26-63t-63-26h-893q-36 0-63 26t-26 63v750q0 37 26 63t63 27h893q37 0 63-27t26-63z" horiz-adv-x="1071.4" />
 
 <glyph glyph-name="math" unicode="&#xf01a;" d="M0 472q0 31 24 54t57 22h215l164-249 89 519h306q34 0 58-22t24-54-24-55-58-22h-170l-136-784-343 516h-125q-33 0-57 22t-24 53z" horiz-adv-x="937.5" />
 
@@ -32,14 +36,10 @@
 
 <glyph glyph-name="link-ext" unicode="&#xf08e;" d="M786 332v-178q0-67-47-114t-114-47h-464q-67 0-114 47t-47 114v464q0 66 47 113t114 48h393q7 0 12-5t5-13v-36q0-8-5-13t-12-5h-393q-37 0-63-26t-27-63v-464q0-37 27-63t63-27h464q37 0 63 27t26 63v178q0 8 5 13t13 5h36q8 0 13-5t5-13z m214 482v-285q0-15-11-25t-25-11-25 11l-98 98-364-364q-5-6-13-6t-12 6l-64 64q-6 5-6 12t6 13l364 364-98 98q-11 11-11 25t11 25 25 11h285q15 0 25-11t11-25z" horiz-adv-x="1000" />
 
-<glyph glyph-name="resize-full-alt" unicode="&#xf0b2;" d="M716 548l-198-198 198-198 80 80q17 18 39 8 22-9 22-33v-250q0-14-10-25t-26-11h-250q-23 0-32 23-10 21 7 38l81 81-198 198-198-198 80-81q17-17 8-38-10-23-33-23h-250q-15 0-25 11t-11 25v250q0 24 22 33 22 10 39-8l80-80 198 198-198 198-80-80q-11-11-25-11-7 0-14 3-22 9-22 33v250q0 14 11 25t25 11h250q23 0 33-23 9-21-8-38l-80-81 198-198 198 198-81 81q-17 17-7 38 9 23 32 23h250q15 0 26-11t10-25v-250q0-24-22-33-7-3-14-3-14 0-25 11z" horiz-adv-x="857.1" />
-
 <glyph glyph-name="list-bullet" unicode="&#xf0ca;" d="M214 64q0-44-31-76t-76-31-76 31-31 76 31 76 76 31 76-31 31-76z m0 286q0-45-31-76t-76-31-76 31-31 76 31 76 76 31 76-31 31-76z m786-232v-107q0-7-5-13t-13-5h-678q-8 0-13 5t-5 13v107q0 7 5 12t13 6h678q7 0 13-6t5-12z m-786 518q0-45-31-76t-76-31-76 31-31 76 31 76 76 31 76-31 31-76z m786-232v-108q0-7-5-12t-13-5h-678q-8 0-13 5t-5 12v108q0 7 5 12t13 5h678q7 0 13-5t5-12z m0 285v-107q0-7-5-12t-13-6h-678q-8 0-13 6t-5 12v107q0 8 5 13t13 5h678q7 0 13-5t5-13z" horiz-adv-x="1000" />
 
 <glyph glyph-name="list-numbered" unicode="&#xf0cb;" d="M213-54q0-45-31-70t-75-26q-60 0-96 37l31 49q28-25 60-25 16 0 28 8t12 24q0 35-59 31l-14 31q4 6 18 24t24 31 20 21v1q-9 0-27-1t-27 0v-30h-59v85h186v-49l-53-65q28-6 45-27t17-49z m1 350v-89h-202q-4 20-4 30 0 29 14 52t31 38 37 27 31 24 14 25q0 14-9 22t-22 7q-25 0-45-32l-47 33q13 28 40 44t59 16q40 0 68-23t28-63q0-28-19-51t-42-36-42-28-20-30h71v34h59z m786-178v-107q0-7-5-13t-13-5h-678q-8 0-13 5t-5 13v107q0 8 5 13t13 5h678q7 0 13-6t5-12z m-786 502v-56h-187v56h60q0 22 0 67t1 68v7h-1q-5-10-28-30l-40 42 76 71h59v-225h60z m786-216v-108q0-7-5-12t-13-5h-678q-8 0-13 5t-5 12v108q0 7 5 12t13 5h678q7 0 13-5t5-12z m0 285v-107q0-7-5-12t-13-6h-678q-8 0-13 6t-5 12v107q0 8 5 13t13 5h678q7 0 13-5t5-13z" horiz-adv-x="1000" />
 
-<glyph glyph-name="strike" unicode="&#xf0cc;" d="M982 350q8 0 13-5t5-13v-36q0-7-5-12t-13-5h-964q-8 0-13 5t-5 12v36q0 8 5 13t13 5h964z m-712 36q-16 19-29 44-27 55-27 105 0 101 75 173 74 71 219 71 28 0 94-11 36-7 98-27 6-21 12-66 8-68 8-102 0-10-3-25l-7-2-46 4-8 1q-28 83-58 114-49 51-117 51-64 0-101-33-38-32-38-81 0-41 37-78t156-72q38-12 96-37 33-16 53-29h-414z m283-143h229q4-22 4-51 0-62-23-119-13-31-40-58-20-19-61-45-44-27-85-37-45-12-113-12-64 0-109 13l-78 23q-32 8-40 15-5 5-5 12v8q0 60-1 87 0 17 0 38l1 20v25l57 1q8-19 17-40t12-31 7-15q20-32 45-52 24-20 59-32 33-12 73-12 36 0 78 15 43 14 68 48 26 34 26 72 0 47-45 87-19 16-76 40z" horiz-adv-x="1000" />
-
 <glyph glyph-name="underline" unicode="&#xf0cd;" d="M27 726q-21 1-25 2l-2 49q7 1 22 1 34 0 63-3 74-4 93-4 47 0 93 2 65 2 82 3 31 0 48 1l-1-8 1-36v-5q-33-5-69-5-33 0-44-14-7-7-7-73 0-7 0-18t0-15l1-127 8-157q3-69 28-112 20-33 54-52 49-26 98-26 59 0 107 16 31 10 55 28 27 20 37 36 20 31 29 63 12 41 12 128 0 44-2 72t-6 68-8 89l-2 33q-3 37-13 49-19 20-43 19l-56-1-8 2 1 48h47l114-6q43-2 110 6l10-1q3-22 3-29 0-4-2-17-25-7-47-8-41-6-44-9-8-8-8-23 0-4 0-15t1-17q5-11 13-221 3-109-9-170-8-42-23-68-21-36-62-69-42-31-102-49-61-19-142-19-93 0-159 26-66 26-99 68-34 42-47 109-9 45-9 132v186q0 105-9 119-14 20-82 22z m830-787v36q0 8-5 13t-13 5h-821q-8 0-13-5t-5-13v-36q0-8 5-13t13-5h821q8 0 13 5t5 13z" horiz-adv-x="857.1" />
 
 <glyph glyph-name="table" unicode="&#xf0ce;" d="M286 82v107q0 8-5 13t-13 5h-179q-7 0-12-5t-6-13v-107q0-8 6-13t12-5h179q8 0 13 5t5 13z m0 214v108q0 7-5 12t-13 5h-179q-7 0-12-5t-6-12v-108q0-7 6-12t12-5h179q8 0 13 5t5 12z m285-214v107q0 8-5 13t-12 5h-179q-8 0-13-5t-5-13v-107q0-8 5-13t13-5h179q7 0 12 5t5 13z m-285 429v107q0 8-5 13t-13 5h-179q-7 0-12-5t-6-13v-107q0-8 6-13t12-5h179q8 0 13 5t5 13z m285-215v108q0 7-5 12t-12 5h-179q-8 0-13-5t-5-12v-108q0-7 5-12t13-5h179q7 0 12 5t5 12z m286-214v107q0 8-5 13t-13 5h-178q-8 0-13-5t-5-13v-107q0-8 5-13t13-5h178q8 0 13 5t5 13z m-286 429v107q0 8-5 13t-12 5h-179q-8 0-13-5t-5-13v-107q0-8 5-13t13-5h179q7 0 12 5t5 13z m286-215v108q0 7-5 12t-13 5h-178q-8 0-13-5t-5-12v-108q0-7 5-12t13-5h178q8 0 13 5t5 12z m0 215v107q0 8-5 13t-13 5h-178q-8 0-13-5t-5-13v-107q0-8 5-13t13-5h178q8 0 13 5t5 13z m72 178v-607q0-37-27-63t-63-26h-750q-36 0-63 26t-26 63v607q0 37 26 63t63 27h750q37 0 63-27t27-63z" horiz-adv-x="928.6" />

TEMPAT SAMPAH
system/author/css/fontello/font/fontello.ttf


TEMPAT SAMPAH
system/author/css/fontello/font/fontello.woff


TEMPAT SAMPAH
system/author/css/fontello/font/fontello.woff2


+ 143 - 72
system/author/css/style.css

@@ -1323,8 +1323,8 @@ label .help, .label .help{
 }
 .editor button.button--secondary[disabled],
 .editor button.button--secondary:disabled{
-	border: 1px solid #eee; 
-	background: #eee;
+	border: 1px solid #f9f8f6; 
+	background: #f9f8f6;
 	color: #444;
 	cursor: default;
 }
@@ -1349,8 +1349,21 @@ label .help, .label .help{
 ****************/
 
 .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;
@@ -1360,82 +1373,55 @@ label .help, .label .help{
 	margin-top: 12px;
 	margin-bottom: 12px;
 }
-.blox-editor{
-	position: relative;
-}
 .blox:hover{
 	background: #f9f8f6;
 }
+.blox-editor{
+	position: relative;
+}
 .blox-buttons{
 	position: absolute;
     bottom: -15px;
-    left: 15px;
-    z-index: 9;
-	left: 15px;
+	text-align: right;
+    right: 25px;
     width: 200px;
 	z-index: 99;
 }
-.blox-editor button{
+.blox-buttons button{
 	display: inline-block;
 	box-sizing: border-box;
-	margin: 1px;
+	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-editor button.edit{
-	background: #fff;
-	color: #444;
-	border: 1px solid #bbb;
-}
-.blox-editor button.edit:hover{
-	background: #cc4146;
+.blox-buttons button.edit:hover{
+	background: #70c1b3;
 	color: #eee;
-	border: 1px solid #cc4146;
-}
-.blox-editor button.cancel{
-	background: #fff;
-	color: #444;	
-	border: 1px solid #bbb;
 }
-.blox-editor button.cancel:hover{
+.blox-buttons button.cancel:hover{
 	background: #e0474c;
-	border: 1px solid #e0474c;
 	color: #eee;
 }
-.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.mdcontent, .blox-editor input.mdcontent, .blox-editor .video.dropbox, .blox-editor .dropbox p{
-	border-left: 40px solid rgba(255,255,255,0.5);
-}
-*/
-.blox-editor input.mdcontent{
-	font-size: 1.4em;
-	font-weight: 700;
-}
-.blox-editor .contenttype {
-	position: absolute;
-	top: 16px;
-	left: -25px;
-	color: #e0474c;
+.blox-buttons button.edit:disabled, .blox-buttons button.cancel:disabled{
+	background: #eee;
+	color: #444;
+	border: 1px solid #eee;
 }
 
-.blox-editor .sideaction{
+
+.sideaction{
 	position: absolute;
-	right: -22px;
 	top: 0px;
+	font-size: 0.8em;
+	right: -22px;
 }
-.blox-editor button.delete, .blox-editor .icon-resize-full-alt{
+.sideaction button{
 	display: block;
 	font-weight: 300;
 	font-size: 0.9em;
@@ -1446,22 +1432,54 @@ label .help, .label .help{
 	line-height: 20px;
 	text-align: center;
 	padding: 0px;
-	margin: 0px 0px 1px;
+	margin: 1px;
 	border: 0px;
 	border-radius: 1px;
 }
-.blox-editor:hover button.delete{
+.blox-wrapper{
+	position: relative;
+}
+.editactive .sideaction button, 
+.blox-wrapper:hover button{
+	background-color: #f9f8f6;
+	color: #666;
+}
+.sideaction:hover ~ .background-helper {
+    background-color: #f9f8f6;
+}
+.blox-wrapper button.add:hover{
+	background: #66b0a3;
+	color: #fff;
+}
+.blox-wrapper button.delete:hover{
 	background: #e0474c;
 	color: #fff;
 }
-.blox-editor button.delete:hover{
-	cursor: pointer;
-	background: #cc4146;
+
+
+
+.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 button.edit:disabled, .blox-editor button.cancel:disabled{
-	background: #eee;
-	color: #444;
-	border: 1px solid #eee;
+.blox-editor textarea:focus, .blox-editor input:focus{
+	box-shadow: none;
+	outline: none;
+}
+.blox-editor input.mdcontent{
+	font-size: 1.4em;
+	font-weight: 700;
+}
+.blox-editor .contenttype {
+	position: absolute;
+	top: 15px;
+	left: -25px;
+	color: #666;
 }
 .visible{
 	display: block;
@@ -1477,18 +1495,15 @@ label .help, .label .help{
 	width: 100%;
 	z-index:9;
 }
+
+
+/* .format-bar at the bottom of the page */
 .format-bar .hidden{
 	display: none;
 }
 .format-bar .component{
 	position: relative;
 }
-.fade-editor-enter-active{
-  transition: opacity .5s;
-}
-.fade-editor-enter{
-  opacity: 0.3;
-}
 .format-bar{
 	padding: 20px;
 	width:100%;
@@ -1498,16 +1513,57 @@ label .help, .label .help{
 	margin-left: -20px;
 	margin-right: 20px;
 }
-.format-bar .blox-editor{
-/*	display: inline; */
+.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-right: 2px;
 	padding: 5px;
-	background: #70c1b3;
-	color: #fff;
+	background: #f9f8f6;
+	border: 1px solid #eee;
+	color: #444;
 	display: inline;
-	border: 0px;
 	border-radius: 2px;
 	width: 40px;
 	height: 40px;
@@ -1515,8 +1571,23 @@ button.format-item{
 }
 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 **
@@ -1525,7 +1596,7 @@ button.format-item:hover{
 .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; margin: 1.2em 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; }

+ 54 - 0
system/author/editor/editor-blox-orig.twig

@@ -0,0 +1,54 @@
+{% extends 'layouts/layoutBlox.twig' %}
+{% block title %}Visual Content Editor{% endblock %}
+
+{% block content %}
+	
+	<div class="formWrapper">
+		
+		<section id="blox">
+			
+			<div class="blox-body">
+			
+				<content-block class="title" :body="false">
+					<div class="blox title" @click.prevent="setData( $event )" data-id="0" id="blox-0">{{ title }}</div>
+				</content-block>
+
+				<div id="sortblox">
+				
+					{% for id, block in content %}
+						<content-block :body="true">
+							<div class="blox" @click.prevent="setData( $event )" data-id="{{ id }}" id="blox-{{id}}">{{block}}</div>
+						</content-block>
+					{% endfor %}
+
+					<content-block :body="true" v-for="newBlock in newBlocks"><div class="blox" @click.prevent="setData( $event )" :data-id="newBlock.id" :id="newBlock.blockId" v-html="newBlock.content"></div></content-block>
+				
+				</div>
+				
+				<div class="format-bar">
+					<content-block :body="false">
+						<button class="format-item" @click.prevent="setData( $event, 'markdown-component' )" data-id="99999" id="blox-99999"><i class="icon-paragraph"></i></button>
+						<button class="format-item" @click.prevent="setData( $event, 'headline-component' )" data-id="99999" id="blox-99999"><i class="icon-header"></i></button>
+						<button class="format-item" @click.prevent="setData( $event, 'ulist-component' )" data-id="99999" id="blox-99999"><i class="icon-list-bullet"></i></button>
+						<button class="format-item" @click.prevent="setData( $event, 'olist-component' )" data-id="99999" id="blox-99999"><i class="icon-list-numbered"></i></button>
+						<button class="format-item" @click.prevent="setData( $event, 'image-component' )" data-id="99999" id="blox-99999"><i class="icon-picture"></i></button>
+						<button class="format-item" @click.prevent="setData( $event, 'video-component' )" data-id="99999" id="blox-99999"><i class="icon-youtube-play"></i></button>
+						<button class="format-item" @click.prevent="setData( $event, 'table-component' )" data-id="99999" id="blox-99999"><i class="icon-table"></i></button>
+						<button class="format-item" @click.prevent="setData( $event, 'quote-component' )" data-id="99999" id="blox-99999"><i class="icon-quote-left"></i></button>
+						<button class="format-item" @click.prevent="setData( $event, 'code-component' )" data-id="99999" id="blox-99999"><i class="icon-code"></i></button>
+				<!--	<button class="format-item" @click.prevent="setData( $event, 'math-component' )" data-id="99999" id="blox-99999"><i class="icon-math"></i></button>  -->
+					</content-block>
+				</div>
+				
+			</div>
+			
+		</section>
+
+		{% include 'editor/publish-controller.twig' %}
+				
+		<input id="path" type="hidden" value="{{ item.urlRel }}" required readonly />
+		{{ csrf_field() | raw }}
+		
+	</div>
+	
+{% endblock %}

+ 31 - 12
system/author/editor/editor-blox.twig

@@ -9,22 +9,41 @@
 			
 			<div class="blox-body">
 			
-				<content-block class="title" :body="false">
-					<div class="blox title" @click.prevent="setData( $event )" data-id="0" id="blox-0">{{ title }}</div>
-				</content-block>
-
-				<div id="sortblox">
+				<transition name="fade">
+					<div v-if="bloxOverlay" class="blox-overlay"></div>
+				</transition>
 				
-					{% for id, block in content %}
-						<content-block :body="true">
-							<div class="blox" @click.prevent="setData( $event )" data-id="{{ id }}" id="blox-{{id}}">{{block}}</div>
-						</content-block>
+				<div id="initial-content">
+				
+					<div class="blox title">{{title}}</div>
+					
+					{% for block in content %}
+							<div class="blox" >{{block}}</div>
 					{% endfor %}
-
-					<content-block :body="true" v-for="newBlock in newBlocks"><div class="blox" @click.prevent="setData( $event )" :data-id="newBlock.id" :id="newBlock.blockId" v-html="newBlock.content"></div></content-block>
 				
 				</div>
 				
+				<content-block class="title" :body="false">
+					<div class="blox title" @click.prevent="setData( $event )" data-id="0" id="blox-0" v-if="title" v-html="title"></div>
+				</content-block>
+				
+				<div id="sortblox" v-cloak>
+					<content-block :body="true" v-for="(block,index) in html" v-if="index !== 0">
+						<div v-if="block" class="blox" @click.prevent="setData( $event )" :data-id="index" :id="'blox-' + index" v-html="block"></div>
+						<div v-else class="format-bar blox" @click.prevent="clearData( $event )" :data-id="index" :id="'blox-' + index">
+							<button class="format-item" @click.prevent="setData( $event, 'markdown-component' )" :data-id="index" :id="'blox-' + index"><i class="icon-paragraph"></i></button>
+							<button class="format-item" @click.prevent="setData( $event, 'headline-component' )" :data-id="index" :id="'blox-' + index"><i class="icon-header"></i></button>
+							<button class="format-item" @click.prevent="setData( $event, 'ulist-component' )" :data-id="index" :id="'blox-' + index"><i class="icon-list-bullet"></i></button>
+							<button class="format-item" @click.prevent="setData( $event, 'olist-component' )" :data-id="index" :id="'blox-' + index"><i class="icon-list-numbered"></i></button>
+							<button class="format-item" @click.prevent="setData( $event, 'image-component' )" :data-id="index" :id="'blox-' + index"><i class="icon-picture"></i></button>
+							<button class="format-item" @click.prevent="setData( $event, 'video-component' )" :data-id="index" :id="'blox-' + index"><i class="icon-youtube-play"></i></button>
+							<button class="format-item" @click.prevent="setData( $event, 'table-component' )" :data-id="index" :id="'blox-' + index"><i class="icon-table"></i></button>
+							<button class="format-item" @click.prevent="setData( $event, 'quote-component' )" :data-id="index" :id="'blox-' + index"><i class="icon-quote-left"></i></button>
+							<button class="format-item" @click.prevent="setData( $event, 'code-component' )" :data-id="index" :id="'blox-' + index"><i class="icon-code"></i></button>
+						</div>
+					</content-block>
+				</div>
+	
 				<div class="format-bar">
 					<content-block :body="false">
 						<button class="format-item" @click.prevent="setData( $event, 'markdown-component' )" data-id="99999" id="blox-99999"><i class="icon-paragraph"></i></button>
@@ -36,7 +55,7 @@
 						<button class="format-item" @click.prevent="setData( $event, 'table-component' )" data-id="99999" id="blox-99999"><i class="icon-table"></i></button>
 						<button class="format-item" @click.prevent="setData( $event, 'quote-component' )" data-id="99999" id="blox-99999"><i class="icon-quote-left"></i></button>
 						<button class="format-item" @click.prevent="setData( $event, 'code-component' )" data-id="99999" id="blox-99999"><i class="icon-code"></i></button>
-				<!--	<button class="format-item" @click.prevent="setData( $event, 'code-component' )" data-id="99999" id="blox-99999"><i class="icon-math"></i></button>  -->
+				<!--	<button class="format-item" @click.prevent="setData( $event, 'math-component' )" data-id="99999" id="blox-99999"><i class="icon-math"></i></button>  -->
 					</content-block>
 				</div>
 				

+ 1312 - 0
system/author/js/vue-blox-inline.js

@@ -0,0 +1,1312 @@
+const eventBus = new Vue();
+
+const contentComponent = Vue.component('content-block', {
+	props: ['body', 'load'],
+	template: '<div ref="bloxcomponent" class="blox-editor">' +
+				'<div class="blox-wrapper" :class="{ editactive: edit }">' +
+				 '<div class="sideaction" v-if="body">' + 
+				  '<button class="add" :disabled="disabled" title="add content-block" @click.prevent="showFormats($event)"><i class="icon-plus"></i></button>' +
+				  '<button class="delete" :disabled="disabled" title="delete content-block" @click.prevent="deleteBlock($event)"><i class="icon-cancel"></i></button>' +
+				 '</div>' + 
+				 '<div class="background-helper" @keyup.enter="submitBlock" @click="getData">' +
+				  '<div class="component" ref="component">' +
+				   '<transition name="fade-editor">' +
+				    '<component :disabled="disabled" :compmarkdown="compmarkdown" @updatedMarkdown="updateMarkdown" :is="componentType"></component>' +
+				   '</transition>' +
+				   '<div class="blox-buttons" v-if="edit">' + 
+				    '<button class="edit" :disabled="disabled" @click.prevent="saveBlock">save</button>' +
+				    '<button class="cancel" :disabled="disabled" @click.prevent="switchToPreviewMode">cancel</button>' +
+				   '</div>' + 
+				  '</div>' + 
+				  '<div :class="preview" ref="preview"><slot name="default"></slot></div>' + 
+				 '</div>' +
+				'</div>' + 
+				'<div v-if="load" class="loadoverlay"><span class="load"></span></div>' +
+			  '</div>',	
+	data: function () {
+		return {
+			preview: 'visible',
+			edit: false,
+			compmarkdown: '',
+			componentType: '',
+			disabled: false,
+			load: false,
+			showformat: false,
+		}
+	},
+	mounted: function()
+	{
+		eventBus.$on('closeComponents', this.closeComponents);
+	},
+	methods: {
+		showFormats: function(event)
+		{
+			this.switchToPreviewMode();
+			this.showformat = true;
+		},
+		hideFormats: function(event)
+		{
+			this.showformat = false;
+		},
+		addBlock: function(event,blocktype)
+		{
+			this.showformat = false;
+						
+			/* 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;
+			
+			/* add kind of content you like */
+			var defaults = {
+				'paragraph': 'New paragraph',
+				'headline': '## New headline',
+				'ulist': '* New list',
+				'olist': '1. New list',
+				'image': '![Alternative Text]()',
+				'video': 'New video',
+				'table': '|head|head|\n|---|---|\n|cell|cell|',
+				'quote': '> New quote',
+				'code': '````\b New code\b````'
+			};
+						
+			/* store the content with id here */
+			this.compmarkdown = defaults[blocktype];
+			this.$root.$data.blockId = bloxid;
+			this.$root.$data.newblock = true;
+			this.saveBlock();
+		},
+		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.sortable.option("disabled",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.sortable.option("disabled",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"){ }
+			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;
+				
+				var compmarkdown = this.compmarkdown.split('\n\n').join('\n');
+				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
+						{
+							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,0,result.content);
+								self.$root.$data.markdown.splice(result.id,0,result.markdown);								
+								
+								self.$root.$data.newblock = false;
+								self.$root.$data.blockMarkdown = '';
+								self.$root.$data.blockType = '';								
+							}
+							else
+							{
+								self.$root.$data.markdown[result.id] = result.markdown;
+
+								self.$root.$data.html[result.id] = result.content;
+								document.getElementById('blox-'+result.id).innerHTML = result.content;
+								
+								self.$root.$data.blockMarkdown = '';
+								self.$root.$data.blockType = '';
+							}
+						}
+					}
+					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;
+			// bloxeditor.firstChild.id = "delete-"+bloxid;
+
+			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 = '';
+					}
+				}
+			}, method, url, params);
+		},
+	},
+})
+
+const formatComponent = Vue.component('format-component', {
+	props: ['compmarkdown', 'disabled'],
+	template: '<transition name="fade">' +
+			      '<div class="formatbuttons" v-if="showformat">' +
+					'<button class="format-item disabled" disabled>add new block:</button>' +
+					'<button class="format-item" @click.prevent="addBlock($event, \'paragraph\')" ><i class="icon-paragraph"></i></button>' +
+					'<button class="format-item" @click.prevent="addBlock($event, \'headline\')"><i class="icon-header"></i></button>' +
+					'<button class="format-item" @click.prevent="addBlock($event, \'ulist\')"><i class="icon-list-bullet"></i></button>' +
+					'<button class="format-item" @click.prevent="addBlock($event, \'olist\')"><i class="icon-list-numbered"></i></button>' +
+					'<button class="format-item" @click.prevent="addBlock($event, \'image\')"><i class="icon-picture"></i></button>' +
+					'<button class="format-item" @click.prevent="addBlock($event, \'video\')"><i class="icon-youtube-play"></i></button>' +
+					'<button class="format-item" @click.prevent="addBlock($event, \'table\')"><i class="icon-table"></i></button>' +
+					'<button class="format-item" @click.prevent="addBlock($event, \'quote\')"><i class="icon-quote-left"></i></button>' +
+					'<button class="format-item" @click.prevent="addBlock($event, \'code\')"><i class="icon-code"></i></button>' +
+					'<button class="format-item close" @click.prevent="hideFormats()">close</button>' +
+			      '</div>' +
+				'</transition>',
+	mounted: function(){
+		this.$refs.markdown.focus();
+		autosize(document.querySelectorAll('textarea'));
+	},
+	methods: {
+		updatemarkdown: function(event)
+		{
+			this.$emit('updatedMarkdown', event.target.value);
+		},
+	},
+})
+
+const titleComponent = Vue.component('title-component', {
+	props: ['compmarkdown', 'disabled'],
+	template: '<div><input type="text" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown"></div>',
+	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: '<div>' + 
+				'<div class="contenttype"><i class="icon-paragraph"></i></div>' +
+				'<textarea class="mdcontent" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown"></textarea>' + 
+				'</div>',
+	mounted: function(){
+		this.$refs.markdown.focus();
+		autosize(document.querySelectorAll('textarea'));
+	},
+	methods: {
+		updatemarkdown: function(event)
+		{
+			this.$emit('updatedMarkdown', event.target.value);
+		},
+	},
+})
+
+const codeComponent = Vue.component('code-component', {
+	props: ['compmarkdown', 'disabled'],
+	template: '<div>' + 
+				'<input type="hidden" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown" />' +	
+				'<div class="contenttype"><i class="icon-code"></i></div>' +
+				'<textarea class="mdcontent" ref="markdown" v-model="codeblock" :disabled="disabled" @input="createmarkdown"></textarea>' + 
+				'</div>',
+	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: '<div>' + 
+				'<input type="hidden" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown" />' +	
+				'<div class="contenttype"><i class="icon-quote-left"></i></div>' +
+				'<textarea class="mdcontent" ref="markdown" v-model="quote" :disabled="disabled" @input="createmarkdown"></textarea>' + 
+				'</div>',
+	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: '<div>' + 
+				'<div class="contenttype"><i class="icon-list-bullet"></i></div>' +
+				'<textarea class="mdcontent" ref="markdown" v-model="compmarkdown" :disabled="disabled" @input="updatemarkdown"></textarea>' + 
+				'</div>',
+	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: '<div>' + 
+				'<div class="contenttype"><i class="icon-list-numbered"></i></div>' +
+				'<textarea class="mdcontent" ref="markdown" v-model="compmarkdown" :disabled="disabled" @input="updatemarkdown"></textarea>' + 
+				'</div>',
+	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: '<div>' + 
+				'<div class="contenttype"><i class="icon-header"></i></div>' +
+				'<input class="mdcontent" type="text" ref="markdown" v-model="compmarkdown" :disabled="disabled" @input="updatemarkdown">' +
+				'</div>',
+	mounted: function(){
+		this.$refs.markdown.focus();
+		if(!this.compmarkdown)
+		{
+			this.compmarkdown = '## ';
+		}
+	},
+	methods: {
+		updatemarkdown: function(event)
+		{
+			this.$emit('updatedMarkdown', event.target.value);
+		},
+	},
+})
+
+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: '<div ref="table" :key="tablekey">' + 
+				'<div class="contenttype"><i class="icon-table"></i></div>' +
+				'<table ref="markdown">' +
+					'<colgroup>' +
+						'<col v-for="col in table[0]">' +
+					'</colgroup>' +
+					'<tbody>' +
+						'<tr v-for="(row, rowindex) in table">' +
+							'<td v-if="rowindex === 0" v-for="(value,colindex) in row" contenteditable="false" class="noteditable" @click="switchcolumnbar(value)">{{value}} ' +
+							  '<div v-if="columnbar === value" class="columnaction">' + 
+							     '<div class="actionline" @click="addrightcolumn(value)">add right column</div>' +
+								 '<div class="actionline" @click="addleftcolumn(value)">add left column</div>' +
+								 '<div class="actionline" @click="deletecolumn(value)">delete column</div>' +
+							  '</div>' +
+							'</td>' +
+							'<th v-if="rowindex === 1" v-for="(value,colindex) in row" :contenteditable="colindex !== 0 ? true : false" @click="switchrowbar(value)" @blur="updatedata($event,colindex,rowindex)" :class="colindex !== 0 ? editable : noteditable">' + 
+							 '<div v-if="colindex === 0 && rowbar === value" class="rowaction">' + 
+							     '<div class="actionline" @click="addaboverow(value)">add row above</div>' +
+								 '<div class="actionline" @click="addbelowrow(value)">add row below</div>' +
+								 '<div class="actionline" @click="deleterow(value)">delete row</div>' +
+							  '</div>' + 
+							'{{value}}</th>' +
+							'<td v-if="rowindex > 1" v-for="(value,colindex) in row" :contenteditable="colindex !== 0 ? true : false" @click="switchrowbar(value)" @blur="updatedata($event,colindex,rowindex)" :class="colindex !== 0 ? editable : noteditable">' + 
+							 '<div v-if="colindex === 0 && rowbar === value" class="rowaction">' + 
+							     '<div class="actionline" @click="addaboverow(value)">add row above</div>' +
+								 '<div class="actionline" @click="addbelowrow(value)">add row below</div>' +
+								 '<div class="actionline" @click="deleterow(value)">delete row</div>' +
+							  '</div>' +
+							'{{ value }}</td>' +
+						'</tr>' +
+					'</tbody>' +
+				'</table>' +
+				'</div>',
+	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 videoComponent = Vue.component('video-component', {
+	props: ['compmarkdown', 'disabled', 'load'],
+	template: '<div class="video dropbox">' +
+				'<div class="contenttype"><i class="icon-youtube-play"></i></div>' +
+				'<label for="video">Link to video: </label><input type="url" ref="markdown" placeholder="https://www.youtube.com/watch?v=" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown">' +
+				'<div v-if="load" class="loadwrapper"><span class="load"></span></div>' +
+				'</div>',
+	methods: {
+		updatemarkdown: function(event)
+		{
+			this.$emit('updatedMarkdown', event.target.value);
+		},
+	},
+})
+
+const imageComponent = Vue.component('image-component', {
+	props: ['compmarkdown', 'disabled'],
+	template: '<div class="dropbox">' +
+				'<input type="hidden" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown" />' +
+				'<input type="file" name="image" accept="image/*" class="input-file" @change="onFileChange( $event )" /> ' +
+				'<p>drag a picture or click to select</p>' +
+				'<div class="contenttype"><i class="icon-picture"></i></div>' +	
+				'<img class="uploadPreview" :src="imgpreview" />' +
+				'<div v-if="load" class="loadwrapper"><span class="load"></span></div>' +
+				'<div class="imgmeta" v-if="imgmeta">' +
+				'<label for="imgalt">Alt-Text: </label><input name="imgalt" type="text" placeholder="alt" @input="createmarkdown" v-model="imgalt" max="100" />' +
+				'<label for="imgtitle">Title: </label><input name="imgtitle" type="text" placeholder="title" v-model="imgtitle" @input="createmarkdown" max="64" />' +
+				'<label for="imgcaption">Caption: </label><input title="imgcaption" type="text" placeholder="caption" v-model="imgcaption" @input="createmarkdown" max="140" />' +
+				'<label for="imgurl">Link: </label><input title="imgurl" type="url" placeholder="url" v-model="imglink" @input="createmarkdown" />' +
+				'<label for="imgclass">Class: </label><select title="imgclass" v-model="imgclass" @change="createmarkdown"><option value="center">Center</option><option value="left">Left</option><option value="right">Right</option><option value="youtube">Youtube</option><option value="vimeo">Vimeo</option></select>' +
+				'<input title="imgid" type="hidden" placeholder="id" v-model="imgid" @input="createmarkdown" max="140" />' +
+				'</div></div>',
+	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;
+			}
+			console.info(this.imgpreview);
+			console.info(this.imgfile);
+		}
+	},
+	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,
+		'title-component': titleComponent,
+		'headline-component': headlineComponent,
+		'image-component': imageComponent,
+		'code-component': codeComponent,
+		'quote-component': quoteComponent,
+		'ulist-component': ulistComponent,
+		'olist-component': olistComponent,
+		'table-component': tableComponent,
+	},
+	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,
+	},
+	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;
+				}
+			}
+		}, method, url, params);
+		
+		self.sortable = new Sortable(sortblox, {
+			animation: 150,
+			onEnd: 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,
+				};
+				self.moveBlock(params);
+			},
+		});
+	},
+	methods: {
+		setData: function(event, blocktype, body)
+		{
+			this.blockId = event.currentTarget.dataset.id;
+			/* this.blockType = blocktype; */
+			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);
+			}
+		},
+		hideModal: function()
+		{
+			this.addblock = false;
+		},
+		determineBlockType: function(block)
+		{			
+			if(block.match(/^\d+\./)){ return "olist-component" }
+			
+			var lines = block.split("\n");
+			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 == "`" && 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';
+			}
+		},
+		moveBlock: function(params)
+		{
+			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
+					{
+						var blox = document.getElementsByClassName("blox");
+						var length = blox.length;
+						for (var i = 0; i < length; i++ ) {
+							blox[i].id = "blox-" + i;
+							blox[i].dataset.id = i;
+						}
+
+						self.freeze = false;
+						self.markdown = result.markdown;
+						self.blockMarkdown = '';
+						self.blockType = '';
+
+						publishController.publishDisabled = false;
+						publishController.publishResult = "";
+					}
+				}
+			}, method, url, params);			
+		},
+	}
+});

+ 1228 - 0
system/author/js/vue-blox-orig.js

@@ -0,0 +1,1228 @@
+const eventBus = new Vue();
+
+const contentComponent = Vue.component('content-block', {
+	props: ['body', 'load'],
+	template: '<div ref="bloxcomponent" class="blox-editor">' +
+				'<div :class="{ editactive: edit }">' +
+				 '<div @keyup.enter="submitBlock" @click="getData">' +
+				  '<div class="component" ref="component">' +
+				   '<transition name="fade-editor">' +
+				    '<component :disabled="disabled" :compmarkdown="compmarkdown" @updatedMarkdown="updateMarkdown" :is="componentType"></component>' +
+				   '</transition>' +
+				   '<div class="blox-buttons" v-if="edit">' + 
+				    '<button class="edit" :disabled="disabled" @click.prevent="saveBlock">save</button>' +
+				    '<button class="cancel" :disabled="disabled" @click.prevent="switchToPreviewMode">cancel</button>' +
+				   '</div>' +
+				  '</div>' +
+				  '<div :class="preview" ref="preview"><slot></slot></div>' + 
+				 '</div>' +
+				 '<div class="sideadd" v-if="body">' + 
+				  '<button class="add" :disabled="disabled" title="add content-block" @click.prevent="addBlock($event)"><i class="icon-plus"></i></button>' +
+				 '</div>' + 
+				 '<div class="sideaction" v-if="body">' + 
+				  '<button class="delete" :disabled="disabled" title="delete content-block" @click.prevent="deleteBlock($event)"><i class="icon-cancel"></i></button>' +
+				 '</div>' + 
+				'</div>' + 
+				'<div v-if="load" class="loadoverlay"><span class="load"></span></div>' +
+			  '</div>',	
+	data: function () {
+		return {
+			preview: 'visible',
+			edit: false,
+			compmarkdown: '',
+			componentType: '',
+			disabled: false,
+			load: false
+		}
+	},
+	mounted: function()
+	{
+		eventBus.$on('closeComponents', this.closeComponents);
+	},
+	methods: {
+		addBlock: function($event)
+		{
+			self = this;
+			console.info($event);
+			var result = {content: '<p>asf</p>', markdown: 'asf', blockId: 'blox-new', id: 'new', errors: 'false'};
+			self.$root.$data.markdown.push(result.markdown);
+			self.$root.$data.newBlocks.push(result);
+			
+			this.$nextTick(function () {
+				var newcontent = document.getElementById("blox-new");
+				var newblock = newcontent.closest('.blox-editor');
+				console.info(newblock);
+			});
+			/*
+			self.$root.$data.blockMarkdown = '';
+			self.$root.$data.blockType = 'markdown-component';
+			// self.getData();
+			var textbox = document.querySelectorAll('textarea')[0];
+			if(textbox){ textbox.style.height = "70px"; }
+			*/
+		},
+		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.sortable.option("disabled",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;
+			this.$refs.preview.style.minHeight = "auto";			
+		},
+		switchToPreviewMode: function()
+		{
+			self = this;
+			self.$root.$data.freeze = false;						/* activate the data again */
+			self.$root.sortable.option("disabled",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"){ }
+			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 == '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
+				{
+					var url = self.$root.$data.root + '/api/v1/block';
+					var method 	= 'PUT';
+				}
+				
+				var compmarkdown = this.compmarkdown.split('\n\n').join('\n');
+				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,
+				};
+
+				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
+						{
+							self.switchToPreviewMode();
+							
+							if(self.$root.$data.blockId == 99999)
+							{
+								self.$root.$data.markdown.push(result.markdown);
+								self.$root.$data.newBlocks.push(result);
+								
+								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
+							{
+								var htmlid = "blox-" + self.$root.$data.blockId;
+								var html = document.getElementById(htmlid);
+								html.innerHTML = result.content;
+								
+								self.$root.$data.markdown[self.$root.$data.blockId] = result.markdown;
+								self.$root.$data.blockMarkdown = '';
+								self.$root.$data.blockType = '';
+							}
+						}
+					}
+					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;
+			bloxeditor.firstChild.id = "delete-"+bloxid;
+
+			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();
+						
+						var deleteblock = document.getElementById("delete-"+bloxid);
+						deleteblock.parentElement.remove(deleteblock);
+						
+						var blox = document.getElementsByClassName("blox");
+						var length = blox.length;
+						for (var i = 0; i < length; i++ ) {
+							blox[i].id = "blox-" + i;
+							blox[i].dataset.id = i;
+						}
+
+						self.$root.$data.markdown = result.markdown;						
+						self.$root.$data.blockMarkdown = '';
+						self.$root.$data.blockType = '';
+					}
+				}
+			}, method, url, params);
+		},
+	},
+})
+
+const titleComponent = Vue.component('title-component', {
+	props: ['compmarkdown', 'disabled'],
+	template: '<div><input type="text" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown"></div>',
+	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: '<div>' + 
+				'<div class="contenttype"><i class="icon-paragraph"></i></div>' +
+				'<textarea class="mdcontent" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown"></textarea>' + 
+				'</div>',
+	mounted: function(){
+		this.$refs.markdown.focus();
+		autosize(document.querySelectorAll('textarea'));
+	},
+	methods: {
+		updatemarkdown: function(event)
+		{
+			this.$emit('updatedMarkdown', event.target.value);
+		},
+	},
+})
+
+const codeComponent = Vue.component('code-component', {
+	props: ['compmarkdown', 'disabled'],
+	template: '<div>' + 
+				'<input type="hidden" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown" />' +	
+				'<div class="contenttype"><i class="icon-code"></i></div>' +
+				'<textarea class="mdcontent" ref="markdown" v-model="codeblock" :disabled="disabled" @input="createmarkdown"></textarea>' + 
+				'</div>',
+	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: '<div>' + 
+				'<input type="hidden" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown" />' +	
+				'<div class="contenttype"><i class="icon-quote-left"></i></div>' +
+				'<textarea class="mdcontent" ref="markdown" v-model="quote" :disabled="disabled" @input="createmarkdown"></textarea>' + 
+				'</div>',
+	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: '<div>' + 
+				'<div class="contenttype"><i class="icon-list-bullet"></i></div>' +
+				'<textarea class="mdcontent" ref="markdown" v-model="compmarkdown" :disabled="disabled" @input="updatemarkdown"></textarea>' + 
+				'</div>',
+	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: '<div>' + 
+				'<div class="contenttype"><i class="icon-list-numbered"></i></div>' +
+				'<textarea class="mdcontent" ref="markdown" v-model="compmarkdown" :disabled="disabled" @input="updatemarkdown"></textarea>' + 
+				'</div>',
+	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: '<div>' + 
+				'<div class="contenttype"><i class="icon-header"></i></div>' +
+				'<input class="mdcontent" type="text" ref="markdown" v-model="compmarkdown" :disabled="disabled" @input="updatemarkdown">' +
+				'</div>',
+	mounted: function(){
+		this.$refs.markdown.focus();
+		if(!this.compmarkdown)
+		{
+			this.compmarkdown = '## ';
+		}
+	},
+	methods: {
+		updatemarkdown: function(event)
+		{
+			this.$emit('updatedMarkdown', event.target.value);
+		},
+	},
+})
+
+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: '<div ref="table" :key="tablekey">' + 
+				'<div class="contenttype"><i class="icon-table"></i></div>' +
+				'<table ref="markdown">' +
+					'<colgroup>' +
+						'<col v-for="col in table[0]">' +
+					'</colgroup>' +
+					'<tbody>' +
+						'<tr v-for="(row, rowindex) in table">' +
+							'<td v-if="rowindex === 0" v-for="(value,colindex) in row" contenteditable="false" class="noteditable" @click="switchcolumnbar(value)">{{value}} ' +
+							  '<div v-if="columnbar === value" class="columnaction">' + 
+							     '<div class="actionline" @click="addrightcolumn(value)">add right column</div>' +
+								 '<div class="actionline" @click="addleftcolumn(value)">add left column</div>' +
+								 '<div class="actionline" @click="deletecolumn(value)">delete column</div>' +
+							  '</div>' +
+							'</td>' +
+							'<th v-if="rowindex === 1" v-for="(value,colindex) in row" :contenteditable="colindex !== 0 ? true : false" @click="switchrowbar(value)" @blur="updatedata($event,colindex,rowindex)" :class="colindex !== 0 ? editable : noteditable">' + 
+							 '<div v-if="colindex === 0 && rowbar === value" class="rowaction">' + 
+							     '<div class="actionline" @click="addaboverow(value)">add row above</div>' +
+								 '<div class="actionline" @click="addbelowrow(value)">add row below</div>' +
+								 '<div class="actionline" @click="deleterow(value)">delete row</div>' +
+							  '</div>' + 
+							'{{value}}</th>' +
+							'<td v-if="rowindex > 1" v-for="(value,colindex) in row" :contenteditable="colindex !== 0 ? true : false" @click="switchrowbar(value)" @blur="updatedata($event,colindex,rowindex)" :class="colindex !== 0 ? editable : noteditable">' + 
+							 '<div v-if="colindex === 0 && rowbar === value" class="rowaction">' + 
+							     '<div class="actionline" @click="addaboverow(value)">add row above</div>' +
+								 '<div class="actionline" @click="addbelowrow(value)">add row below</div>' +
+								 '<div class="actionline" @click="deleterow(value)">delete row</div>' +
+							  '</div>' +
+							'{{ value }}</td>' +
+						'</tr>' +
+					'</tbody>' +
+				'</table>' +
+				'</div>',
+	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)
+		{
+			/* generate markdown here ??? */
+			this.$emit('updatedMarkdown', event.target.value);
+		},
+	},
+})
+
+const videoComponent = Vue.component('video-component', {
+	props: ['compmarkdown', 'disabled', 'load'],
+	template: '<div class="video dropbox">' +
+				'<div class="contenttype"><i class="icon-youtube-play"></i></div>' +
+				'<label for="video">Link to video: </label><input type="url" ref="markdown" placeholder="https://www.youtube.com/watch?v=" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown">' +
+				'<div v-if="load" class="loadwrapper"><span class="load"></span></div>' +
+				'</div>',
+	methods: {
+		updatemarkdown: function(event)
+		{
+			this.$emit('updatedMarkdown', event.target.value);
+		},
+	},
+})
+
+const imageComponent = Vue.component('image-component', {
+	props: ['compmarkdown', 'disabled'],
+	template: '<div class="dropbox">' +
+				'<input type="hidden" ref="markdown" :value="compmarkdown" :disabled="disabled" @input="updatemarkdown" />' +
+				'<input type="file" name="image" accept="image/*" class="input-file" @change="onFileChange( $event )" /> ' +
+				'<p>drag a picture or click to select</p>' +
+				'<div class="contenttype"><i class="icon-picture"></i></div>' +	
+				'<img class="uploadPreview" :src="imgpreview" />' +
+				'<div v-if="load" class="loadwrapper"><span class="load"></span></div>' +
+				'<div class="imgmeta" v-if="imgmeta">' +
+				'<label for="imgalt">Alt-Text: </label><input name="imgalt" type="text" placeholder="alt" @input="createmarkdown" v-model="imgalt" max="100" />' +
+				'<label for="imgtitle">Title: </label><input name="imgtitle" type="text" placeholder="title" v-model="imgtitle" @input="createmarkdown" max="64" />' +
+				'<label for="imgcaption">Caption: </label><input title="imgcaption" type="text" placeholder="caption" v-model="imgcaption" @input="createmarkdown" max="140" />' +
+				'<label for="imgurl">Link: </label><input title="imgurl" type="url" placeholder="url" v-model="imglink" @input="createmarkdown" />' +
+				'<label for="imgclass">Class: </label><select title="imgclass" v-model="imgclass" @change="createmarkdown"><option value="center">Center</option><option value="left">Left</option><option value="right">Right</option><option value="youtube">Youtube</option><option value="vimeo">Vimeo</option></select>' +
+				'<input title="imgid" type="hidden" placeholder="id" v-model="imgid" @input="createmarkdown" max="140" />' +
+				'</div></div>',
+	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,
+		'title-component': titleComponent,
+		'headline-component': headlineComponent,
+		'image-component': imageComponent,
+		'code-component': codeComponent,
+		'quote-component': quoteComponent,
+		'ulist-component': ulistComponent,
+		'olist-component': olistComponent,
+		'table-component': tableComponent,
+	},
+	data: {
+		root: document.getElementById("main").dataset.url,
+		markdown: false,
+		blockId: false,
+		blockType: false,
+		blockMarkdown: '',
+		file: false,
+		freeze: false,
+		newBlocks: [],
+		draftDisabled: true,
+	},
+	mounted: function(){
+
+		console.info(publishController);
+		publishController.visual = true;
+		
+		var url = this.root + '/api/v1/article/markdown';
+		
+		var params = {
+			'url':				document.getElementById("path").value,
+			'csrf_name': 		document.getElementById("csrf_name").value,
+			'csrf_value':		document.getElementById("csrf_value").value,
+		};
+		
+		var method 	= 'POST';
+
+		var self = this;		
+		
+		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;
+				}
+			}
+		}, method, url, params);
+		
+		self.sortable = new Sortable(sortblox, {
+			animation: 150,
+			onEnd: 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,
+				};
+				self.moveBlock(params);
+			},
+		});
+	},
+	methods: {
+		setData: function(event, blocktype, body)
+		{
+			this.blockId = event.currentTarget.dataset.id;
+			/* this.blockType = blocktype; */
+			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);
+			}
+		},
+		determineBlockType: function(block)
+		{
+			if(block.match(/^\d+\./)){ return "olist-component" }
+			
+			var lines = block.split("\n");
+			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 == "`" && 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';
+			}
+		},
+		moveBlock: function(params)
+		{
+			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
+					{
+						var blox = document.getElementsByClassName("blox");
+						var length = blox.length;
+						for (var i = 0; i < length; i++ ) {
+							blox[i].id = "blox-" + i;
+							blox[i].dataset.id = i;
+						}
+
+						self.freeze = false;
+						self.markdown = result.markdown;
+						self.blockMarkdown = '';
+						self.blockType = '';
+
+						publishController.publishDisabled = false;
+						publishController.publishResult = "";
+					}
+				}
+			}, method, url, params);			
+		},
+	}
+});

+ 142 - 55
system/author/js/vue-blox.js

@@ -2,9 +2,14 @@ const eventBus = new Vue();
 
 const contentComponent = Vue.component('content-block', {
 	props: ['body', 'load'],
-	template: '<div ref="bloxcomponent" class="blox-editor">' +
-				'<div :class="{ editactive: edit }">' +
-				 '<div @keyup.enter="submitBlock" @click="getData">' +
+	template: '<div ref="bloxcomponent" class="blox-editor" :class="newblock">' +
+				'<div v-if="newblock" class="newblock-info">Choose a content-type <button class="newblock-close" @click.prevent="closeNewBlock($event)">close</button></div>' +	
+				'<div class="blox-wrapper" :class="{ editactive: edit }">' +
+				 '<div class="sideaction" v-if="body">' + 
+				  '<button class="add" :disabled="disabled" title="add content-block" @click.prevent="addNewBlock($event)"><i class="icon-plus"></i></button>' +
+				  '<button class="delete" :disabled="disabled" title="delete content-block" @click.prevent="deleteBlock($event)"><i class="icon-cancel"></i></button>' +
+				 '</div>' + 
+				 '<div class="background-helper" @keyup.enter="submitBlock" @click="getData">' +
 				  '<div class="component" ref="component">' +
 				   '<transition name="fade-editor">' +
 				    '<component :disabled="disabled" :compmarkdown="compmarkdown" @updatedMarkdown="updateMarkdown" :is="componentType"></component>' +
@@ -12,16 +17,13 @@ const contentComponent = Vue.component('content-block', {
 				   '<div class="blox-buttons" v-if="edit">' + 
 				    '<button class="edit" :disabled="disabled" @click.prevent="saveBlock">save</button>' +
 				    '<button class="cancel" :disabled="disabled" @click.prevent="switchToPreviewMode">cancel</button>' +
-				   '</div>' + 
-				  '</div>' + 
-				  '<div :class="preview" ref="preview"><slot></slot></div>' + 
+				   '</div>' +
+				  '</div>' +
+				  '<div :class="preview" ref="preview"><slot><format-component></format-component></slot></div>' +
 				 '</div>' +
-				 '<div class="sideaction" v-if="body">' + 
-				  '<button class="delete" :disabled="disabled" title="delete content-block" @click.prevent="deleteBlock($event)"><i class="icon-cancel"></i></button>' +
-				 '</div>' + 
-				'</div>' + 
+				'</div>' +
 				'<div v-if="load" class="loadoverlay"><span class="load"></span></div>' +
-			  '</div>',	
+			  '</div>',
 	data: function () {
 		return {
 			preview: 'visible',
@@ -29,7 +31,8 @@ const contentComponent = Vue.component('content-block', {
 			compmarkdown: '',
 			componentType: '',
 			disabled: false,
-			load: false
+			load: false,
+			newblock: false,
 		}
 	},
 	mounted: function()
@@ -37,6 +40,39 @@ const contentComponent = Vue.component('content-block', {
 		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.sortable.option("disabled", 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.sortable.option("disabled", false);
+
+			this.$root.$data.html.splice(bloxid,1);
+			this.$root.$data.markdown.splice(bloxid,1);			
+		},
 		updateMarkdown: function($event)
 		{
 			this.compmarkdown = $event;
@@ -77,7 +113,10 @@ const contentComponent = Vue.component('content-block', {
 			this.preview = 'visible';
 			this.edit = false;
 			this.componentType = false;
-			this.$refs.preview.style.minHeight = "auto";			
+			if(this.$refs.preview)
+			{
+				this.$refs.preview.style.minHeight = "auto";
+			}
 		},
 		switchToPreviewMode: function()
 		{
@@ -173,6 +212,15 @@ const contentComponent = Vue.component('content-block', {
 
 				var self = this;
 				
+				var compmarkdown = this.compmarkdown.split('\n\n').join('\n');
+				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';
@@ -183,21 +231,17 @@ const contentComponent = Vue.component('content-block', {
 					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';
 				}
 				
-				var compmarkdown = this.compmarkdown.split('\n\n').join('\n');
-				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,
-				};
-
 				sendJson(function(response, httpStatus)
 				{
 					if(httpStatus == 400)
@@ -210,7 +254,7 @@ const contentComponent = Vue.component('content-block', {
 						self.activatePage();
 
 						var result = JSON.parse(response);
-										
+									
 						if(result.errors)
 						{
 							publishController.errors.message = result.errors.message;
@@ -222,7 +266,7 @@ const contentComponent = Vue.component('content-block', {
 							if(self.$root.$data.blockId == 99999)
 							{
 								self.$root.$data.markdown.push(result.markdown);
-								self.$root.$data.newBlocks.push(result);
+								self.$root.$data.html.push(result.content);
 								
 								self.$root.$data.blockMarkdown = '';
 								self.$root.$data.blockType = 'markdown-component';
@@ -230,13 +274,24 @@ const contentComponent = Vue.component('content-block', {
 								var textbox = document.querySelectorAll('textarea')[0];
 								if(textbox){ textbox.style.height = "70px"; }
 							}
+							else if(self.$root.$data.newblock)
+							{
+								self.$root.$data.html[result.id] = result.content;
+								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
 							{
-								var htmlid = "blox-" + self.$root.$data.blockId;
-								var html = document.getElementById(htmlid);
-								html.innerHTML = result.content;
+								self.$root.$data.markdown[result.id] = result.markdown;
+
+								self.$root.$data.html[result.id] = result.content;
+								document.getElementById('blox-'+result.id).innerHTML = result.content;
 								
-								self.$root.$data.markdown[self.$root.$data.blockId] = result.markdown;
 								self.$root.$data.blockMarkdown = '';
 								self.$root.$data.blockType = '';
 							}
@@ -257,21 +312,21 @@ const contentComponent = Vue.component('content-block', {
 			var bloxeditor = event.target.closest('.blox-editor');
 			
 			var bloxid = bloxeditor.getElementsByClassName('blox')[0].dataset.id;
-			bloxeditor.firstChild.id = "delete-"+bloxid;
-
+			/* bloxeditor.firstChild.id = "delete-"+bloxid; */
+							
 			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)
@@ -283,7 +338,7 @@ const contentComponent = Vue.component('content-block', {
 					self.activatePage();
 					
 					var result = JSON.parse(response);
-
+	
 					if(result.errors)
 					{
 						publishController.errors.message = result.errors;
@@ -291,23 +346,14 @@ const contentComponent = Vue.component('content-block', {
 					else
 					{	
 						self.switchToPreviewMode();
-						
-						var deleteblock = document.getElementById("delete-"+bloxid);
-						deleteblock.parentElement.remove(deleteblock);
-						
-						var blox = document.getElementsByClassName("blox");
-						var length = blox.length;
-						for (var i = 0; i < length; i++ ) {
-							blox[i].id = "blox-" + i;
-							blox[i].dataset.id = i;
-						}
-
-						self.$root.$data.markdown = result.markdown;						
+							
+						self.$root.$data.html.splice(bloxid,1);
+						self.$root.$data.markdown.splice(bloxid,1);
 						self.$root.$data.blockMarkdown = '';
 						self.$root.$data.blockType = '';
 					}
 				}
-			}, method, url, params);
+			}, method, url, params);				
 		},
 	},
 })
@@ -723,7 +769,6 @@ const tableComponent = Vue.component('table-component', {
 		},
 		updatemarkdown: function(event)
 		{
-			/* generate markdown here ??? */
 			this.$emit('updatedMarkdown', event.target.value);
 		},
 	},
@@ -841,6 +886,8 @@ const imageComponent = Vue.component('image-component', {
 				this.imgpreview = imgpreview[0].slice(1,-1);
 				this.imgfile = this.imgpreview;
 			}
+			console.info(this.imgpreview);
+			console.info(this.imgfile);
 		}
 	},
 	methods: {
@@ -1037,20 +1084,26 @@ let editor = new Vue({
 	},
 	data: {
 		root: document.getElementById("main").dataset.url,
+		html: false,
+		title: false,
 		markdown: false,
 		blockId: false,
 		blockType: false,
-		blockMarkdown: '',
+		blockMarkdown: false,
 		file: false,
 		freeze: false,
 		newBlocks: [],
+		addblock: false,
 		draftDisabled: true,
+		bloxOverlay: false,
 	},
 	mounted: function(){
-		
+
 		publishController.visual = true;
+
+		var self = this;		
 		
-		var url = this.root + '/api/v1/article/markdown';
+		var url = this.root + '/api/v1/article/html';
 		
 		var params = {
 			'url':				document.getElementById("path").value,
@@ -1060,7 +1113,31 @@ let editor = new Vue({
 		
 		var method 	= 'POST';
 
-		var self = this;		
+		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)
 		{
@@ -1102,6 +1179,7 @@ let editor = new Vue({
 			this.blockId = event.currentTarget.dataset.id;
 			/* this.blockType = blocktype; */
 			this.blockMarkdown = this.markdown[this.blockId];
+			console.info(this.blockId);
 			if(blocktype)
 			{
 				this.blockType = blocktype;
@@ -1109,14 +1187,23 @@ let editor = new Vue({
 			else if(this.blockId == 0)
 			{ 
 				this.blockType = "title-component"
-			} 
+			}
 			else 
 			{
 				this.blockType = this.determineBlockType(this.blockMarkdown);
 			}
 		},
-		determineBlockType: function(block)
+		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");

+ 2 - 2
system/system.php

@@ -129,9 +129,9 @@ $container['csrf'] 	= false;
 foreach($session_segments as $segment)
 {
 	if(substr( $path, 0, strlen($segment) ) === $segment)
-	{		
+	{	
 		// configure session
-		ini_set( 'session.cookie_httponly', 1 );
+		ini_set('session.cookie_httponly', 1 );
 		ini_set('session.use_strict_mode', 1);
 		if($container['request']->getUri()->getScheme() == 'https')
 		{

+ 4 - 4
themes/typemill/page.twig

@@ -18,12 +18,12 @@
 			<div id="share-icons" class="share-icons hide">
 				{% include 'partials/share.twig' with {'share' : settings.themes.typemill.socialButtons } %}
 				<div class="share-button" id="share-button">
-					<i class="icon-share-1"></i>
+					<i class="icon-share"></i>
 				</div>
 			</div>
 		{% endif %}
 		{% if settings.themes.typemill.gitPosition.top %} 
-			<div class="gitlink"><a title="edit on git" href="{{ settings.themes.typemill.gitlink }}{{ item.urlRel }}">edit <i class="icon-github-circled"></i></a></div>
+			<div class="gitlink"><a title="edit on github" href="{{ settings.themes.typemill.gitlink }}{{ item.path }}">edit <i class="icon-github-circled"></i></a></div>
 		{% endif %}
 	</div>
 {% endif %}
@@ -43,12 +43,12 @@
 			<div id="share-icons-bottom" class="share-icons hide">
 				{% include 'partials/share.twig' with {'share' : settings.themes.typemill.socialButtons } %}
 				<div id="share-button-bottom" class="share-button">
-					<i class="icon-share-1"></i>
+					<i class="icon-share"></i>
 				</div>
 			</div>
 		{% endif %}
 		{% if settings.themes.typemill.gitPosition.bottom %} 
-			<div class="gitlink"><a title="edit on git" href="{{ settings.themes.typemill.gitlink }}{{ item.urlRel }}">edit <i class="icon-github-circled"></i></a></div>
+			<div class="gitlink"><a title="edit on github" href="{{ settings.themes.typemill.gitlink }}{{ item.path }}">edit <i class="icon-github-circled"></i></a></div>
 		{% endif %}		
 	</div>
 {% endif %}

+ 1 - 1
themes/typemill/typemill.yaml

@@ -1,5 +1,5 @@
 name: Typemill Theme
-version: 1.1.4
+version: 1.1.5
 description: The standard theme for Typemill. Responsive, minimal and without any dependencies. It uses the system fonts Calibri and Helvetica. No JavaScript is used. 
 author: Sebastian Schürmanns
 homepage: https://typemill.net

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini