yeungalan 5 роки тому
батько
коміт
113cc53162

+ 2 - 0
README.md

@@ -1,2 +1,4 @@
 # WriterA
 
+Current ISSUE:
+pdfJS can't generate more than one page

+ 16 - 0
WriterA/FloatWindow.php

@@ -0,0 +1,16 @@
+<?php
+include '../auth.php';
+?>
+<html>
+<body>
+Now Loading...
+<div id="DATA_MODULENAME"><?php echo dirname(str_replace("\\","/",__FILE__)); ?></div>
+<script src="../script/jquery.min.js"></script>
+<script src="../script/ao_module.js"></script>
+<script>
+var moduleName = $("#DATA_MODULENAME").text().trim().split("/").pop();
+var uid = (new Date()).getTime();
+ao_module_newfw( moduleName + "/index.php","WriterA","file text outline", uid,1050,550,undefined,undefined,true,false);
+</script>
+</body>
+</html>

+ 1548 - 0
WriterA/Parsedown.php

@@ -0,0 +1,1548 @@
+<?php
+
+#
+#
+# Parsedown
+# http://parsedown.org
+#
+# (c) Emanuil Rusev
+# http://erusev.com
+#
+# For the full license information, view the LICENSE file that was distributed
+# with this source code.
+#
+#
+
+class Parsedown
+{
+    # ~
+
+    const version = '1.6.0';
+
+    # ~
+
+    function text($text)
+    {
+        # make sure no definitions are set
+        $this->DefinitionData = array();
+
+        # standardize line breaks
+        $text = str_replace(array("\r\n", "\r"), "\n", $text);
+
+        # remove surrounding line breaks
+        $text = trim($text, "\n");
+
+        # split text into lines
+        $lines = explode("\n", $text);
+
+        # iterate through lines to identify blocks
+        $markup = $this->lines($lines);
+
+        # trim line breaks
+        $markup = trim($markup, "\n");
+
+        return $markup;
+    }
+
+    #
+    # Setters
+    #
+
+    function setBreaksEnabled($breaksEnabled)
+    {
+        $this->breaksEnabled = $breaksEnabled;
+
+        return $this;
+    }
+
+    protected $breaksEnabled;
+
+    function setMarkupEscaped($markupEscaped)
+    {
+        $this->markupEscaped = $markupEscaped;
+
+        return $this;
+    }
+
+    protected $markupEscaped;
+
+    function setUrlsLinked($urlsLinked)
+    {
+        $this->urlsLinked = $urlsLinked;
+
+        return $this;
+    }
+
+    protected $urlsLinked = true;
+
+    #
+    # Lines
+    #
+
+    protected $BlockTypes = array(
+        '#' => array('Header'),
+        '*' => array('Rule', 'List'),
+        '+' => array('List'),
+        '-' => array('SetextHeader', 'Table', 'Rule', 'List'),
+        '0' => array('List'),
+        '1' => array('List'),
+        '2' => array('List'),
+        '3' => array('List'),
+        '4' => array('List'),
+        '5' => array('List'),
+        '6' => array('List'),
+        '7' => array('List'),
+        '8' => array('List'),
+        '9' => array('List'),
+        ':' => array('Table'),
+        '<' => array('Comment', 'Markup'),
+        '=' => array('SetextHeader'),
+        '>' => array('Quote'),
+        '[' => array('Reference'),
+        '_' => array('Rule'),
+        '`' => array('FencedCode'),
+        '|' => array('Table'),
+        '~' => array('FencedCode'),
+    );
+
+    # ~
+
+    protected $unmarkedBlockTypes = array(
+        'Code',
+    );
+
+    #
+    # Blocks
+    #
+
+    protected function lines(array $lines)
+    {
+        $CurrentBlock = null;
+
+        foreach ($lines as $line)
+        {
+            if (chop($line) === '')
+            {
+                if (isset($CurrentBlock))
+                {
+                    $CurrentBlock['interrupted'] = true;
+                }
+
+                continue;
+            }
+
+            if (strpos($line, "\t") !== false)
+            {
+                $parts = explode("\t", $line);
+
+                $line = $parts[0];
+
+                unset($parts[0]);
+
+                foreach ($parts as $part)
+                {
+                    $shortage = 4 - mb_strlen($line, 'utf-8') % 4;
+
+                    $line .= str_repeat(' ', $shortage);
+                    $line .= $part;
+                }
+            }
+
+            $indent = 0;
+
+            while (isset($line[$indent]) and $line[$indent] === ' ')
+            {
+                $indent ++;
+            }
+
+            $text = $indent > 0 ? substr($line, $indent) : $line;
+
+            # ~
+
+            $Line = array('body' => $line, 'indent' => $indent, 'text' => $text);
+
+            # ~
+
+            if (isset($CurrentBlock['continuable']))
+            {
+                $Block = $this->{'block'.$CurrentBlock['type'].'Continue'}($Line, $CurrentBlock);
+
+                if (isset($Block))
+                {
+                    $CurrentBlock = $Block;
+
+                    continue;
+                }
+                else
+                {
+                    if ($this->isBlockCompletable($CurrentBlock['type']))
+                    {
+                        $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
+                    }
+                }
+            }
+
+            # ~
+
+            $marker = $text[0];
+
+            # ~
+
+            $blockTypes = $this->unmarkedBlockTypes;
+
+            if (isset($this->BlockTypes[$marker]))
+            {
+                foreach ($this->BlockTypes[$marker] as $blockType)
+                {
+                    $blockTypes []= $blockType;
+                }
+            }
+
+            #
+            # ~
+
+            foreach ($blockTypes as $blockType)
+            {
+                $Block = $this->{'block'.$blockType}($Line, $CurrentBlock);
+
+                if (isset($Block))
+                {
+                    $Block['type'] = $blockType;
+
+                    if ( ! isset($Block['identified']))
+                    {
+                        $Blocks []= $CurrentBlock;
+
+                        $Block['identified'] = true;
+                    }
+
+                    if ($this->isBlockContinuable($blockType))
+                    {
+                        $Block['continuable'] = true;
+                    }
+
+                    $CurrentBlock = $Block;
+
+                    continue 2;
+                }
+            }
+
+            # ~
+
+            if (isset($CurrentBlock) and ! isset($CurrentBlock['type']) and ! isset($CurrentBlock['interrupted']))
+            {
+                $CurrentBlock['element']['text'] .= "\n".$text;
+            }
+            else
+            {
+                $Blocks []= $CurrentBlock;
+
+                $CurrentBlock = $this->paragraph($Line);
+
+                $CurrentBlock['identified'] = true;
+            }
+        }
+
+        # ~
+
+        if (isset($CurrentBlock['continuable']) and $this->isBlockCompletable($CurrentBlock['type']))
+        {
+            $CurrentBlock = $this->{'block'.$CurrentBlock['type'].'Complete'}($CurrentBlock);
+        }
+
+        # ~
+
+        $Blocks []= $CurrentBlock;
+
+        unset($Blocks[0]);
+
+        # ~
+
+        $markup = '';
+
+        foreach ($Blocks as $Block)
+        {
+            if (isset($Block['hidden']))
+            {
+                continue;
+            }
+
+            $markup .= "\n";
+            $markup .= isset($Block['markup']) ? $Block['markup'] : $this->element($Block['element']);
+        }
+
+        $markup .= "\n";
+
+        # ~
+
+        return $markup;
+    }
+
+    protected function isBlockContinuable($Type)
+    {
+        return method_exists($this, 'block'.$Type.'Continue');
+    }
+
+    protected function isBlockCompletable($Type)
+    {
+        return method_exists($this, 'block'.$Type.'Complete');
+    }
+
+    #
+    # Code
+
+    protected function blockCode($Line, $Block = null)
+    {
+        if (isset($Block) and ! isset($Block['type']) and ! isset($Block['interrupted']))
+        {
+            return;
+        }
+
+        if ($Line['indent'] >= 4)
+        {
+            $text = substr($Line['body'], 4);
+
+            $Block = array(
+                'element' => array(
+                    'name' => 'pre',
+                    'handler' => 'element',
+                    'text' => array(
+                        'name' => 'code',
+                        'text' => $text,
+                    ),
+                ),
+            );
+
+            return $Block;
+        }
+    }
+
+    protected function blockCodeContinue($Line, $Block)
+    {
+        if ($Line['indent'] >= 4)
+        {
+            if (isset($Block['interrupted']))
+            {
+                $Block['element']['text']['text'] .= "\n";
+
+                unset($Block['interrupted']);
+            }
+
+            $Block['element']['text']['text'] .= "\n";
+
+            $text = substr($Line['body'], 4);
+
+            $Block['element']['text']['text'] .= $text;
+
+            return $Block;
+        }
+    }
+
+    protected function blockCodeComplete($Block)
+    {
+        $text = $Block['element']['text']['text'];
+
+        $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
+
+        $Block['element']['text']['text'] = $text;
+
+        return $Block;
+    }
+
+    #
+    # Comment
+
+    protected function blockComment($Line)
+    {
+        if ($this->markupEscaped)
+        {
+            return;
+        }
+
+        if (isset($Line['text'][3]) and $Line['text'][3] === '-' and $Line['text'][2] === '-' and $Line['text'][1] === '!')
+        {
+            $Block = array(
+                'markup' => $Line['body'],
+            );
+
+            if (preg_match('/-->$/', $Line['text']))
+            {
+                $Block['closed'] = true;
+            }
+
+            return $Block;
+        }
+    }
+
+    protected function blockCommentContinue($Line, array $Block)
+    {
+        if (isset($Block['closed']))
+        {
+            return;
+        }
+
+        $Block['markup'] .= "\n" . $Line['body'];
+
+        if (preg_match('/-->$/', $Line['text']))
+        {
+            $Block['closed'] = true;
+        }
+
+        return $Block;
+    }
+
+    #
+    # Fenced Code
+
+    protected function blockFencedCode($Line)
+    {
+        if (preg_match('/^['.$Line['text'][0].']{3,}[ ]*([\w-]+)?[ ]*$/', $Line['text'], $matches))
+        {
+            $Element = array(
+                'name' => 'code',
+                'text' => '',
+            );
+
+            if (isset($matches[1]))
+            {
+                $class = 'language-'.$matches[1];
+
+                $Element['attributes'] = array(
+                    'class' => $class,
+                );
+            }
+
+            $Block = array(
+                'char' => $Line['text'][0],
+                'element' => array(
+                    'name' => 'pre',
+                    'handler' => 'element',
+                    'text' => $Element,
+                ),
+            );
+
+            return $Block;
+        }
+    }
+
+    protected function blockFencedCodeContinue($Line, $Block)
+    {
+        if (isset($Block['complete']))
+        {
+            return;
+        }
+
+        if (isset($Block['interrupted']))
+        {
+            $Block['element']['text']['text'] .= "\n";
+
+            unset($Block['interrupted']);
+        }
+
+        if (preg_match('/^'.$Block['char'].'{3,}[ ]*$/', $Line['text']))
+        {
+            $Block['element']['text']['text'] = substr($Block['element']['text']['text'], 1);
+
+            $Block['complete'] = true;
+
+            return $Block;
+        }
+
+        $Block['element']['text']['text'] .= "\n".$Line['body'];
+
+        return $Block;
+    }
+
+    protected function blockFencedCodeComplete($Block)
+    {
+        $text = $Block['element']['text']['text'];
+
+        $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
+
+        $Block['element']['text']['text'] = $text;
+
+        return $Block;
+    }
+
+    #
+    # Header
+
+    protected function blockHeader($Line)
+    {
+        if (isset($Line['text'][1]))
+        {
+            $level = 1;
+
+            while (isset($Line['text'][$level]) and $Line['text'][$level] === '#')
+            {
+                $level ++;
+            }
+
+            if ($level > 6)
+            {
+                return;
+            }
+
+            $text = trim($Line['text'], '# ');
+
+            $Block = array(
+                'element' => array(
+                    'name' => 'h' . min(6, $level),
+                    'text' => $text,
+                    'handler' => 'line',
+                ),
+            );
+
+            return $Block;
+        }
+    }
+
+    #
+    # List
+
+    protected function blockList($Line)
+    {
+        list($name, $pattern) = $Line['text'][0] <= '-' ? array('ul', '[*+-]') : array('ol', '[0-9]+[.]');
+
+        if (preg_match('/^('.$pattern.'[ ]+)(.*)/', $Line['text'], $matches))
+        {
+            $Block = array(
+                'indent' => $Line['indent'],
+                'pattern' => $pattern,
+                'element' => array(
+                    'name' => $name,
+                    'handler' => 'elements',
+                ),
+            );
+
+            if($name === 'ol') 
+            {
+                $listStart = stristr($matches[0], '.', true);
+                
+                if($listStart !== '1')
+                {
+                    $Block['element']['attributes'] = array('start' => $listStart);
+                }
+            }
+
+            $Block['li'] = array(
+                'name' => 'li',
+                'handler' => 'li',
+                'text' => array(
+                    $matches[2],
+                ),
+            );
+
+            $Block['element']['text'] []= & $Block['li'];
+
+            return $Block;
+        }
+    }
+
+    protected function blockListContinue($Line, array $Block)
+    {
+        if ($Block['indent'] === $Line['indent'] and preg_match('/^'.$Block['pattern'].'(?:[ ]+(.*)|$)/', $Line['text'], $matches))
+        {
+            if (isset($Block['interrupted']))
+            {
+                $Block['li']['text'] []= '';
+
+                unset($Block['interrupted']);
+            }
+
+            unset($Block['li']);
+
+            $text = isset($matches[1]) ? $matches[1] : '';
+
+            $Block['li'] = array(
+                'name' => 'li',
+                'handler' => 'li',
+                'text' => array(
+                    $text,
+                ),
+            );
+
+            $Block['element']['text'] []= & $Block['li'];
+
+            return $Block;
+        }
+
+        if ($Line['text'][0] === '[' and $this->blockReference($Line))
+        {
+            return $Block;
+        }
+
+        if ( ! isset($Block['interrupted']))
+        {
+            $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
+
+            $Block['li']['text'] []= $text;
+
+            return $Block;
+        }
+
+        if ($Line['indent'] > 0)
+        {
+            $Block['li']['text'] []= '';
+
+            $text = preg_replace('/^[ ]{0,4}/', '', $Line['body']);
+
+            $Block['li']['text'] []= $text;
+
+            unset($Block['interrupted']);
+
+            return $Block;
+        }
+    }
+
+    #
+    # Quote
+
+    protected function blockQuote($Line)
+    {
+        if (preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
+        {
+            $Block = array(
+                'element' => array(
+                    'name' => 'blockquote',
+                    'handler' => 'lines',
+                    'text' => (array) $matches[1],
+                ),
+            );
+
+            return $Block;
+        }
+    }
+
+    protected function blockQuoteContinue($Line, array $Block)
+    {
+        if ($Line['text'][0] === '>' and preg_match('/^>[ ]?(.*)/', $Line['text'], $matches))
+        {
+            if (isset($Block['interrupted']))
+            {
+                $Block['element']['text'] []= '';
+
+                unset($Block['interrupted']);
+            }
+
+            $Block['element']['text'] []= $matches[1];
+
+            return $Block;
+        }
+
+        if ( ! isset($Block['interrupted']))
+        {
+            $Block['element']['text'] []= $Line['text'];
+
+            return $Block;
+        }
+    }
+
+    #
+    # Rule
+
+    protected function blockRule($Line)
+    {
+        if (preg_match('/^(['.$Line['text'][0].'])([ ]*\1){2,}[ ]*$/', $Line['text']))
+        {
+            $Block = array(
+                'element' => array(
+                    'name' => 'hr'
+                ),
+            );
+
+            return $Block;
+        }
+    }
+
+    #
+    # Setext
+
+    protected function blockSetextHeader($Line, array $Block = null)
+    {
+        if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
+        {
+            return;
+        }
+
+        if (chop($Line['text'], $Line['text'][0]) === '')
+        {
+            $Block['element']['name'] = $Line['text'][0] === '=' ? 'h1' : 'h2';
+
+            return $Block;
+        }
+    }
+
+    #
+    # Markup
+
+    protected function blockMarkup($Line)
+    {
+        if ($this->markupEscaped)
+        {
+            return;
+        }
+
+        if (preg_match('/^<(\w[\w-]*)(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*(\/)?>/', $Line['text'], $matches))
+        {
+            $element = strtolower($matches[1]);
+
+            if (in_array($element, $this->textLevelElements))
+            {
+                return;
+            }
+
+            $Block = array(
+                'name' => $matches[1],
+                'depth' => 0,
+                'markup' => $Line['text'],
+            );
+
+            $length = strlen($matches[0]);
+
+            $remainder = substr($Line['text'], $length);
+
+            if (trim($remainder) === '')
+            {
+                if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
+                {
+                    $Block['closed'] = true;
+
+                    $Block['void'] = true;
+                }
+            }
+            else
+            {
+                if (isset($matches[2]) or in_array($matches[1], $this->voidElements))
+                {
+                    return;
+                }
+
+                if (preg_match('/<\/'.$matches[1].'>[ ]*$/i', $remainder))
+                {
+                    $Block['closed'] = true;
+                }
+            }
+
+            return $Block;
+        }
+    }
+
+    protected function blockMarkupContinue($Line, array $Block)
+    {
+        if (isset($Block['closed']))
+        {
+            return;
+        }
+
+        if (preg_match('/^<'.$Block['name'].'(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*>/i', $Line['text'])) # open
+        {
+            $Block['depth'] ++;
+        }
+
+        if (preg_match('/(.*?)<\/'.$Block['name'].'>[ ]*$/i', $Line['text'], $matches)) # close
+        {
+            if ($Block['depth'] > 0)
+            {
+                $Block['depth'] --;
+            }
+            else
+            {
+                $Block['closed'] = true;
+            }
+        }
+
+        if (isset($Block['interrupted']))
+        {
+            $Block['markup'] .= "\n";
+
+            unset($Block['interrupted']);
+        }
+
+        $Block['markup'] .= "\n".$Line['body'];
+
+        return $Block;
+    }
+
+    #
+    # Reference
+
+    protected function blockReference($Line)
+    {
+        if (preg_match('/^\[(.+?)\]:[ ]*<?(\S+?)>?(?:[ ]+["\'(](.+)["\')])?[ ]*$/', $Line['text'], $matches))
+        {
+            $id = strtolower($matches[1]);
+
+            $Data = array(
+                'url' => $matches[2],
+                'title' => null,
+            );
+
+            if (isset($matches[3]))
+            {
+                $Data['title'] = $matches[3];
+            }
+
+            $this->DefinitionData['Reference'][$id] = $Data;
+
+            $Block = array(
+                'hidden' => true,
+            );
+
+            return $Block;
+        }
+    }
+
+    #
+    # Table
+
+    protected function blockTable($Line, array $Block = null)
+    {
+        if ( ! isset($Block) or isset($Block['type']) or isset($Block['interrupted']))
+        {
+            return;
+        }
+
+        if (strpos($Block['element']['text'], '|') !== false and chop($Line['text'], ' -:|') === '')
+        {
+            $alignments = array();
+
+            $divider = $Line['text'];
+
+            $divider = trim($divider);
+            $divider = trim($divider, '|');
+
+            $dividerCells = explode('|', $divider);
+
+            foreach ($dividerCells as $dividerCell)
+            {
+                $dividerCell = trim($dividerCell);
+
+                if ($dividerCell === '')
+                {
+                    continue;
+                }
+
+                $alignment = null;
+
+                if ($dividerCell[0] === ':')
+                {
+                    $alignment = 'left';
+                }
+
+                if (substr($dividerCell, - 1) === ':')
+                {
+                    $alignment = $alignment === 'left' ? 'center' : 'right';
+                }
+
+                $alignments []= $alignment;
+            }
+
+            # ~
+
+            $HeaderElements = array();
+
+            $header = $Block['element']['text'];
+
+            $header = trim($header);
+            $header = trim($header, '|');
+
+            $headerCells = explode('|', $header);
+
+            foreach ($headerCells as $index => $headerCell)
+            {
+                $headerCell = trim($headerCell);
+
+                $HeaderElement = array(
+                    'name' => 'th',
+                    'text' => $headerCell,
+                    'handler' => 'line',
+                );
+
+                if (isset($alignments[$index]))
+                {
+                    $alignment = $alignments[$index];
+
+                    $HeaderElement['attributes'] = array(
+                        'style' => 'text-align: '.$alignment.';',
+                    );
+                }
+
+                $HeaderElements []= $HeaderElement;
+            }
+
+            # ~
+
+            $Block = array(
+                'alignments' => $alignments,
+                'identified' => true,
+                'element' => array(
+                    'name' => 'table',
+                    'handler' => 'elements',
+                ),
+            );
+
+            $Block['element']['text'] []= array(
+                'name' => 'thead',
+                'handler' => 'elements',
+            );
+
+            $Block['element']['text'] []= array(
+                'name' => 'tbody',
+                'handler' => 'elements',
+                'text' => array(),
+            );
+
+            $Block['element']['text'][0]['text'] []= array(
+                'name' => 'tr',
+                'handler' => 'elements',
+                'text' => $HeaderElements,
+            );
+
+            return $Block;
+        }
+    }
+
+    protected function blockTableContinue($Line, array $Block)
+    {
+        if (isset($Block['interrupted']))
+        {
+            return;
+        }
+
+        if ($Line['text'][0] === '|' or strpos($Line['text'], '|'))
+        {
+            $Elements = array();
+
+            $row = $Line['text'];
+
+            $row = trim($row);
+            $row = trim($row, '|');
+
+            preg_match_all('/(?:(\\\\[|])|[^|`]|`[^`]+`|`)+/', $row, $matches);
+
+            foreach ($matches[0] as $index => $cell)
+            {
+                $cell = trim($cell);
+
+                $Element = array(
+                    'name' => 'td',
+                    'handler' => 'line',
+                    'text' => $cell,
+                );
+
+                if (isset($Block['alignments'][$index]))
+                {
+                    $Element['attributes'] = array(
+                        'style' => 'text-align: '.$Block['alignments'][$index].';',
+                    );
+                }
+
+                $Elements []= $Element;
+            }
+
+            $Element = array(
+                'name' => 'tr',
+                'handler' => 'elements',
+                'text' => $Elements,
+            );
+
+            $Block['element']['text'][1]['text'] []= $Element;
+
+            return $Block;
+        }
+    }
+
+    #
+    # ~
+    #
+
+    protected function paragraph($Line)
+    {
+        $Block = array(
+            'element' => array(
+                'name' => 'p',
+                'text' => $Line['text'],
+                'handler' => 'line',
+            ),
+        );
+
+        return $Block;
+    }
+
+    #
+    # Inline Elements
+    #
+
+    protected $InlineTypes = array(
+        '"' => array('SpecialCharacter'),
+        '!' => array('Image'),
+        '&' => array('SpecialCharacter'),
+        '*' => array('Emphasis'),
+        ':' => array('Url'),
+        '<' => array('UrlTag', 'EmailTag', 'Markup', 'SpecialCharacter'),
+        '>' => array('SpecialCharacter'),
+        '[' => array('Link'),
+        '_' => array('Emphasis'),
+        '`' => array('Code'),
+        '~' => array('Strikethrough'),
+        '\\' => array('EscapeSequence'),
+    );
+
+    # ~
+
+    protected $inlineMarkerList = '!"*_&[:<>`~\\';
+
+    #
+    # ~
+    #
+
+    public function line($text)
+    {
+        $markup = '';
+
+        # $excerpt is based on the first occurrence of a marker
+
+        while ($excerpt = strpbrk($text, $this->inlineMarkerList))
+        {
+            $marker = $excerpt[0];
+
+            $markerPosition = strpos($text, $marker);
+
+            $Excerpt = array('text' => $excerpt, 'context' => $text);
+
+            foreach ($this->InlineTypes[$marker] as $inlineType)
+            {
+                $Inline = $this->{'inline'.$inlineType}($Excerpt);
+
+                if ( ! isset($Inline))
+                {
+                    continue;
+                }
+
+                # makes sure that the inline belongs to "our" marker
+
+                if (isset($Inline['position']) and $Inline['position'] > $markerPosition)
+                {
+                    continue;
+                }
+
+                # sets a default inline position
+
+                if ( ! isset($Inline['position']))
+                {
+                    $Inline['position'] = $markerPosition;
+                }
+
+                # the text that comes before the inline
+                $unmarkedText = substr($text, 0, $Inline['position']);
+
+                # compile the unmarked text
+                $markup .= $this->unmarkedText($unmarkedText);
+
+                # compile the inline
+                $markup .= isset($Inline['markup']) ? $Inline['markup'] : $this->element($Inline['element']);
+
+                # remove the examined text
+                $text = substr($text, $Inline['position'] + $Inline['extent']);
+
+                continue 2;
+            }
+
+            # the marker does not belong to an inline
+
+            $unmarkedText = substr($text, 0, $markerPosition + 1);
+
+            $markup .= $this->unmarkedText($unmarkedText);
+
+            $text = substr($text, $markerPosition + 1);
+        }
+
+        $markup .= $this->unmarkedText($text);
+
+        return $markup;
+    }
+
+    #
+    # ~
+    #
+
+    protected function inlineCode($Excerpt)
+    {
+        $marker = $Excerpt['text'][0];
+
+        if (preg_match('/^('.$marker.'+)[ ]*(.+?)[ ]*(?<!'.$marker.')\1(?!'.$marker.')/s', $Excerpt['text'], $matches))
+        {
+            $text = $matches[2];
+            $text = htmlspecialchars($text, ENT_NOQUOTES, 'UTF-8');
+            $text = preg_replace("/[ ]*\n/", ' ', $text);
+
+            return array(
+                'extent' => strlen($matches[0]),
+                'element' => array(
+                    'name' => 'code',
+                    'text' => $text,
+                ),
+            );
+        }
+    }
+
+    protected function inlineEmailTag($Excerpt)
+    {
+        if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<((mailto:)?\S+?@\S+?)>/i', $Excerpt['text'], $matches))
+        {
+            $url = $matches[1];
+
+            if ( ! isset($matches[2]))
+            {
+                $url = 'mailto:' . $url;
+            }
+
+            return array(
+                'extent' => strlen($matches[0]),
+                'element' => array(
+                    'name' => 'a',
+                    'text' => $matches[1],
+                    'attributes' => array(
+                        'href' => $url,
+                    ),
+                ),
+            );
+        }
+    }
+
+    protected function inlineEmphasis($Excerpt)
+    {
+        if ( ! isset($Excerpt['text'][1]))
+        {
+            return;
+        }
+
+        $marker = $Excerpt['text'][0];
+
+        if ($Excerpt['text'][1] === $marker and preg_match($this->StrongRegex[$marker], $Excerpt['text'], $matches))
+        {
+            $emphasis = 'strong';
+        }
+        elseif (preg_match($this->EmRegex[$marker], $Excerpt['text'], $matches))
+        {
+            $emphasis = 'em';
+        }
+        else
+        {
+            return;
+        }
+
+        return array(
+            'extent' => strlen($matches[0]),
+            'element' => array(
+                'name' => $emphasis,
+                'handler' => 'line',
+                'text' => $matches[1],
+            ),
+        );
+    }
+
+    protected function inlineEscapeSequence($Excerpt)
+    {
+        if (isset($Excerpt['text'][1]) and in_array($Excerpt['text'][1], $this->specialCharacters))
+        {
+            return array(
+                'markup' => $Excerpt['text'][1],
+                'extent' => 2,
+            );
+        }
+    }
+
+    protected function inlineImage($Excerpt)
+    {
+        if ( ! isset($Excerpt['text'][1]) or $Excerpt['text'][1] !== '[')
+        {
+            return;
+        }
+
+        $Excerpt['text']= substr($Excerpt['text'], 1);
+
+        $Link = $this->inlineLink($Excerpt);
+
+        if ($Link === null)
+        {
+            return;
+        }
+
+        $Inline = array(
+            'extent' => $Link['extent'] + 1,
+            'element' => array(
+                'name' => 'img',
+                'attributes' => array(
+                    'src' => $Link['element']['attributes']['href'],
+                    'alt' => $Link['element']['text'],
+                ),
+            ),
+        );
+
+        $Inline['element']['attributes'] += $Link['element']['attributes'];
+
+        unset($Inline['element']['attributes']['href']);
+
+        return $Inline;
+    }
+
+    protected function inlineLink($Excerpt)
+    {
+        $Element = array(
+            'name' => 'a',
+            'handler' => 'line',
+            'text' => null,
+            'attributes' => array(
+                'href' => null,
+                'title' => null,
+            ),
+        );
+
+        $extent = 0;
+
+        $remainder = $Excerpt['text'];
+
+        if (preg_match('/\[((?:[^][]++|(?R))*+)\]/', $remainder, $matches))
+        {
+            $Element['text'] = $matches[1];
+
+            $extent += strlen($matches[0]);
+
+            $remainder = substr($remainder, $extent);
+        }
+        else
+        {
+            return;
+        }
+
+        if (preg_match('/^[(]\s*+((?:[^ ()]++|[(][^ )]+[)])++)(?:[ ]+("[^"]*"|\'[^\']*\'))?\s*[)]/', $remainder, $matches))
+        {
+            $Element['attributes']['href'] = $matches[1];
+
+            if (isset($matches[2]))
+            {
+                $Element['attributes']['title'] = substr($matches[2], 1, - 1);
+            }
+
+            $extent += strlen($matches[0]);
+        }
+        else
+        {
+            if (preg_match('/^\s*\[(.*?)\]/', $remainder, $matches))
+            {
+                $definition = strlen($matches[1]) ? $matches[1] : $Element['text'];
+                $definition = strtolower($definition);
+
+                $extent += strlen($matches[0]);
+            }
+            else
+            {
+                $definition = strtolower($Element['text']);
+            }
+
+            if ( ! isset($this->DefinitionData['Reference'][$definition]))
+            {
+                return;
+            }
+
+            $Definition = $this->DefinitionData['Reference'][$definition];
+
+            $Element['attributes']['href'] = $Definition['url'];
+            $Element['attributes']['title'] = $Definition['title'];
+        }
+
+        $Element['attributes']['href'] = str_replace(array('&', '<'), array('&amp;', '&lt;'), $Element['attributes']['href']);
+
+        return array(
+            'extent' => $extent,
+            'element' => $Element,
+        );
+    }
+
+    protected function inlineMarkup($Excerpt)
+    {
+        if ($this->markupEscaped or strpos($Excerpt['text'], '>') === false)
+        {
+            return;
+        }
+
+        if ($Excerpt['text'][1] === '/' and preg_match('/^<\/\w[\w-]*[ ]*>/s', $Excerpt['text'], $matches))
+        {
+            return array(
+                'markup' => $matches[0],
+                'extent' => strlen($matches[0]),
+            );
+        }
+
+        if ($Excerpt['text'][1] === '!' and preg_match('/^<!---?[^>-](?:-?[^-])*-->/s', $Excerpt['text'], $matches))
+        {
+            return array(
+                'markup' => $matches[0],
+                'extent' => strlen($matches[0]),
+            );
+        }
+
+        if ($Excerpt['text'][1] !== ' ' and preg_match('/^<\w[\w-]*(?:[ ]*'.$this->regexHtmlAttribute.')*[ ]*\/?>/s', $Excerpt['text'], $matches))
+        {
+            return array(
+                'markup' => $matches[0],
+                'extent' => strlen($matches[0]),
+            );
+        }
+    }
+
+    protected function inlineSpecialCharacter($Excerpt)
+    {
+        if ($Excerpt['text'][0] === '&' and ! preg_match('/^&#?\w+;/', $Excerpt['text']))
+        {
+            return array(
+                'markup' => '&amp;',
+                'extent' => 1,
+            );
+        }
+
+        $SpecialCharacter = array('>' => 'gt', '<' => 'lt', '"' => 'quot');
+
+        if (isset($SpecialCharacter[$Excerpt['text'][0]]))
+        {
+            return array(
+                'markup' => '&'.$SpecialCharacter[$Excerpt['text'][0]].';',
+                'extent' => 1,
+            );
+        }
+    }
+
+    protected function inlineStrikethrough($Excerpt)
+    {
+        if ( ! isset($Excerpt['text'][1]))
+        {
+            return;
+        }
+
+        if ($Excerpt['text'][1] === '~' and preg_match('/^~~(?=\S)(.+?)(?<=\S)~~/', $Excerpt['text'], $matches))
+        {
+            return array(
+                'extent' => strlen($matches[0]),
+                'element' => array(
+                    'name' => 'del',
+                    'text' => $matches[1],
+                    'handler' => 'line',
+                ),
+            );
+        }
+    }
+
+    protected function inlineUrl($Excerpt)
+    {
+        if ($this->urlsLinked !== true or ! isset($Excerpt['text'][2]) or $Excerpt['text'][2] !== '/')
+        {
+            return;
+        }
+
+        if (preg_match('/\bhttps?:[\/]{2}[^\s<]+\b\/*/ui', $Excerpt['context'], $matches, PREG_OFFSET_CAPTURE))
+        {
+            $Inline = array(
+                'extent' => strlen($matches[0][0]),
+                'position' => $matches[0][1],
+                'element' => array(
+                    'name' => 'a',
+                    'text' => $matches[0][0],
+                    'attributes' => array(
+                        'href' => $matches[0][0],
+                    ),
+                ),
+            );
+
+            return $Inline;
+        }
+    }
+
+    protected function inlineUrlTag($Excerpt)
+    {
+        if (strpos($Excerpt['text'], '>') !== false and preg_match('/^<(\w+:\/{2}[^ >]+)>/i', $Excerpt['text'], $matches))
+        {
+            $url = str_replace(array('&', '<'), array('&amp;', '&lt;'), $matches[1]);
+
+            return array(
+                'extent' => strlen($matches[0]),
+                'element' => array(
+                    'name' => 'a',
+                    'text' => $url,
+                    'attributes' => array(
+                        'href' => $url,
+                    ),
+                ),
+            );
+        }
+    }
+
+    # ~
+
+    protected function unmarkedText($text)
+    {
+        if ($this->breaksEnabled)
+        {
+            $text = preg_replace('/[ ]*\n/', "<br />\n", $text);
+        }
+        else
+        {
+            $text = preg_replace('/(?:[ ][ ]+|[ ]*\\\\)\n/', "<br />\n", $text);
+            $text = str_replace(" \n", "\n", $text);
+        }
+
+        return $text;
+    }
+
+    #
+    # Handlers
+    #
+
+    protected function element(array $Element)
+    {
+        $markup = '<'.$Element['name'];
+
+        if (isset($Element['attributes']))
+        {
+            foreach ($Element['attributes'] as $name => $value)
+            {
+                if ($value === null)
+                {
+                    continue;
+                }
+
+                $markup .= ' '.$name.'="'.$value.'"';
+            }
+        }
+
+        if (isset($Element['text']))
+        {
+            $markup .= '>';
+
+            if (isset($Element['handler']))
+            {
+                $markup .= $this->{$Element['handler']}($Element['text']);
+            }
+            else
+            {
+                $markup .= $Element['text'];
+            }
+
+            $markup .= '</'.$Element['name'].'>';
+        }
+        else
+        {
+            $markup .= ' />';
+        }
+
+        return $markup;
+    }
+
+    protected function elements(array $Elements)
+    {
+        $markup = '';
+
+        foreach ($Elements as $Element)
+        {
+            $markup .= "\n" . $this->element($Element);
+        }
+
+        $markup .= "\n";
+
+        return $markup;
+    }
+
+    # ~
+
+    protected function li($lines)
+    {
+        $markup = $this->lines($lines);
+
+        $trimmedMarkup = trim($markup);
+
+        if ( ! in_array('', $lines) and substr($trimmedMarkup, 0, 3) === '<p>')
+        {
+            $markup = $trimmedMarkup;
+            $markup = substr($markup, 3);
+
+            $position = strpos($markup, "</p>");
+
+            $markup = substr_replace($markup, '', $position, 4);
+        }
+
+        return $markup;
+    }
+
+    #
+    # Deprecated Methods
+    #
+
+    function parse($text)
+    {
+        $markup = $this->text($text);
+
+        return $markup;
+    }
+
+    #
+    # Static Methods
+    #
+
+    static function instance($name = 'default')
+    {
+        if (isset(self::$instances[$name]))
+        {
+            return self::$instances[$name];
+        }
+
+        $instance = new static();
+
+        self::$instances[$name] = $instance;
+
+        return $instance;
+    }
+
+    private static $instances = array();
+
+    #
+    # Fields
+    #
+
+    protected $DefinitionData;
+
+    #
+    # Read-Only
+
+    protected $specialCharacters = array(
+        '\\', '`', '*', '_', '{', '}', '[', ']', '(', ')', '>', '#', '+', '-', '.', '!', '|',
+    );
+
+    protected $StrongRegex = array(
+        '*' => '/^[*]{2}((?:\\\\\*|[^*]|[*][^*]*[*])+?)[*]{2}(?![*])/s',
+        '_' => '/^__((?:\\\\_|[^_]|_[^_]*_)+?)__(?!_)/us',
+    );
+
+    protected $EmRegex = array(
+        '*' => '/^[*]((?:\\\\\*|[^*]|[*][*][^*]+?[*][*])+?)[*](?![*])/s',
+        '_' => '/^_((?:\\\\_|[^_]|__[^_]*__)+?)_(?!_)\b/us',
+    );
+
+    protected $regexHtmlAttribute = '[a-zA-Z_:][\w:.-]*(?:\s*=\s*(?:[^"\'=<>`\s]+|"[^"]*"|\'[^\']*\'))?';
+
+    protected $voidElements = array(
+        'area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'link', 'meta', 'param', 'source',
+    );
+
+    protected $textLevelElements = array(
+        'a', 'br', 'bdo', 'abbr', 'blink', 'nextid', 'acronym', 'basefont',
+        'b', 'em', 'big', 'cite', 'small', 'spacer', 'listing',
+        'i', 'rp', 'del', 'code',          'strike', 'marquee',
+        'q', 'rt', 'ins', 'font',          'strong',
+        's', 'tt', 'kbd', 'mark',
+        'u', 'xm', 'sub', 'nobr',
+                   'sup', 'ruby',
+                   'var', 'span',
+                   'wbr', 'time',
+    );
+}

+ 1 - 0
WriterA/description.txt

@@ -0,0 +1 @@
+SimpleMDE powered Markdown text editor for ArOZ Online System

+ 90 - 0
WriterA/documentIO.php

@@ -0,0 +1,90 @@
+<?php
+include_once("../auth.php");
+if (isset($_GET['filepath']) && $_GET['filepath'] != ""){
+    //Get the content of the 
+    $content = "";
+    if (file_exists("../" . $_GET['filepath'])){
+        //This filepath is represented from AOR
+        $content = file_get_contents("../" . $_GET['filepath']);
+    }else if (file_exists($_GET['filepath']) && strpos($_GET['savepath'],"/media") == 0){
+        //This file is represented using real path and it is located inside /media
+        $content = file_get_contents($_GET['filepath']);
+    }else{
+        die("ERROR. file not found.");
+    }
+    echo json_encode($content);
+    exit(0);
+}else if (isset($_POST['create']) && $_POST['create'] != "" && isset($_POST['content']) && $_POST['content'] != ""){
+    //The create path can be start with AOR or /media
+    $content = json_decode($_POST['content']);
+    if (strpos($_POST['create'],"/media") === 0){
+        //This is a file in external storage
+        if (!file_exists($_POST['create'])){
+            file_put_contents($_POST['create'],$content);
+            die($_POST['create']);
+        }else{
+            die("ERROR. File already exists.");
+        }
+    }else{
+        //This is a filepath from AOR
+        //Check if it is a valid path by checking if the realpath contains the AOR as part of its path
+        $root = realpath("../");
+        $target = realpath(dirname("../" . $_POST['create']));
+        if (strpos($target,$root) === false){
+            //This path is not inside AOR. not a valid path.
+            die("ERROR. Filepath out of AOR");
+        }
+        $filename ="../" . $_POST['create'];
+        if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN' && preg_match('/[^A-Za-z0-9]/',$filename)) {
+            //If this is window host, and the filename contains non alphebet numeric values
+            $tmp = explode("/",$filename);
+            $filename = array_pop($tmp);
+            $targetDir = implode("/",$tmp);
+            $ftmp = explode(".",$filename);
+            $ext = array_pop($ftmp);
+            $filename = implode(".",$ftmp);
+            $umfilename = "inith" . bin2hex($filename);
+            $filename = $targetDir . "/" . $umfilename . "." .  $ext;
+        }
+        if (!file_exists($filename)){
+            file_put_contents($filename,$content);
+            echo $filename;
+            exit(0);
+        }else{
+            die("ERROR. File already exists.");
+        }
+    }
+}else if (isset($_POST['content']) && $_POST['content'] != "" && isset($_POST['savepath']) && $_POST['savepath'] != ""){
+     $content =  json_decode($_POST['content']);
+    if (file_exists("../" . $_POST['savepath'])){
+        //This filepath is represented from AOR
+        $root = realpath("../");
+        $target = realpath(dirname("../" . $_POST['savepath']));
+        if (strpos($target,$root) === false){
+            //This path is not inside AOR. not a valid path.
+            die("ERROR. Filepath out of AOR");
+        }
+        file_put_contents("../" . $_POST['savepath'],$content);
+        echo "DONE";
+        exit(0);
+    }else if (file_exists($_POST['savepath']) && strpos($_POST['savepath'],"/media") == 0){
+        //This file is represented using real path and it is located inside /media
+        file_put_contents($_POST['savepath'],$content);
+        echo "DONE";
+        exit(0);
+    }else{
+        die("ERROR. file not found. " . $_POST['savepath'] . " was given.");
+    }
+    exit(0);
+}else if (isset($_POST['parseMD']) && $_POST['parseMD']){
+	//parseMD will pass in the content of the document as JSON string via POST
+	include_once("Parsedown.php");
+	$content = json_decode($_POST['parseMD']);
+	$Parsedown = new Parsedown();
+	echo $Parsedown->text($content);
+	exit(0);
+}else{
+    die("ERROR. Missing variables.");
+}
+
+?>

+ 134 - 0
WriterA/embedded.php

@@ -0,0 +1,134 @@
+<!DOCTYPE html>
+<meta name="apple-mobile-web-app-capable" content="yes" />
+<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=0.6, maximum-scale=0.6"/>
+<html>
+<head>
+<meta charset="UTF-8">
+<script type='text/javascript' charset='utf-8'>
+    // Hides mobile browser's address bar when page is done loading.
+      window.addEventListener('load', function(e) {
+        setTimeout(function() { window.scrollTo(0, 1); }, 1);
+      }, false);
+</script>
+<title>View Document</title>
+<style type="text/css">
+        body {
+            padding: 20px 20px;
+        }
+		.CodeMirror, .CodeMirror-scroll {
+			max-height: 500px;
+		}
+		.p{
+			font-family: monospace, monospace;
+		}
+</style>
+<link rel="stylesheet" href="../script/tocas/tocas.css">
+<script src="../script/tocas/tocas.js"></script>
+<script src="../script/jquery.min.js"></script>
+<script src="../script/ao_module.js"></script>
+</head>
+<body style="background-color:#ededed;">
+<?php
+//Prepare the document
+$docName = "";
+$filePath = "";
+$mimeType = "";
+if (isset($_GET['filepath']) && $_GET['filepath'] != ""){
+	if (file_exists($_GET['filepath'])){
+		$filePath = trim($_GET['filepath']);
+		$mimeType = mime_content_type($filePath);
+	}else{
+	    if (strpos($_GET['filepath'],"../SystemAOB/functions/extDiskAccess.php?file=") === 0){
+	        //This is a file in external storage. Extract the filepath from extDiskAccess path
+	        $tmp = explode(".php?file=",$_GET['filepath']);
+	        $filePath = trim($tmp[1]);
+	        $mimeType = mime_content_type($filePath);
+	    }
+	}
+}
+if (isset($_GET['filename']) && $_GET['filename'] != ""){
+	$docName = $_GET['filename'];
+}
+?>
+	<div style="position:absolute; background-color:white;left:2%;right:2%;">
+	<div class="ts text container" style="font-size: 100%;overflow-wrap: break-word;font-family: monospace, monospace;">
+	<br>
+<?php
+$generalType = explode("/",$mimeType)[0];
+if ($filePath != "" && $generalType=="text"){
+	include_once("Parsedown.php");
+	$Parsedown = new Parsedown();
+	echo $Parsedown->text(file_get_contents($filePath));
+}else if (explode("/",$mimeType)[1] == "x-empty"){
+    //This is an empty file. Go ahead and allow something to be written in.
+    echo '<code>[WARNING] This is an empty file.</code>';
+
+}else{
+	echo '[Error. Unable to open file due to not supported datatype] <br>';
+	echo "Sorry. This file format is not supported. <br> " . mime_content_type($filePath) . " mime type was given. <br> filepath: " . $filePath;
+	
+}
+
+?>
+<br><br><br><br>
+<div class="ts horizontal divider">End of File</div>
+<br><br><br><br>
+	</div>
+	</div>
+<div style="position:fixed;right:1%;top 1%;" onClick="editFile();">
+	<div style="font-size:250%;"><i class="edit icon"></i></div>
+</div>
+<script>
+	var filePath = "<?php echo $filePath;?>";
+	var fileName = "<?php echo $docName;?>";
+	var inVDI = !(!parent.isFunctionBar);
+	if (inVDI){
+		var windowID = $(window.frameElement).parent().attr("id");
+		parent.setWindowIcon(windowID + "","file text outline");
+		parent.changeWindowTitle(windowID + "",fileName);
+	}
+	
+	//Force embed tocas table into the parse result
+	$("table").each(function(){
+	    $(this).addClass("ts").addClass("small").addClass("celled").addClass("table");
+	});
+	function editFile(){
+		if (ao_module_virtualDesktop){
+			if (filePath.includes("../")){
+				filePath = filePath.replace("../",""); //Replace the first ../ with nothing
+			}
+			ao_module_newfw('WriterA/index.php?filepath=' + filePath,'Initializing - WriterA','file text outline',ao_module_utils.getRandomUID(),1050,550);
+			ao_module_close();
+		}else{
+			window.open("index.php?filepath=" + filePath);
+		}
+		
+	}
+	function BrowseFileInFS(){
+		 alert("wip");
+	 }
+	 
+	 function DownloadDoc(){
+		 saveAs(filePath,fileName);
+	 }
+	 
+	 function ShareDoc(){
+		 window.location.href="../QuickSend/index.php?share=" + window.location.href.replace("&","<and>");
+		 
+	 }
+
+	function saveAs(uri, filename) {
+		var link = document.createElement('a');
+		if (typeof link.download === 'string') {
+			document.body.appendChild(link); // Firefox requires the link to be in the body
+			link.download = filename;
+			link.href = uri;
+			link.click();
+			document.body.removeChild(link); // remove the link when done
+		} else {
+			location.replace(uri);
+		}
+	}
+</script>
+</body>
+</html>

BIN
WriterA/image.png


+ 12 - 0
WriterA/imageLoader.php

@@ -0,0 +1,12 @@
+<?php
+include_once("../auth.php");
+$images = glob("uploads/*");
+$result = [];
+foreach ($images as $image){
+	if (is_file($image)){
+		array_push($result,$image);
+	}
+}
+header('Content-Type: application/json');
+echo json_encode($result);
+?>

BIN
WriterA/img/desktop_icon.png


BIN
WriterA/img/desktop_icon.psd


BIN
WriterA/img/function_icon.png


BIN
WriterA/img/function_icon.psd


BIN
WriterA/img/small_icon.png


BIN
WriterA/img/small_icon.psd


+ 1127 - 0
WriterA/index.php

@@ -0,0 +1,1127 @@
+<!DOCTYPE html>
+<meta name="apple-mobile-web-app-capable" content="yes" />
+<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1"/>
+<html>
+<head>
+<meta charset="UTF-8">
+<script type='text/javascript' charset='utf-8'>
+    // Hides mobile browser's address bar when page is done loading.
+      window.addEventListener('load', function(e) {
+        setTimeout(function() { window.scrollTo(0, 1); }, 1);
+      }, false);
+</script>
+<title>WriterA</title>
+<script src="../script/jquery.min.js"></script>
+<link rel="stylesheet" href="mde/simplemde.min.css">
+<script src="../script/ao_module.js"></script>
+<script src="mde/simplemde.min.js"></script>
+<script src="jspdf.min.js"></script>
+<style>
+body{
+	margin:0px;
+	font-family:Georgia;
+	background-color:#f9f9f9;
+}
+
+.topmenu{
+    font-size:90%;
+	margin-bottom: 0px !important;
+	width:100%;
+	font-family: Arial, Helvetica, sans-serif;
+	background-color:#f9f9f9;
+}
+
+#status{
+	font-size:80%; 
+	text-overflow: ellipsis !important; 
+	overflow: hidden !important; 
+	padding-left:20px;
+	padding-top:5px;
+}
+
+.rightcorner{
+	position:fixed;
+	top:5px;
+	right:5px;
+	z-index:999;
+	cursor: pointer;
+}
+
+.button {
+  color: black;
+  border: none;
+  padding: 1px 10px;
+  text-align: center;
+  text-decoration: none;
+  display: inline;
+  cursor: pointer;
+    -webkit-user-select: none; /* Safari */        
+    -moz-user-select: none; /* Firefox */
+    -ms-user-select: none; /* IE10+/Edge */
+    user-select: none; /* Standard */
+}
+
+.button:hover{
+    background-color:#efefef;
+}
+
+.item{
+	height: 35px;
+	display: inline;
+}
+
+#main{
+	top:35px;
+    font-family: Georgia;
+}
+
+#contextMenu{
+    min-width:100px;
+    background-color:rgb(247, 247, 247);
+    position:fixed;
+    top:0px;
+    left:0px;
+    border: 1px solid #9e9e9e;
+    z-index:999;
+    padding-left:14px;
+    padding-top:8px;
+    display:none;
+    padding-bottom:10px;
+}
+
+.menuItem{
+    padding-bottom:3px;
+    padding-left:5px;
+    padding-right:15px;
+    border-left: 1px solid #9e9e9e;
+    cursor:pointer;
+}
+
+.menuItem:hover{
+    background-color:#e3e3e3;
+}
+
+.tooltip {
+  position: relative;
+  display: inline-block;
+  border-bottom: 1px dotted black;
+}
+
+.tooltip .tooltiptext {
+  visibility: hidden;
+  width: 200px;
+  background-color: rgba(61,61,61, 0.8);
+  color: #fff;
+  text-align: center;
+  border-radius: 6px;
+  padding: 5px 0;
+  
+  /* Position the tooltip */
+  position: absolute;
+  z-index: 1;
+  top: 100%;
+  left: 50%;
+  margin-left: -150px;
+}
+
+.tooltip:hover .tooltiptext {
+  visibility: visible;
+}
+
+.centered{
+    z-index:999;
+    position:fixed;
+    left:20%;
+    right:20%;
+    top:10%;
+    max-height:80%;
+    padding:20px;
+    background-color:#f9f9f9;
+    border: 1px solid #cccccc;
+}
+
+#specialCharInsert{
+    position:fixed !important;
+    z-index:999 !important;
+    position:fixed !important;
+    left:20% !important;
+    right:20% !important;
+    top:10% !important;
+    height:80%;
+    padding:20px;
+    background-color:#ededed;
+    box-shadow: 3px 3px 4px #f9f9f9;
+    
+}
+
+.scs{
+	display:inline-block;
+	margin: 3px;
+	padding-left: 3px;
+	padding-top: 3px;
+	width: 20px !important;
+	height:30px;
+	border: 1px solid #c4c4c4;
+	cursor:pointer;
+	font-weight: bold;
+}
+.scs:hover{
+	border: 1px solid #6e86a0;
+	background-color:#a9c7e8;
+}
+
+.selectable{
+    border: 2px solid transparent !important;
+    margin: 5px !important;
+}
+.selectable:hover{
+    border:2px solid #2bb5ff !important;
+    
+}
+.selected{
+    border:2px solid #2bb5ff !important;
+}
+
+.closebtn{
+    position:absolute;
+    top:-10px;
+    right:-10px;
+    width:30px !important;
+    height:30px !important;
+    border: 1px solid #4c4c4c;
+
+}
+.image{
+    max-height:200px;
+}
+.fluid{
+    width: 80% !important;
+}
+.primary{
+    border: 1px solid #3b3b3b;
+}
+</style>
+</head>
+<body>
+<div class="topmenu">
+    <div style="padding:5px;padding-left:10px;">
+    	<a id="backBtn" class="item" href="../index.php">⬅️</a>
+        <a class="item" style="font-size:120%;margin-right:15px;">🖋 WriterA</a>
+        <a class="item button" onClick = "showContextMenu(this);">File</a>
+        <a class="item button" onClick = "showContextMenu(this);">Edit</a>
+    	<a class="item button" onClick = "showContextMenu(this);">Help</a>
+    	<a id="extInputDisplay" class="rightcorner" style="color:red;" onClick="toggleExternalInputMode();">
+    		⌨ AIME
+    	</a>
+    	<div style="padding-top:3px;padding-left:5px;">
+    	<a class="item" id="status" style="padding-top:10px !important;"></a>
+    	</div>
+	</div>
+</div>
+<div id="main">
+<textarea id="mde"></textarea>
+</div>
+<div id="contextMenu">
+    <div class="menuItem">Loading...</div>
+    
+</div>
+<div id="selectImage" style="display:none;" class="centered">
+    <div id="previewArea" style="height:350px;overflow-y: auto;" align="left">
+    </div>
+    <div style="position:absolute;right:30px;bottom:30px;background-color:white;">
+        <button class="ts tiny primary button" onClick="insertSelectedImages();">Insert</button>
+        <button class="ts tiny basic button" onClick="$(this).parent().parent().hide();">Cancel</button>
+    </div>
+     <button class="closebtn button"  onClick="$(this).parent().hide();">X</button>
+</div>
+<div id="insertImage" style="display:none;" class="centered">
+     <p><i class="file image outline icon"></i>Insert Image</p>
+    <iframe width="100%" height="260px" src="../Upload%20Manager/upload_interface_min.php?target=WriterA&filetype=png,jpg,jpeg"> </iframe>
+    <button class="ts right floated tiny basic button" onClick="$(this).parent().hide();">Cancel</button>
+    <button class="ts right floated tiny primary button" onClick="initImageSelector();">Insert</button>
+    <button class="button closebtn"  onClick="$(this).parent().hide();">X</button>
+</div>
+<div id="saveNewDocument" style="display:none;" class="centered">
+    <p><i class="save icon"></i>💾 Save As New Document</p>
+    <p>Storage Directory</p>
+    <div class="ts mini fluid action left icon input">
+        <input class="fluid" type="text" id="newStorageDirectory" placeholder="Storage Path" onClick="ao_module_focus();">
+        <button style="display:inline-block;" class="ts primary button" onClick="selectCreatePath();">Open</button>
+    </div><br><p>Document Filename</p>
+    <div class="ts left icon mini fluid input">
+        <input class="fluid" id="createFileName" type="text" placeholder="Filename"  onClick="ao_module_focus();">
+        <i class="file text outline icon"></i>
+    </div>
+    <br><br>
+    <div id="createError" style="display:none;" class="ts inverted mini negative segment">
+        <p id="createErrorMessage">Loading...</p>
+    </div>
+    <button class="ts tiny primary button" onClick="$(this).parent().hide(); saveAs=false;">Cancel</button>
+    <button class="ts tiny primary button" onClick="confirmCreateNewDocument();">Confirm</button>
+    <button class="closebtn button"  onClick="$(this).parent().hide(); saveAs=false;">X</button>
+</div>
+<div id="information" style="display:none;" class="centered">
+    <div style="overflow-y:auto;height:300px">
+        <h4><i class="write icon"></i>WriterA for ArOZ Online System</h4>
+        <p>WriterA is developed by Toby Chui for the ArOZ Online System. <br>
+        Originate from the ArOZ Document (which has been deprecated), the WriterA is a new generation of Mark Down Editor powered by SimpleMDE and ArOZ File System.
+        Providing the power of simple yet quick editing within the ArOZ Virtual Desktop Environment as well as Document Editing under Normal Web View Mode.<br><br>
+        WriterA support new generations of ArOZ Online API including ArOZ IME, floatWindows and File Open API from the latest ArOZ Online Standard. 
+        Please reference the README.txt included with the module for development details.<br><br><br>
+        <small style="font-size:80%">Developed since March 2019, Project under ArOZ Online System feat. IMUS Laboratory</small>
+        </p>
+    </div>
+    <button class="closebtn button" onClick="$(this).parent().hide();">X</button>
+</div>
+<div id="license" style="display:none;" class="centered">
+    <div style="overflow-y:auto;height:300px">
+        <p>Project licensed under MIT License<br> Copyright 2019 Toby Chui</p>
+        <p style="font-size:80%">MIT License <br>Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.</p>
+    </div>
+     <button class="button closebtn" onClick="$(this).parent().hide();">X</button>
+</div>
+<div id="specialCharInsert" class="centered ts segment" style="display:none;">
+	Insert Special Character
+	<br>
+	<div id="iscl" style="max-height:70%;left:0;right:0;overflow-y:scroll;overflow-wrap: break-word;">
+			
+	</div>
+	<hr>
+	<button style="background-color:white;border: 1px solid #707070;padding:3px;cursor:pointer;" onClick="$('#specialCharInsert').hide();">Close</button>
+	<p style="display:inline-block;padding-left:20px;" id="scid">N/A</p>
+</div>
+<div style="display:none">
+<div id="data_editingFilePath"><?php if (isset($_GET['filepath']) && $_GET['filepath'] != "" && (file_exists($_GET['filepath']) || file_exists("../" . $_GET['filepath']))){ 
+echo $_GET['filepath']; 
+} ?></div>
+<div id="data_modulename"><?php echo dirname(str_replace("\\","/",__FILE__)); ?></div>
+<div id="data_umexists"><?php 
+    if (file_exists("../Upload Manager/")){
+        echo "true";
+    }else{
+        echo "false";
+    }
+    ?></div>
+</div>
+
+<script>
+    var currentFilepath = $("#data_editingFilePath").text().trim().replace("../",""); 
+    var menuItem = {
+        File: ["New File","Open File","Save","Save As","Download","Reload","Print","Export as HTML","Export as PDF","Export as Plain Text","Close"],
+        Edit:["Undo","Redo","Insert Special Characters","Toggle External Input Method","Upload Image","Insert Image","Save as HTML","Save as PDF"],
+        Help:["About WriterA","Markdown Guide","License"]
+    };    
+    var currentContextMenuItem = "";
+    var lastSaveContent = "";
+    var umExists = $("#data_umexists").text().trim() == "true";
+    var simplemde = new SimpleMDE({
+     element: document.getElementById("mde"),
+     spellChecker: false,
+     showIcons: ["code", "table","clean-block","horizontal-rule"],
+     hideIcons: ["guide"],
+     status: ["autosave", "lines", "words", "cursor"], 	 
+     });
+    var enableExternalInput = false;
+    var shiftHolding = false;
+    var controlHolding = false;
+    var saveAs = false; //If saveAs = false, after creating the new document, the current page will be updated to that new document. If set to true, new window will pop out for the new document (aka save As)
+    var lastCursorPosition;
+    
+    //Run when the document is ready
+    $(document).ready(function(){
+        //Initialization function
+        initWriterA();
+        loadAllSpecialCharacter();
+        $(".scs").hover(function(){
+        	var keyid = $(this).attr("keyid");
+        	$("#scid").html("HTML Keycode: #&" + keyid);
+        });
+        
+        $(".scs").on("mousedown",function(){
+        	insertTextAtCursor($(this).text());
+        	$("#specialCharInsert").hide();
+        });
+        
+        if (ao_module_getStorage("WriterA","enableExternalInput") == "true"){
+            toggleExternalInputMode();
+        }
+        
+        //Bind save check status 
+        setInterval(function(){
+            var icon = '<i class="home icon"></i>/';
+			if (currentFilepath.substring(0,6) == "/media"){
+			    //This filepath is in external storage
+			    icon = "";
+			}
+			if (currentFilepath == ""){
+			    updateStatus(icon + currentFilepath + " - NOT SAVED");
+			    return;
+			}
+            if (!checkIfContentSaved()){
+                updateStatus(icon + currentFilepath.replace("./","") + " - 💾 ❗ NOT SAVED");
+            }else{
+                updateStatus(icon + currentFilepath.replace("./","") + " - 💾 ✔️ Saved");
+            }
+        },1000);
+  
+    });
+    //Run before the page is rendered
+    if (ao_module_virtualDesktop){
+        $("#backBtn").hide();
+        $("body").css("padding-bottom","30px");
+        ao_module_setWindowSize(1050,550);
+    }else{
+        $("#extInputDisplay").hide();
+    }
+    
+    //Functions related to text insert and external input method
+    function insertTextAtCursor(text,ignoreNodename = false){
+        if (ignoreNodename){
+            pos = simplemde.codemirror.getCursor();
+            simplemde.codemirror.setSelection(pos, pos);
+            simplemde.codemirror.replaceSelection(text);
+            return;
+        }
+        if (document.activeElement.nodeName == "TEXTAREA"){
+            //The user is focused on the textarea.
+            pos = simplemde.codemirror.getCursor();
+            simplemde.codemirror.setSelection(pos, pos);
+            simplemde.codemirror.replaceSelection(text);
+        }else if (document.activeElement.nodeName == "INPUT"){
+            //The user is focused on an input instead.
+            $(document.activeElement).val($(document.activeElement).val() + text);
+        }else{
+            //Not focused anywhere. Inject into simpleMDE using the last active position
+             simplemde.codemirror.setSelection(lastCursorPosition,lastCursorPosition);
+             simplemde.codemirror.replaceSelection(text);
+        }
+        
+    }
+
+    simplemde.codemirror.on("cursorActivity",function(){
+        let pos = simplemde.codemirror.getCursor();
+        lastCursorPosition = pos;
+    });
+    
+    function loadAllSpecialCharacter(){
+    	$("#iscl").html("");
+    	for (var i =161; i < 1023; i++){
+    		if (i != 173){
+    			$("#iscl").append("<div class='scs' keyid='" + i +"'>" + String.fromCharCode(i) + "</div>");
+    		}
+    		
+    	}
+    }
+    
+    function initImageSelector(){
+        //After uploading the image, allow user to select the image they want to use
+        $("#insertImage").hide();
+        $("#previewArea").html("");
+        $.get("imageLoader.php",function(data){
+           var images = data;
+           for (var i =0; i < images.length; i++){
+               $("#previewArea").append('<img class="ts small rounded image selectable" src="' + images[i] + '" onClick="selectThisImage(this);">');
+           }
+        });
+        $("#selectImage").show();
+    }
+    
+    function selectThisImage(object){
+        if ($(object).hasClass("selected")){
+            $(object).removeClass("selected");
+        }else{
+            $(object).addClass("selected");
+        }
+    }
+    
+    function insertSelectedImages(){
+        $(".selected").each(function(){
+            var imagePath = $(this).attr("src");
+            insertTextAtCursor("![](" + imagePath + ")",true);
+        });
+        $("#selectImage").hide();
+    }
+    
+    function confirmCreateNewDocument(){
+        //This is a new document and now it is time to create this document and save it to file system
+        var mdeContent = JSON.stringify(simplemde.value());
+        var filename = $("#createFileName").val().trim();
+        var saveTarget = $("#newStorageDirectory").val().trim();
+        var error = false;
+        if (filename == ""){
+            $("#createFileName").parent().addClass("error");
+            error = true;
+        }
+        if (saveTarget == ""){
+            $("#newStorageDirectory").parent().addClass("error");
+            error = true;
+        }
+        if (error){
+            return;
+        }else{
+            //Remove the error class if exists
+            $("#createFileName").parent().removeClass("error");
+            $("#newStorageDirectory").parent().removeClass("error");
+        }
+        if (getExtension(filename) == filename){
+            //This file do not have an extension. Save as mark down instead.
+            filename = filename + ".md";
+        }
+        //Post the data and create the document
+         $.post( "documentIO.php", { content: mdeContent,create: saveTarget + filename})
+            .done(function( data ) {
+                console.log("[WriterA] Created File: " + data);
+                if (data.includes("ERROR") == false){
+                    //Create and save success.
+                    if (saveAs){
+                        //save as new document. Open a new document for that.
+                        var moduleName = $("#data_modulename").text().trim().split("/").pop();
+                	    var uid = ao_module_utils.getRandomUID();
+                	    var result = ao_module_newfw(moduleName + "/index.php?filepath=" + data.replace("../",""),"WriterA","file text outline", uid,1050,550,undefined,undefined,true,false);
+                	    if (result == false){
+                	        window.open("index.php?filepath=" + data);
+                	    }
+                    }else{
+                        //Create new document. Updat the current document to that of the new one
+                        updateStatus('💾 File Saved');
+                        lastSaveContent = simplemde.value();
+                        currentFilepath = data.replace("../",""); 
+                        setWindowTitle(ao_module_codec.decodeUmFilename(basename(currentFilepath)) + " - WriterA");
+                    }
+                    $("#saveNewDocument").hide();
+                }else{
+                    $("#createErrorMessage").html(data);
+                    $("#createError").slideDown().delay(5000).slideUp();
+                }
+            });
+    }
+    
+    function selectCreatePath(){
+        var fwID = ao_module_utils.getRandomUID();
+        if (ao_module_virtualDesktop){
+            ao_module_openFileSelector(fwID,"createDirSelected",undefined,undefined,false,"folder");
+        }else{
+            ao_module_openFileSelectorTab(fwID,"../",false,"folder",createDirSelected);
+        }
+    }
+    
+    $("#createFileName").on("keydown",function(e){
+        if (e.keyCode == 13){
+            //Enter pressed on the input filename, activate save as well
+            confirmCreateNewDocument();
+        }
+    });
+    
+    function createDirSelected(object){
+        result = JSON.parse(object);
+        for (var i=0; i < result.length; i++){
+    		var filename = result[i].filename;
+    		var filepath = result[i].filepath;
+    		$("#newStorageDirectory").val(filepath + "/");
+        }
+    }
+    
+    $(document).keydown(function(e){
+       switch(e.keyCode){
+            case 16:
+               //Shift
+               shiftHolding = true;
+               break;
+            case 17:
+                //Ctrl
+                controlHolding = true;
+                break;
+            case 83:
+                if (controlHolding){
+                    //Ctrl + S
+                    e.preventDefault();
+                    //Save the document here
+                    saveFile();
+                }else{
+                    ao_module_inputs.hookKeyHandler(e);
+                }
+            default:
+                if (enableExternalInput){
+                     ao_module_inputs.hookKeyHandler(e);
+                }
+                break;
+       }
+       if (shiftHolding && controlHolding){
+          toggleExternalInputMode();
+       }
+    }).keyup(function(e){
+        switch(e.keyCode){
+            case 16:
+               //Shift
+               shiftHolding = false;
+               break;
+            case 17:
+                //Ctrl
+                controlHolding = false;
+                break;
+        }
+            
+    });
+    
+    function updateStatus(text){
+        $("#status").html("📄 " + text);
+    }
+    
+    function toggleExternalInputMode(){
+        //Toggle Input Method
+        enableExternalInput = !enableExternalInput;
+        if (!enableExternalInput){
+           //External input mode disabled
+           $("#extInputDisplay").css("color","red");
+            ao_module_inputs.hookStdIn(function(text){});
+            ao_module_saveStorage("WriterA","enableExternalInput","false");
+        }else{
+           //External input mode enabled
+           $("#extInputDisplay").css("color","green");
+           ao_module_inputs.hookStdIn(function(text){insertTextAtCursor(text);});
+           ao_module_saveStorage("WriterA","enableExternalInput","true");
+        }
+    }
+	//Run when the editor is ready
+	function initWriterA(){
+		//Check if the current editing filepath is empty. If yes, this is a new document.
+		ao_module_setWindowIcon("file text outline");
+		if (currentFilepath == ""){
+		    setWindowTitle("Untitiled - WriterA");
+			updateStatus("Editor Ready!");
+		}else{
+			setWindowTitle(ao_module_codec.decodeUmFilename(basename(currentFilepath)) + " - WriterA");
+			loadDocumentFromPath(currentFilepath);
+			var icon = '🏠/';
+			if (currentFilepath.substring(0,6) == "/media"){
+			    //This filepath is in external storage
+			    icon = "";
+			}
+			updateStatus(icon + currentFilepath + " - Loaded");
+		}
+		
+	}
+	
+	function setWindowTitle(text){
+	    ao_module_setWindowTitle(text);
+		document.title = text;
+	}
+	
+	function loadDocumentFromPath(path){
+	    $.get("documentIO.php?filepath=" + path,function(data){
+	        data = JSON.parse(data);
+	        simplemde.value(data);
+	        lastSaveContent = data;
+	    });
+	}
+	
+	function checkIfContentSaved(){
+	    if (simplemde.value() != lastSaveContent){
+	        return false;
+	    }else{
+	        return true;
+	    }
+	}
+	
+	//New SaveAs Handler
+    function saveAsHandler(fileData){
+        result = JSON.parse(fileData);
+        for (var i=0; i < result.length; i++){
+            //var filename = result[i].filename;
+            var filepath = result[i].filepath;
+            var filename = result[i].filename;
+            var tmp = filepath.split("/")
+            tmp.pop();
+            filepath = tmp.join("/") + "/";
+            var mdeContent = JSON.stringify(simplemde.value());
+            var saveTarget = filepath;
+            var error = false;
+            if (getExtension(filename) == filename){
+                //This file do not have an extension. Save as mark down instead.
+                filename = filename + ".md";
+            }
+            //Post the data and create the document
+             $.post( "documentIO.php", { content: mdeContent,create: saveTarget + filename})
+                .done(function( data ) {
+                    console.log("[WriterA] Created File: " + data);
+                    if (data.includes("ERROR") == false){
+                        //Create and save success.
+                        if (saveAs){
+                            //save as new document. Open a new document for that.
+                            var moduleName = $("#data_modulename").text().trim().split("/").pop();
+                    	    var uid = ao_module_utils.getRandomUID();
+                    	    var result = ao_module_newfw(moduleName + "/index.php?filepath=" + data.replace("../",""),"WriterA","file text outline", uid,1050,550,undefined,undefined,true,false);
+                    	    if (result == false){
+                    	        window.open("index.php?filepath=" + data);
+                    	    }
+                        }else{
+                            //Create new document. Updat the current document to that of the new one
+                            updateStatus('💾 File Saved');
+                            lastSaveContent = simplemde.value();
+                            currentFilepath = data.replace("../",""); 
+                            setWindowTitle(ao_module_codec.decodeUmFilename(basename(currentFilepath)) + " - WriterA");
+                        }
+                        $("#saveNewDocument").hide();
+                    }else{
+                        $("#createErrorMessage").html(data);
+                        $("#createError").slideDown().delay(5000).slideUp();
+                    }
+                });
+        }
+    }
+	
+	//2020/01/15 ADDED
+	//for saving the datauri only
+	var datauri = "";
+	//END
+	
+	//Menu item handler
+	function menuClicked(object){
+	    var text = $(object).text().trim();
+	    switch(text){
+            case "New File":
+                newFile();
+                break;
+            case "Open File":
+                openFile();
+                break;
+            case "Save":
+                saveFile();
+                hideContextMenu();
+                break
+            case "Save As":
+                saveAs = true;
+                var uid = ao_module_utils.getRandomUID();
+                if (ao_module_virtualDesktop){
+                    ao_module_openFileSelector(uid,"saveAsHandler",undefined,undefined,false,"new","newdoc.md",true);
+                }else{
+                    ao_module_openFileSelectorTab(uid,"../",true,"new",saveAsHandler,"newdoc.md",true);
+                }
+                //$("#saveNewDocument").fadeIn("fast");
+                hideContextMenu();
+                break;
+            case "Toggle External Input Method":
+                toggleExternalInputMode();
+                hideContextMenu();
+                break;
+            case "About WriterA":
+                $("#information").show();
+                hideContextMenu();
+                break;
+            case "License":
+                $("#license").show();
+                hideContextMenu();
+                break;
+            case "Print":
+                printFile();
+                hideContextMenu();
+                break;
+            case "Close":
+                handleWindowClose();
+                //ao_module_close();
+                break;
+            case "Reload":
+                window.location.reload();
+                break;
+            case "Undo":
+                simplemde.undo();
+                hideContextMenu();
+                break;
+            case "Redo":
+                simplemde.redo();
+                hideContextMenu();
+                break;
+            case "Insert Special Characters":
+                $("#specialCharInsert").show();
+                hideContextMenu();
+                break;
+            case "Markdown Guide":
+                if (ao_module_virtualDesktop){
+                    ao_module_newfw("https://simplemde.com/markdown-guide",'Markdown Guide','sticky note outline',ao_module_utils.getRandomUID(),475,700);
+                }else{
+                    window.open("https://simplemde.com/markdown-guide");
+                }
+                hideContextMenu();
+                break;
+            case "Insert Image":
+                initImageSelector();
+                hideContextMenu();
+                break;
+            case "Upload Image":
+                 if (umExists){
+                    //Allow file upload and insert
+                    $("#insertImage").show();
+                }else{
+                    //Insert url image bracket to the text
+                    insertTextAtCursor("![](http://)");
+                }
+                hideContextMenu();
+                break;
+            case "Download":
+                var filename = "";
+                if (currentFilepath != ""){
+                    filename = ao_module_codec.decodeUmFilename(basename(currentFilepath));
+                }else{
+                    filename = prompt("Please enter a filename","Untitled.txt");
+                    if (filename == null) {
+                        filename = "Untitled.txt"
+                    }
+                      
+                }
+                download(filename,simplemde.value());
+                hideContextMenu();
+                break;
+            case "Export as PDF":
+                 $.post( "documentIO.php", { parseMD: JSON.stringify(simplemde.value())})
+                    .done(function( data ) {
+                        printPDF(data,true);
+                    });
+                hideContextMenu();
+                break;
+            case "Save as PDF":
+                //Work in progress
+				//v1.0 2020/1/15 14:46PM PST AlanYeung First version
+				$.post( "documentIO.php", { parseMD: JSON.stringify(simplemde.value())})
+                    .done(function( data ) {
+						printPDF(data,false);
+						if(ao_module_virtualDesktop){
+							ao_module_openFileSelector(Math.round(Math.random()*10000),"saveas",undefined,undefined,false,"new","Untitled.pdf",false);
+							//ao_module_openFileSelector(Math.round(Math.random()*10000), "../",allowMultiple = false, selectMode = "folder",callBack=saveasPDF);
+						}else{
+							ao_module_openFileSelectorTab(Math.round(Math.random()*10000),"saveas",undefined,undefined,false,"new","Untitled.pdf",false);
+							//ao_module_openFileSelectorTab(Math.round(Math.random()*10000), "../",allowMultiple = false, selectMode = "folder",callBack=saveasPDF);
+                        }
+                    });
+                hideContextMenu();
+                break;
+			case "Save as HTML":
+                //Work in progress
+				//v1.0 2020/1/15 14:46PM PST AlanYeung First version
+				$.post( "documentIO.php", { parseMD: JSON.stringify(simplemde.value())})
+                    .done(function( data ) {
+						datauri = data;
+						if(ao_module_virtualDesktop){
+							ao_module_openFileSelector(Math.round(Math.random()*10000),"saveas",undefined,undefined,false,"new","Untitled.html",false);
+							//ao_module_openFileSelector(Math.round(Math.random()*10000), "../",allowMultiple = false, selectMode = "folder",callBack=saveasHTML);
+						}else{
+							ao_module_openFileSelectorTab(Math.round(Math.random()*10000),"saveas",undefined,undefined,false,"new","Untitled.html",false);
+							//ao_module_openFileSelectorTab(Math.round(Math.random()*10000), "../",allowMultiple = false, selectMode = "folder",callBack=saveasHTML);
+                        }
+                        
+                    });
+                hideContextMenu();
+                break;
+			case "Export as HTML":
+				var filename = prompt("Please enter a filename","Untitled.html");
+                 $.post( "documentIO.php", { parseMD: JSON.stringify(simplemde.value())})
+                    .done(function( data ) {
+                        download(filename,data);
+                    });
+                hideContextMenu();
+                break;
+            case "Export as Plain Text":
+                var filename = "";
+                if (currentFilepath != ""){
+                    filename = ao_module_codec.decodeUmFilename(basename(currentFilepath));
+                }else{
+                    filename = prompt("Please enter a filename","Untitled.txt");
+                    if (filename == null) {
+                        filename = "Untitled.txt"
+                    }
+                      
+                }
+                 $.post( "documentIO.php", { parseMD: JSON.stringify(simplemde.value())})
+                 .done(function( data ) {
+                     download(filename,$(data).text());
+                 });
+                hideContextMenu();
+            default:
+                hideContextMenu();
+                break;
+	    }
+	}
+	
+	$(window).bind('beforeunload', function(){
+	     if (!checkIfContentSaved()){
+	         return 'Document is not saved yet. Confirm leaving?';
+	     }
+    });
+    
+    //Override function for ao_module_close();
+    function ao_module_close(){
+    	if (ao_module_virtualDesktop){
+    	    handleWindowClose();
+    		return true;
+    	}
+    	return false;
+    }
+ 
+	function handleWindowClose(){
+	    if (checkIfContentSaved()){
+	        //Content saved. Close window.
+	        if (ao_module_virtualDesktop){
+	            parent.closeWindow(ao_module_windowID);
+	        }
+	    }else{
+	        if (confirm("Document is not saved yet. Confirm leaving?")){
+	            parent.closeWindow(ao_module_windowID);
+	        }
+	    }
+	}
+	
+	function printPDF(htmlContent,saveAsFile = true) {
+        var pdf = new jsPDF('p', 'pt', 'letter');
+        // source can be HTML-formatted string, or a reference
+        // to an actual DOM element from which the text will be scraped.
+        source = htmlContent;
+        
+        // we support special element handlers. Register them with jQuery-style 
+        // ID selector for either ID or node name. ("#iAmID", "div", "span" etc.)
+        // There is no support for any other type of selectors 
+        // (class, of compound) at this time.
+        specialElementHandlers = {
+            // element with id of "bypass" - jQuery style selector
+            '#bypassme': function (element, renderer) {
+                // true = "handled elsewhere, bypass text extraction"
+                return true
+            }
+        };
+        margins = {
+            top: 80,
+            bottom: 60,
+            left: 40,
+            width: 522
+        };
+        // all coords and widths are in jsPDF instance's declared units
+        // 'inches' in this case
+        pdf.fromHTML(
+            source, // HTML string or DOM elem ref.
+            margins.left, // x coord
+            margins.top, { // y coord
+                'width': margins.width, // max width of content on PDF
+                'elementHandlers': specialElementHandlers
+            },
+
+            function (dispose) {
+                // dispose: object with X, Y of the last line add to the PDF 
+                //          this allow the insertion of new lines after html
+                var filename = "";
+                if (currentFilepath != ""){
+                    filename = ao_module_codec.decodeUmFilename(basename(currentFilepath));
+                }else{
+                    filename = "Untitled"
+                }
+				if(saveAsFile){
+					pdf.save(filename + '.pdf');
+				}else{
+					datauri = pdf.output("datauristring");
+				}
+            }, margins
+        );
+    }
+    
+	function saveas(path){
+		try{
+			console.log(path);
+			var filepath = JSON.parse(path)[0]["filepath"];
+			var formd = new FormData();
+			formd.append('fname', "../" + filepath);
+			formd.append('data', datauri);
+			datauri = "";
+			$.ajax({
+				type: 'POST',
+				url: 'save.php',
+				data: formd,
+				processData: false,
+				contentType: false
+			}).done(function(data) {
+				   alert(data);
+			});
+		}catch(err) {
+		  alert("Error! Action can't be performed. Error message:" + err.message);
+		}
+	}
+	
+	function download(filename, text) {
+      var element = document.createElement('a');
+      element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
+      element.setAttribute('download', filename);
+    
+      element.style.display = 'none';
+      document.body.appendChild(element);
+    
+      element.click();
+    
+      document.body.removeChild(element);
+    }
+
+	//Functions related to menus
+	function newFile(){
+	    var moduleName = $("#data_modulename").text().trim().split("/").pop();
+	    var uid = (new Date()).getTime();
+	    var result = ao_module_newfw(moduleName + "/index.php","WriterA","file text outline", uid,1050,550,undefined,undefined,true,false);
+	    if (result == false){
+	        window.open("index.php");
+	    }
+	    hideContextMenu();
+	}
+	
+	function saveFile(){
+	    if (currentFilepath.trim() != ""){
+	        //This is a file with path. save with savepath function call
+	        var mdeContent = JSON.stringify(simplemde.value());
+	         $.post( "documentIO.php", { content: mdeContent,savepath: currentFilepath.trim()})
+            .done(function( data ) {
+                console.log("[WriterA] Save File: " + data);
+                if (data.includes("ERROR") == false){
+                    updateStatus('💾 File Saved');
+                    lastSaveContent = simplemde.value();
+                }else{
+                    updateStatus('⚠️ Something went wrong when trying to save this file.');
+                }
+            });
+	    }else{
+	        //This is a new file. Let the user choose where to save this file first and use create function call to save
+	        var uid = ao_module_utils.getRandomUID();
+            if (ao_module_virtualDesktop){
+                ao_module_openFileSelector(uid,"saveAsHandler",undefined,undefined,false,"new","newdoc.md",true);
+            }else{
+                ao_module_openFileSelectorTab(uid,"../",true,"new",saveAsHandler,"newdoc.md",true);
+            }
+	    }
+	    
+	}
+	
+	function openFile(){
+	    var uid = ao_module_utils.getRandomUID();
+	    if (ao_module_virtualDesktop){
+	        ao_module_openFileSelector(uid,"openFileFromSelector");
+	    }else{
+	        ao_module_openFileSelectorTab(uid,"../",false,"file",openFileFromSelector);
+	    }
+	    hideContextMenu();
+	}
+	
+	function openFileFromSelector(fileData){
+        result = JSON.parse(fileData);
+        for (var i=0; i < result.length; i++){
+            var filename = result[i].filename;
+            var filepath = result[i].filepath;
+            //Opening file from Selector. If the current document path is undefined, then open in this windows. Otherwise, open in a new window.
+            if (currentFilepath == ""){
+                //Open in this window
+                window.location.href = window.location.href + "?filepath=" + filepath;
+            }else{
+                //Open in new window
+                var moduleName = $("#data_modulename").text().trim().split("/").pop();
+        	    var uid = ao_module_utils.getRandomUID();
+        	    var result = ao_module_newfw(moduleName + "/index.php?filepath=" + filepath,"WriterA","file text outline", uid,1050,550,undefined,undefined,true,false);
+        	    if (result == false){
+        	        window.open("index.php?filepath=" + filepath);
+        	    }
+            }
+            console.log(filename,filepath);
+        }
+    }
+	
+	function printFile(){
+	    try {
+			var fileContent = "";
+			var documentName = "Untitled Document";
+			if (currentFilepath.trim() != ""){
+			    var documentName = ao_module_codec.decodeUmFilename(basename(currentFilepath));
+			    var ext = getExtension(currentFilepath);
+			    if (ext == "md"){
+			        //Use another function for markdown
+			        parseMarkdownAndPrint(documentName);
+			        return;
+			    }else{
+			        //Just print the content of the simpleMDE
+			       fileContent =  simplemde.value()
+			    }
+			}else{
+			    //Force parse it into markdown
+			    parseMarkdownAndPrint(documentName);
+			    return;
+			}
+			var printWindow = window.open("", "", "height=400,width=800");
+			printWindow.document.write("<html><head><title>" + documentName + "</title>");
+			printWindow.document.write("</head><xmp>");
+			printWindow.document.write("Filename: '" + documentName + "' Print-time: " + new Date().toLocaleString() + "\n");
+			printWindow.document.write(fileContent);
+			printWindow.document.write("</xmp></html>");
+			printWindow.document.close();
+			printWindow.print();
+		}catch (ex) {
+			console.error("Error: " + ex.message);
+		}
+	}
+	
+	function parseMarkdownAndPrint(documentName){
+	    var content = simplemde.value();
+	    content = JSON.stringify(content);
+	    $.post( "documentIO.php", { parseMD: content})
+        .done(function( data ) {
+            printHTML(documentName,data);
+        });
+	}
+	
+	function printHTML(documentName,fileContent){
+	    	var printWindow = window.open("", "", "height=400,width=800");
+			printWindow.document.write("<html><head><title>" + documentName + "</title>");
+			printWindow.document.write("</head>");
+			printWindow.document.write("<small>📎Filename: " + documentName + " Print-time: " + new Date().toLocaleString() + "<br></small>");
+			printWindow.document.write(fileContent);
+			printWindow.document.write("</html>");
+			printWindow.document.close();
+			printWindow.print();
+	}
+	
+	
+	function basename(path){
+		return path.split("\\").join("/").split("/").pop();
+	}
+	
+	function getExtension(path){
+	    if (path.includes("/") || path.includes("\\")){
+	        path = basename(path);
+	    }
+	    return path.split(".").pop(); //Return the last section of the array which split with dot
+	}
+	
+	function showContextMenu(object){
+	    if ($("#contextMenu").is(":visible") && currentContextMenuItem == $(object).text().trim()){
+	        $("#contextMenu").hide();
+	        return;
+	    }
+	    currentContextMenuItem = $(object).text().trim();
+		var menu = $("#contextMenu");
+		var position = [$(object).offset().left,$(object).offset().top + $(object).height() + 2];
+		$("#contextMenu").css("left",position[0]);
+		$("#contextMenu").css("top",position[1]);
+		$("#contextMenu").html("");
+		var items = menuItem[$(object).text().trim()];
+		for (var i =0; i < items.length; i++){
+		   $("#contextMenu").append('<div class="menuItem" onClick="menuClicked(this);">' + items[i] + '</div>')
+		}
+		$("#contextMenu").show();
+	}
+	
+	function hideContextMenu(){
+	    $("#contextMenu").hide();
+	}
+	
+	//Click on the main edit area. 
+	$("#main").on("click",function(){
+	    //If context menu is shown, hide it
+	    if ($("#contextMenu").is(":visible")){
+	        $("#contextMenu").hide();
+	    }
+	    //focus this floatWindow
+	    ao_module_focus();
+	});
+
+</script>
+</body>
+</html>
+

Різницю між файлами не показано, бо вона завелика
+ 28 - 0
WriterA/jspdf.min.js


Різницю між файлами не показано, бо вона завелика
+ 6 - 0
WriterA/mde/simplemde.min.css


Різницю між файлами не показано, бо вона завелика
+ 6 - 0
WriterA/mde/simplemde.min.js


+ 19 - 0
WriterA/save.php

@@ -0,0 +1,19 @@
+<?php
+include '../auth.php';
+?>
+<?php
+$data = $_POST["data"];
+list($type, $data) = explode(';', $data);
+list($encode, $data) = explode(',', $data);
+if($filetype == "data:application/pdf"){
+	die("ERROR: UNSUPPORTED FILE FORMAT");
+}
+if($encode == "base64"){
+	$data = base64_decode($data);
+	file_put_contents($_POST["fname"], $data);
+}else{
+	file_put_contents($_POST["fname"], $_POST["data"]);
+}
+
+echo "Saved.";
+?>

BIN
WriterA/uploads/inith323031392d30322d32385f32332d31312d3239.png


BIN
WriterA/uploads/inith323031392d30342d30385f31342d32382d3038.png


BIN
WriterA/uploads/inith323031392d30342d30385f31342d33312d3130.png


BIN
WriterA/uploads/test.png


Деякі файли не було показано, через те що забагато файлів було змінено