소스 검색

Merge pull request #918 from 2du/master

버그 수정
잉여개발기 (SPDV) 6 년 전
부모
커밋
cd91e65948

+ 4 - 0
.gitignore

@@ -5,6 +5,8 @@ data/set.json
 data/mysql.json
 data/oauthsettings.json
 
+route/tool/set_mark/custom.py
+
 images
 .vscode
 
@@ -21,3 +23,5 @@ views/before_namu
 views/acme
 views/sl_open
 views/nitori
+
+.DS_Store

+ 7 - 3
app.py

@@ -4,7 +4,7 @@ import re
 for i_data in os.listdir("route"):
     f_src = re.search("(.+)\.py$", i_data)
     if f_src:
-        f_src = f_src.groups()[0]
+        f_src = f_src.group(1)
 
         exec("from route." + f_src + " import *")
 
@@ -56,7 +56,7 @@ while 1:
             for i_data in os.listdir("."):
                 f_src = re.search("(.+)\.db$", i_data)
                 if f_src:
-                    all_src += [f_src.groups()[0]]
+                    all_src += [f_src.group(1)]
 
             if all_src != [] and new_json[0] != 'mysql':
                 print('DB name (data) [' + ', '.join(all_src) + '] : ', end = '')
@@ -80,7 +80,7 @@ if set_data['db_type'] == 'mysql':
     try:
         set_data_mysql = json.loads(open('data/mysql.json', encoding='utf8').read())
     except:
-        new_json = ['', '']
+        new_json = ['', '', '']
 
         while 1:
             print('DB user ID : ', end = '')
@@ -375,6 +375,10 @@ def list_old_page():
 def list_acl():
     return list_acl_2(conn)
 
+@app.route('/image_file_list')
+def list_image_file():
+    return list_image_file_2(conn)
+
 @app.route('/admin_plus/<name>', methods=['POST', 'GET'])
 def give_admin_groups(name = None):
     return give_admin_groups_2(conn, name)

+ 2 - 2
emergency_tool.py

@@ -50,7 +50,7 @@ while 1:
             for i_data in os.listdir("."):
                 f_src = re.search("(.+)\.db$", i_data)
                 if f_src:
-                    all_src += [f_src.groups()[0]]
+                    all_src += [f_src.group(1)]
 
             if all_src != [] and new_json[0] != 'mysql':
                 print('DB name (data) [' + ', '.join(all_src) + '] : ', end = '')
@@ -74,7 +74,7 @@ if set_data['db_type'] == 'mysql':
     try:
         set_data_mysql = json.loads(open('data/mysql.json', encoding='utf8').read())
     except:
-        new_json = ['', '']
+        new_json = ['', '', '']
 
         while 1:
             print('DB user ID : ', end = '')

+ 3 - 1
language/en-US.json

@@ -125,7 +125,7 @@
         "recent_discussion" : "Recently discussion(s)",
         "recent_change" : "Recently edit(s)",
         "edit_filter" : "Contents filter",
-        "recent_ban" : "Recently Block(s)",
+        "recent_ban" : "Block(s) record",
         "load" : "Load another document",
         "edit_filter_rule" : "Contents filter rule",
         "move_history" : "History of moveing",
@@ -285,6 +285,7 @@
             "admin_group_list" : "Administrator group(s) list",
             "all_document_list" : "All document(s) list",
             "watchlist" : "Watchlist",
+            "image_file_list" : "Image file(s) list",
             "_comment_2.3.1_" : "ACL document list",
                 "acl_document_list" : "ACL document(s) list",
                 "acl_required" : "Required ACL",
@@ -323,6 +324,7 @@
                 "user_document_acl" : "User document ACL",
                 "upload_acl" : "Upload ACL",
                 "edit_req_acl" : "Edit request ACL",
+                "many_upload_acl" : "Upload multiple files ACL",
         "_comment_2.7_" : "Application list",
             "application_list": "Application list",
             "application_time": "Application time",

+ 4 - 2
language/ko-KR.json

@@ -266,7 +266,7 @@
     "destruction": "취소",
     "count": "기여 횟수",
     "main_head": "메인 <HEAD>",
-    "recent_ban": "최근 차단",
+    "recent_ban": "차단 기록",
     "smtp_password": "SMTP Password",
     "id_filter_list": "ID 필터 목록",
     "other": "기타",
@@ -357,5 +357,7 @@
     "topic_name_change": "토론 제목 변경",
     "topic_acl_setting" : "토론 ACL 설정",
     "topic_acl" : "토론 ACL",
-    "main_skin_set" : "기본 스킨 설정"
+    "main_skin_set" : "기본 스킨 설정",
+    "image_file_list" : "이미지 파일 목록",
+    "many_upload_acl" : "다중 파일 업로드 ACL"
 }

+ 1 - 1
route/api_user_info.py

@@ -49,7 +49,7 @@ def api_user_info_2(conn, name):
             plus_t += [load_lang('blocked') + '<br>']
 
             match = re.search("^([0-9]{1,3}\.[0-9]{1,3})", name)
-            match = match.groups()[0] if match else '-'
+            match = match.group(1) if match else '-'
             regex_ban = 0
 
             curs.execute(db_change("select login, block, end, why from ban where band = 'regex'"))

+ 65 - 40
route/edit.py

@@ -5,6 +5,8 @@ def edit_2(conn, name):
 
     ip = ip_check()
     section = flask.request.args.get('section', None)
+    if section:
+        section = int(number_check(section))
 
     curs.execute(db_change("select data from data where title = ?"), [name])
     old = curs.fetchall()
@@ -22,9 +24,10 @@ def edit_2(conn, name):
             return re_error('/error/24')
 
         today = get_time()
-        content = flask.request.form.get('content', '')
+        content = flask.request.form.get('content', '').replace('\r\n', '\n')
+        o_content = flask.request.form.get('o_content', '').replace('\r\n', '\n')
 
-        if flask.request.form.get('otent', '') == content:
+        if o_content == content:
             return redirect('/w/' + url_pas(name))
         
         if edit_filter_do(content) == 1:
@@ -37,14 +40,34 @@ def edit_2(conn, name):
 
         content = savemark(content)
         
-        if old:
-            leng = leng_check(len(flask.request.form.get('otent', '')), len(content))
-            
+        if old:  
+            o_data = old[0][0].replace('\r\n', '\n')
+
             if section:
-                content = old[0][0].replace('\r\n', '\n').replace(
-                    flask.request.form.get('otent', '').replace('\r\n', '\n'), 
-                    content.replace('\r\n', '\n')
-                )
+                class run_count_section:
+                    def __init__(self, key, change):
+                        self.counter = key
+                        self.change = change
+
+                    def __call__(self, match):
+                        self.counter -= 1
+
+                        if self.counter == 0:
+                            return '\n' + self.change
+                        else:
+                            return '\n' + match[1]
+
+                run_count = run_count_section(section, content)
+
+                c_data = html.escape('\n' + o_data)
+                c_data = re.sub('\n(?P<in>={1,6})', '<br>\g<in>', c_data)
+                c_data = re.sub('<br>((?:(?:(?!<br>).)*\n*)*)', run_count, c_data)
+                c_data = re.sub('^\n', '', c_data)
+                c_data = html.unescape(c_data)
+
+                content = c_data
+
+            leng = leng_check(len(o_data), len(content))
         else:
             leng = '+' + str(len(content))
 
@@ -85,42 +108,36 @@ def edit_2(conn, name):
         conn.commit()
         
         return redirect('/w/' + url_pas(name))
-    else:            
+    else:
         if old:
             if section:
-                data = re.sub(
-                    '\n(?P<in>={1,6})', 
-                    '<br>\g<in>', 
-                    html.escape('\n' + old[0][0].replace('\r\n', '\n') + '\n')
-                )
-                i = 0
-
-                while 1:
-                    g_data = re.search('((?:<br>)(?:(?:(?!\n|<br>).)+)(?:\n*(?:(?:(?!<br>).)+\n*)+)?)', data)
-                    if g_data:
-                        if int(section) - 1 == i:
-                            data = html.unescape(re.sub('<br>(?P<in>={1,6})', '\n\g<in>', g_data.groups()[0]))
-                            
-                            break
-                        else:
-                            data = re.sub('((?:<br>)(?:(?:(?!\n|<br>).)+)(?:\n*(?:(?:(?!<br>).)+\n*)+)?)', '\n', data, 1)
+                data = html.escape('\n' + old[0][0].replace('\r\n', '\n'))
+                
+                data = re.sub('\n(?P<in>={1,6})', '<br>\g<in>', data)
 
-                        i += 1
-                    else:
-                        break
+                section_data = re.findall('<br>((?:(?:(?!<br>).)*\n*)*)', data)
+                if len(section_data) >= section:
+                    data = section_data[section - 1]
+                else:
+                    return redirect('/edit/' + url_pas(name))
             else:
                 data = old[0][0].replace('\r\n', '\n')
         else:
             data = ''
-            
+
         data_old = data
-        get_name = ''
 
-        if not section:
-            get_name = '''
-                <a href="/manager/15?plus=''' + url_pas(name) + '">(' + load_lang('load') + ')</a> <a href="/edit_filter">(' + load_lang('edit_filter_rule') + ''')</a>
-                <hr class=\"main_hr\">
-            '''
+        if section:
+            get_name = '' + \
+                '<a href="/edit_filter">(' + load_lang('edit_filter_rule') + ')</a>' + \
+                '<hr class=\"main_hr\">' + \
+            ''
+        else:
+            get_name = '' + \
+                '<a href="/manager/15?plus=' + url_pas(name) + '">(' + load_lang('load') + ')</a> ' + \
+                '<a href="/edit_filter">(' + load_lang('edit_filter_rule') + ')</a>' + \
+                '<hr class=\"main_hr\">' + \
+            ''
             
         if flask.request.args.get('plus', None):
             curs.execute(db_change("select data from data where title = ?"), [flask.request.args.get('plus', 'test')])
@@ -143,11 +160,16 @@ def edit_2(conn, name):
         else:
             b_text = ''
         
-        cccb_text = ''
         curs.execute(db_change('select data from other where name = "copyright_checkbox_text"'))
         sql_d = curs.fetchall()
         if sql_d and sql_d[0][0] != '':
-            cccb_text = '<hr class=\"wmain_hr\"><input type="checkbox" name="copyright_agreement" value="yes">' + sql_d[0][0] + '<hr class=\"main_hr\">'
+            cccb_text = '' + \
+                '<hr class=\"main_hr\">' + \
+                '<input type="checkbox" name="copyright_agreement" value="yes"> ' + sql_d[0][0] + \
+                '<hr class=\"main_hr\">' + \
+            ''
+        else:
+            cccb_text = ''
 
         curs.execute(db_change('select data from other where name = "edit_help"'))
         sql_d = curs.fetchall()
@@ -156,14 +178,17 @@ def edit_2(conn, name):
         else:
             p_text = load_lang('defalut_edit_help')
 
+        data = re.sub('\n$', '', data)
+        data_old = re.sub('\n$', '', data_old)
+
         return easy_minify(flask.render_template(skin_check(), 
             imp = [name, wiki_set(), custom(), other2([' (' + sub + ')', 0])],
             data =  get_name + '''
                 <form method="post">
                     <script>do_stop_exit();</script>
                     ''' + edit_button() + '''
-                    <textarea rows="25" id="content" placeholder="''' + p_text + '''" name="content">''' + html.escape(re.sub('\n$', '', data)) + '''</textarea>
-                    <textarea id="origin" name="otent">''' + html.escape(re.sub('\n$', '', data_old)) + '''</textarea>
+                    <textarea id="content" placeholder="''' + p_text + '''" name="content">''' + html.escape(data) + '''</textarea>
+                    <textarea id="origin" name="o_content">''' + html.escape(data_old) + '''</textarea>
                     <hr class=\"main_hr\">
                     <input placeholder="''' + load_lang('why') + '''" name="send" type="text">
                     <hr class=\"main_hr\">

+ 3 - 0
route/func_upload.py

@@ -19,6 +19,9 @@ def func_upload_2(conn):
         if len(file_data) == 1:
             file_num = None
         else:
+            if acl_check(None, 'many_upload') == 1:
+                return re_error('/ban')
+
             file_num = 1
 
         for data in file_data:

+ 1 - 1
route/give_acl.py

@@ -16,7 +16,7 @@ def give_acl_2(conn, name):
         if check_data and ip_or_user(ip) != 0:
             return redirect('/login')
 
-        if user_data.groups()[0] != ip_check():
+        if user_data.group(1) != ip_check():
             if admin_check(5) != 1:
                 if check_data:
                     return re_error('/error/3')

+ 1 - 1
route/list_admin.py

@@ -10,7 +10,7 @@ def list_admin_2(conn):
         name = ip_pas(data[0]) + ' <a href="/admin_plus/' + url_pas(data[1]) + '">(' + data[1] + ')</a>'
 
         if data[2] != '':
-            name += '(' + data[2] + ')'
+            name += ' (' + data[2] + ')'
 
         div += '<li>' + name + '</li>'
 

+ 26 - 0
route/list_image_file.py

@@ -0,0 +1,26 @@
+from .tool.func import *
+
+def list_image_file_2(conn):
+    curs = conn.cursor()
+
+    num = int(number_check(flask.request.args.get('num', '1')))
+    if num * 50 > 0:
+        sql_num = num * 50 - 50
+    else:
+        sql_num = 0
+
+    list_data = '<ul>'
+    back = ''
+
+    curs.execute(db_change("select title from data where title like 'file:%' limit ?, 50"), [sql_num])
+    data_list = curs.fetchall()
+    for data in data_list:
+        list_data += '<li><a href="/w/' + url_pas(data[0]) + '">' + data[0] + '</a></li>'
+
+    list_data += next_fix('/image_file_list?num=', num, data_list)
+
+    return easy_minify(flask.render_template(skin_check(),
+        imp = [load_lang('image_file_list'), wiki_set(), custom(), other2([0, 0])],
+        data = list_data,
+        menu = [['other', load_lang('return')]]
+    ))    

+ 1 - 1
route/login_need_email.py

@@ -41,7 +41,7 @@ def login_need_email_2(conn, tool):
             if 'c_id' in flask.session:
                 data = re.search('@([^@]+)$', flask.request.form.get('email', ''))
                 if data:
-                    data = data.groups()[0]
+                    data = data.group(1)
 
                     curs.execute(db_change("select html from html_filter where html = ? and kind = 'email'"), [data])
                     if curs.fetchall():

+ 1 - 1
route/main_image_view.py

@@ -6,7 +6,7 @@ def main_image_view_2(conn, name, app_var):
     if os.path.exists(os.path.join(app_var['path_data_image'], name)):
         return flask.send_from_directory(
             './' + app_var['path_data_image'], name, 
-            mimetype = 'image/' + re.search('\.([^\.]+)$', name).groups()[0]
+            mimetype = 'image/' + re.search('\.([^\.]+)$', name).group(1)
         )
     else:
         return redirect()

+ 0 - 1
route/main_manager.py

@@ -37,7 +37,6 @@ def main_manager_2(conn, num, r_ver):
                         <li><a href="/give_log">''' + load_lang('admin_group_list') + '''</a></li>
                         <li><a href="/many_delete">''' + load_lang('many_delete') + '''</a></li>
                         <li><a href="/applications">''' + load_lang('application_list') + '''</a></li>
-                        <li><a href="/adsense_setting">''' + load_lang('adsense_setting') + '''</a></li>
                         <li><a href="/setting">''' + load_lang('setting') + '''</a></li>
                     </ul>
                     <h3>''' + load_lang('filter') + '''</h3>

+ 1 - 0
route/main_other.py

@@ -23,6 +23,7 @@ def main_other_2(conn):
                 <li><a href="/user_log">''' + load_lang('member_list') + '''</a></li>
                 <li><a href="/admin_log">''' + load_lang('authority_use_list') + '''</a></li>
                 <li><a href="/old_page">''' + load_lang('old_page') + '''</a></li>
+                <li><a href="/image_file_list">''' + load_lang('image_file_list') + '''</a></li>
             </ul>
             <br>
             <h2>''' + load_lang('other') + '''</h2>

+ 1 - 1
route/main_views.py

@@ -16,7 +16,7 @@ def main_views_2(conn, name):
         plus = ''
         rename = name
 
-    mime_type = re.search('\.([^\.]+)$', rename).groups()[0]
+    mime_type = re.search('\.([^\.]+)$', rename).group(1)
     if mime_type:
         if mime_type in ['.jpeg', '.jpg', '.gif', '.png', '.webp', '.JPEG', '.JPG', '.GIF', '.PNG', '.WEBP']:
             mime_type = 'image/' + mime_type

+ 16 - 13
route/setting.py

@@ -26,6 +26,8 @@ def setting_2(conn, num):
             x += 1
             li_data += '<li><a href="/setting/' + str(x) + '">' + li + '</a></li>'
 
+        li_data += '<li><a href="/adsense_setting">'+ load_lang('adsense_setting') + '</a></li>'
+
         return easy_minify(flask.render_template(skin_check(),
             imp = [load_lang('setting'), wiki_set(), custom(), other2([0, 0])],
             data = '<h2>' + load_lang('list') + '</h2><ul>' + li_data + '</ul>',
@@ -558,20 +560,14 @@ def setting_2(conn, num):
             2 : 'discussion',
             3 : 'upload_acl',
             4 : 'all_view_acl',
-            5 : 'edit_req_acl'
-        }
-        n_list = {
-            1 : 'normal',
-            2 : 'normal',
-            3 : 'normal',
-            4 : 'normal',
-            5 : 'normal'
+            5 : 'edit_req_acl',
+            6 : 'many_upload_acl'
         }
 
         if flask.request.method == 'POST':
             for i in i_list:
                 curs.execute(db_change("update other set data = ? where name = ?"), [
-                    flask.request.form.get(i_list[i], n_list[i]),
+                    flask.request.form.get(i_list[i], 'normal'),
                     i_list[i]
                 ])
 
@@ -589,15 +585,18 @@ def setting_2(conn, num):
                 if sql_d:
                     d_list[i] = sql_d[0][0]
                 else:
-                    curs.execute(db_change('insert into other (name, data) values (?, ?)'), [i_list[i], n_list[i]])
+                    curs.execute(db_change('insert into other (name, data) values (?, ?)'), [i_list[i], 'normal'])
 
-                    d_list[i] = n_list[i]
+                    d_list[i] = 'normal'
 
             conn.commit()
 
-            acl_div = ['', '', '', '', '']
+            acl_div = []
+            for i in range(0, len(i_list)):
+                acl_div += ['']
+
             acl_list = get_acl_list()
-            for i in range(0, 5):
+            for i in range(0, len(i_list)):
                 for data_list in acl_list:
                     if data_list == d_list[i + 1]:
                         check = 'selected="selected"'
@@ -631,7 +630,11 @@ def setting_2(conn, num):
                         <span>''' + load_lang('edit_req_acl') + '''</span>
                         <hr class=\"main_hr\">
                         <select name="edit_req_acl">''' + acl_div[4] + '''</select>
+                        <hr>
+                        <span>''' + load_lang('many_upload_acl') + '''</span>
                         <hr class=\"main_hr\">
+                        <select name="many_upload_acl">''' + acl_div[5] + '''</select>
+                        <hr>
                         <button id="save" type="submit">''' + load_lang('save') + '''</button>
                     </form>
                 ''',

+ 2 - 2
route/setting_adsense.py

@@ -14,7 +14,7 @@ def setting_adsense_2(conn):
             return easy_minify(flask.render_template(skin_check(),
                 imp = [load_lang('inter_error'), wiki_set(), custom(), other2([0, 0])],
                 data = '<h2>ie_no_data_required</h2>' + load_lang('ie_no_data_required'),
-                menu = [['other', load_lang('return')]]
+                menu = [['setting', load_lang('return')]]
             ))
 
         if adsense_enabled == 'on':
@@ -62,5 +62,5 @@ def setting_adsense_2(conn):
     return easy_minify(flask.render_template(skin_check(),
         imp = [load_lang('adsense_setting'), wiki_set(), custom(), other2([0, 0])],
         data = body_content,
-        menu = [['other', load_lang('return')]]
+        menu = [['setting', load_lang('return')]]
     ))

+ 8 - 4
route/tool/func.py

@@ -193,7 +193,7 @@ def update(ver_num, set_data):
         if change_rec and change_rec[0][0] != '':
             new_rec = re.search('data-sitekey="([^"]+)"', change_rec[0][0])
             if new_rec:
-                curs.execute(db_change("update other set data = ? where name = 'recaptcha'"), [new_rec.groups()[0]])
+                curs.execute(db_change("update other set data = ? where name = 'recaptcha'"), [new_rec.group(1)])
             else:
                 curs.execute(db_change("update other set data = '' where name = 'recaptcha'"))
                 curs.execute(db_change("update other set data = '' where name = 'sec_re'"))
@@ -499,7 +499,7 @@ def other2(data):
         data += ['']
 
     req_list = ''
-    main_css_ver = 25
+    main_css_ver = 29
 
     if not 'main_css_load' in flask.session or not 'main_css_ver' in flask.session or flask.session['main_css_ver'] != main_css_ver:
         for i_data in os.listdir(os.path.join("views", "main_css", "css")):
@@ -908,6 +908,10 @@ def acl_check(name = 'test', tool = '', topic_num = '1'):
         elif tool == 'upload':
             curs.execute(db_change("select data from other where name = 'upload_acl'"))
 
+            num = 5
+        elif tool == 'many_upload':
+            curs.execute(db_change("select data from other where name = 'many_upload_acl'"))
+
             num = 5
         elif tool == 'edit_req':
             curs.execute(db_change("select data from other where name = 'edit_req_acl'"))
@@ -986,7 +990,7 @@ def ban_check(ip = None, tool = None):
 
     band = re.search("^([0-9]{1,3}\.[0-9]{1,3})", ip)
     if band:
-        band_it = band.groups()[0]
+        band_it = band.group(1)
     else:
         band_it = '-'
 
@@ -1253,7 +1257,7 @@ def re_error(data):
                             '<li>' + data + '</li>' + \
                         '</ul>' + \
                     '</div>' + \
-                    ('<script>window.onload = function () { main_css_skin_set(); }</script>' if get_url == '/main_skin_set' else ''),
+                    ('<script>window.addEventListener(\'DOMContentLoaded\', function() { main_css_skin_set(); });</script>' if get_url == '/main_skin_set' else ''),
                 menu = ([['main_skin_set', load_lang('main_skin_set')]] if get_url != '/main_skin_set' else [['skin_set', load_lang('skin_set')]])
             ))
         else:

+ 2 - 5
route/tool/init.py

@@ -22,14 +22,14 @@ server_set_var = {
     'language' : {
         'display' : 'Language',
         'require' : 'select',
-        'default' : 'en-US',
+        'default' : 'ko-KR',
         'list' : ['ko-KR', 'en-US']
     },
     'markup' : {
         'display' : 'Markup',
         'require' : 'select',
         'default' : 'namumark',
-        'list' : ['namumark', 'raw']
+        'list' : ['namumark', 'custom', 'raw']
     },
     'encode' : {
         'display' : 'Encryption method',
@@ -43,9 +43,6 @@ def init(key):
     if env_dict[key] != None:
         return env_dict[key]
     else:
-        if key == 'markup':
-            return server_set_var[key]['default']
-
         while 1:
             if server_set_var[key]['require'] == 'select':
                 list_ = '[' + ', '.join(server_set_var[key]['list']) + ']'

+ 10 - 1
route/tool/mark.py

@@ -3,6 +3,7 @@ from .set_mark.namumark import namumark, link_fix
 from .set_mark.tool import *
 
 import re
+import os
 import html
 import sqlite3
 import asyncio
@@ -10,6 +11,12 @@ import threading
 import urllib.parse
 import multiprocessing
 
+if os.path.exists('route/tool/set_mark/custom.py'):
+    from .set_mark.custom import custom_mark
+else:
+    def custom_mark(conn, data, title, num, include):
+        return [data, '', []]
+
 def load_conn2(data):
     global conn
     global curs
@@ -41,7 +48,9 @@ def render_do(title, data, num, include):
     curs.execute(db_change('select data from other where name = "markup"'))
     rep_data = curs.fetchall()
     if rep_data[0][0] == 'namumark':
-        data = namumark(conn, data, title, num, include)
+        data = namumark(conn, data, title, include)
+    elif rep_data[0][0] == 'custom':
+        data = custom_mark(conn, data, title, include)
     elif rep_data[0][0] == 'raw':
         data = [data, '', []]
     else:

+ 31 - 31
route/tool/set_mark/namumark.py

@@ -29,7 +29,7 @@ def link_fix(main_link):
 
     other_link = re.search('[^\\\\]?(#[^#]+)$', main_link)
     if other_link:
-        other_link = other_link.groups()[0]
+        other_link = other_link.group(1)
 
         main_link = re.sub('(#[^#]+)$', '', main_link)
     else:
@@ -173,11 +173,11 @@ def table_start(data):
 
         table = re.search('\n((?:(?:(?:(?:\|\||\|[^|]+\|)+(?:(?:(?!\|\|).\n*)*))+)\|\|(?:\n)?)+)', data)
         if table:
-            table = re.sub('(\|\|)+\n', '||\n', table.groups()[0])
+            table = re.sub('(\|\|)+\n', '||\n', table.group(1))
             
             table_caption = re.search('^\|([^|]+)\|', table)
             if table_caption:
-                table_caption = '<caption>' + table_caption.groups()[0] + '</caption>'
+                table_caption = '<caption>' + table_caption.group(1) + '</caption>'
                 
                 table = re.sub('^\|([^|]+)\|', '||', table)
             else:
@@ -188,7 +188,7 @@ def table_start(data):
             table_cel = re.findall('(\n(?:(?:\|\|)+)|\|\|\n(?:(?:\|\|)+)|(?:(?:\|\|)+))((?:(?:(?!\n|\|\|).)+\n*)+)', table)
             for i in table_cel:
                 cel_plus = re.search('^((?:&lt;(?:(?:(?!&gt;).)*)&gt;)+)', i[1])
-                cel_plus = cel_plus.groups()[0] if cel_plus else ''
+                cel_plus = cel_plus.group(1) if cel_plus else ''
                 cel_data = re.sub('^((?:&lt;(?:(?:(?!&gt;).)*)&gt;)+)', '', i[1])
 
                 if re.search('^\n', i[0]):
@@ -285,22 +285,22 @@ def middle_parser(data, include_num):
                         middle_search = re.search('^(#(?:[0-9a-f-A-F]{3}){1,2})', middle_data[0])
                         middle_list += ['span']
 
-                        data = middle_re.sub('<span style="color: ' + middle_search.groups()[0] + ';">', data, 1)
+                        data = middle_re.sub('<span style="color: ' + middle_search.group(1) + ';">', data, 1)
                     elif re.search('^(?:#(\w+))', middle_data[0]):
                         middle_search = re.search('^(?:#(\w+))', middle_data[0])
                         middle_list += ['span']
 
-                        data = middle_re.sub('<span style="color: ' + middle_search.groups()[0] + ';">', data, 1)
+                        data = middle_re.sub('<span style="color: ' + middle_search.group(1) + ';">', data, 1)
                     elif re.search('^(?:@((?:[0-9a-f-A-F]{3}){1,2}))', middle_data[0]):
                         middle_search = re.search('^(?:@((?:[0-9a-f-A-F]{3}){1,2}))', middle_data[0])
                         middle_list += ['span']
 
-                        data = middle_re.sub('<span style="background: #' + middle_search.groups()[0] + ';">', data, 1)
+                        data = middle_re.sub('<span style="background: #' + middle_search.group(1) + ';">', data, 1)
                     elif re.search('^(?:@(\w+))', middle_data[0]):
                         middle_search = re.search('^(?:@(\w+))', middle_data[0])
                         middle_list += ['span']
 
-                        data = middle_re.sub('<span style="background: ' + middle_search.groups()[0] + ';">', data, 1)
+                        data = middle_re.sub('<span style="background: ' + middle_search.group(1) + ';">', data, 1)
                     elif re.search('^(\+|-)([1-5])', middle_data[0]):
                         middle_search = re.search('^(\+|-)([1-5])', middle_data[0])
                         middle_search = middle_search.groups()
@@ -478,7 +478,7 @@ def middle_parser(data, include_num):
 
     return data
 
-def namumark(conn, data, title, main_num, include_num):
+def namumark(conn, data, title, include_num):
     curs = conn.cursor()
 
     global plus_data
@@ -502,7 +502,7 @@ def namumark(conn, data, title, main_num, include_num):
     while 1:
         math = math_re.search(data)
         if math:
-            math = math.groups()[0]
+            math = math.group(1)
             math = math.replace('{', '<math_mid_1>')
             math = math.replace('}', '<math_mid_2>')
             math = math.replace('\\', '<math_slash>')
@@ -520,7 +520,7 @@ def namumark(conn, data, title, main_num, include_num):
     while 1:
         math = math_re.search(data)
         if math:
-            math = math.groups()[0]
+            math = math.group(1)
             math = math.replace('<math_mid_1>', '{')
             math = math.replace('<math_mid_2>', '}')
             math = math.replace('<math_slash>', '\\')
@@ -562,11 +562,11 @@ def namumark(conn, data, title, main_num, include_num):
 
         include = include_re.search(data)
         if include:
-            include = include.groups()[0]
+            include = include.group(1)
 
             include_data = re.search('^((?:(?!,).)+)', include)
             if include_data:
-                include_data = include_data.groups()[0]
+                include_data = include_data.group(1)
             else:
                 include_data = 'Test'
 
@@ -635,7 +635,7 @@ def namumark(conn, data, title, main_num, include_num):
     redirect_re = re.compile('\n#(?:redirect|넘겨주기) ((?:(?!\n).)+)\n', re.I)
     redirect = redirect_re.search(data)
     if redirect:
-        redirect = redirect.groups()[0]
+        redirect = redirect.group(1)
 
         return_link = link_fix(redirect)
         main_link = html.unescape(return_link[0])
@@ -751,7 +751,7 @@ def namumark(conn, data, title, main_num, include_num):
     
     now_time = tool.get_time()
     time_data = re.search('^([0-9]{4}-[0-9]{2}-[0-9]{2})', now_time)
-    time = time_data.groups()[0]
+    time = time_data.group(1)
     
     macro_re = re.compile('\[([^[(]+)\(((?:(?!\)]).)+)\)\]')
     macro_data = macro_re.findall(data)
@@ -760,7 +760,7 @@ def namumark(conn, data, title, main_num, include_num):
         if macro_name == 'youtube' or macro_name == 'kakaotv' or macro_name == 'nicovideo':
             width = re.search(', ?width=((?:(?!,).)+)', i[1])
             if width:
-                video_width = width.groups()[0]
+                video_width = width.group(1)
                 if re.search('^[0-9]+$', video_width):
                     video_width += 'px'
             else:
@@ -768,7 +768,7 @@ def namumark(conn, data, title, main_num, include_num):
 
             height = re.search(', ?height=((?:(?!,).)+)', i[1])
             if height:
-                video_height = height.groups()[0]
+                video_height = height.group(1)
                 if re.search('^[0-9]+$', video_height):
                     video_height += 'px'
             else:
@@ -776,7 +776,7 @@ def namumark(conn, data, title, main_num, include_num):
 
             code = re.search('^((?:(?!,).)+)', i[1])
             if code:
-                video_code = code.groups()[0]
+                video_code = code.group(1)
             else:
                 video_code = ''
 
@@ -785,7 +785,7 @@ def namumark(conn, data, title, main_num, include_num):
             if macro_name == 'youtube':
                 start = re.search(', ?(start=(?:(?!,).)+)', i[1])
                 if start:
-                    video_start = '?' + start.groups()[0]
+                    video_start = '?' + start.group(1)
 
                 video_code = re.sub('^https:\/\/www\.youtube\.com\/watch\?v=', '', video_code)
                 video_code = re.sub('^https:\/\/youtu\.be\/', '', video_code)
@@ -809,19 +809,19 @@ def namumark(conn, data, title, main_num, include_num):
         elif macro_name == 'ruby':
             ruby_code = re.search('^([^,]+)', i[1])
             if ruby_code:
-                ruby_code = ruby_code.groups()[0]
+                ruby_code = ruby_code.group(1)
             else:
                 ruby_code = 'Test'
 
             ruby_top = re.search('ruby=([^,]+)', i[1], flags = re.I)
             if ruby_top:
-                ruby_top = ruby_top.groups()[0]
+                ruby_top = ruby_top.group(1)
             else:
                 ruby_top = 'Test'
 
             ruby_color = re.search('color=([^,]+)', i[1], flags = re.I)
             if ruby_color:
-                ruby_color = 'color: ' + ruby_color.groups()[0] + ';'
+                ruby_color = 'color: ' + ruby_color.group(1) + ';'
             else:
                 ruby_color = ''
 
@@ -869,7 +869,7 @@ def namumark(conn, data, title, main_num, include_num):
     while 1:
         block = re.search('(\n(?:&gt; ?(?:(?:(?!\n).)+)?\n)+)', data)
         if block:
-            block = block.groups()[0]
+            block = block.group(1)
 
             block = re.sub('^\n&gt; ?', '', block)
             block = re.sub('\n&gt; ?', '\n', block)
@@ -892,7 +892,7 @@ def namumark(conn, data, title, main_num, include_num):
     while 1:
         li = re.search('(\n(?:(?: *)\* ?(?:(?:(?!\n).)+)\n)+)', data)
         if li:
-            li = li.groups()[0]
+            li = li.group(1)
             while 1:
                 sub_li = re.search('\n(?:( *)\* ?((?:(?!\n).)+))', li)
                 if sub_li:
@@ -917,7 +917,7 @@ def namumark(conn, data, title, main_num, include_num):
     while 1:
         indent = re.search('\n( +)', data)
         if indent:
-            indent = len(indent.groups()[0])
+            indent = len(indent.group(1))
 
             margin = '<span style="margin-left: 20px;"></span>' * indent
 
@@ -932,7 +932,7 @@ def namumark(conn, data, title, main_num, include_num):
     while 1:
         link = re.search('\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]', data)
         if link:
-            link = link.groups()[0]
+            link = link.group(1)
 
             link_split = re.search('((?:(?!\|).)+)(?:\|((?:(?!\|).)+))', link)
             if link_split:
@@ -949,7 +949,7 @@ def namumark(conn, data, title, main_num, include_num):
 
                 file_width = re.search('width=((?:(?!&).)+)', see_link)
                 if file_width:
-                    file_width = file_width.groups()[0]
+                    file_width = file_width.group(1)
                     if re.search('px$', file_width):
                         file_style += 'width: ' + file_width + ';'
                     else:
@@ -957,7 +957,7 @@ def namumark(conn, data, title, main_num, include_num):
 
                 file_height = re.search('height=((?:(?!&).)+)', see_link)
                 if file_height:
-                    file_height = file_height.groups()[0]
+                    file_height = file_height.group(1)
                     if re.search('px$', file_height):
                         file_style += 'height: ' + file_height + ';'
                     else:
@@ -965,7 +965,7 @@ def namumark(conn, data, title, main_num, include_num):
 
                 file_align = re.search('align=((?:(?!&).)+)', see_link)
                 if file_align:
-                    file_align = file_align.groups()[0]
+                    file_align = file_align.group(1)
                     if file_align == 'center':
                         file_align = 'display: block; text-align: center;'
                     else:
@@ -975,7 +975,7 @@ def namumark(conn, data, title, main_num, include_num):
 
                 file_color = re.search('bgcolor=((?:(?!&).)+)', see_link)
                 if file_color:
-                    file_color = 'background: ' + file_color.groups()[0] + '; display: inline-block;'
+                    file_color = 'background: ' + file_color.group(1) + '; display: inline-block;'
                 else:
                     file_color = ''
 
@@ -1078,7 +1078,7 @@ def namumark(conn, data, title, main_num, include_num):
                     data = re.sub('\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]', 'Not exist', data, 1)
             elif re.search('^(\/(?:.+))$', main_link):
                 under_title = re.search('^(\/(?:.+))$', main_link)
-                under_title = under_title.groups()[0]
+                under_title = under_title.group(1)
 
                 if see_link != main_link:
                     data = re.sub('\[\[((?:(?!\[\[|\]\]|<\/td>).)+)\]\]', '[[' + title + under_title + '|' + see_link + ']]', data, 1)

+ 5 - 5
route/topic.py

@@ -44,25 +44,25 @@ def topic_2(conn, topic_num):
         match = re.search('^user:([^/]+)', name)
         if match:
             y_check = 0
-            if ip_or_user(match.groups()[0]) == 1:
-                curs.execute(db_change("select ip from history where ip = ? limit 1"), [match.groups()[0]])
+            if ip_or_user(match.group(1)) == 1:
+                curs.execute(db_change("select ip from history where ip = ? limit 1"), [match.group(1)])
                 u_data = curs.fetchall()
                 if u_data:
                     y_check = 1
                 else:
-                    curs.execute(db_change("select ip from topic where ip = ? limit 1"), [match.groups()[0]])
+                    curs.execute(db_change("select ip from topic where ip = ? limit 1"), [match.group(1)])
                     u_data = curs.fetchall()
                     if u_data:
                         y_check = 1
             else:
-                curs.execute(db_change("select id from user where id = ?"), [match.groups()[0]])
+                curs.execute(db_change("select id from user where id = ?"), [match.group(1)])
                 u_data = curs.fetchall()
                 if u_data:
                     y_check = 1
 
             if y_check == 1:
                 curs.execute(db_change('insert into alarm (name, data, date) values (?, ?, ?)'), [
-                    match.groups()[0],
+                    match.group(1),
                     ip + ' | <a href="/thread/' + topic_num + '#' + num + '">' + name + ' | ' + sub + ' | #' + num + '</a>',
                     today
                 ])

+ 13 - 8
route/view_diff_data.py

@@ -16,36 +16,41 @@ def view_diff_data_2(conn, name):
     curs.execute(db_change("select data from history where id = ? and title = ?"), [first, name])
     first_raw_data = curs.fetchall()
     if first_raw_data:
+        first_raw_data = first_raw_data[0][0].replace('\r', '')
+
         curs.execute(db_change("select data from history where id = ? and title = ?"), [second, name])
         second_raw_data = curs.fetchall()
         if second_raw_data:
+            second_raw_data = second_raw_data[0][0].replace('\r', '')
+
             if first == second:
                 result = ''
             elif first_raw_data == second_raw_data:
                 result = ''
             else:
-                i = 2
+                i = 1
                 include_ins = 0
-                diff_data = '1 : ' + diff_match_patch().diff_prettyHtml(diff_match_patch().diff_main(first_raw_data[0][0], second_raw_data[0][0]))
+                diff_data = diff_match_patch().diff_prettyHtml(diff_match_patch().diff_main(first_raw_data, second_raw_data))
+                end_data = ''
 
-                re_data = re.findall('((?:(?!&para;<br>).)*)(?:&para;<br>|$)', diff_data)
+                re_data = re.findall('(?:(?:(?:(?!&para;<br>).)*)(?:&para;<br>)|(?:(?:(?!&para;<br>).)+)$)', diff_data)
                 for re_in_data in re_data:
+                    re_in_data = re.sub('&para;<br>$', '', re_in_data)
                     if re.search('(<ins |<del )', re_in_data) and re.search('(<\/ins>|<\/del>)', re_in_data):
-                        re_data[i - 2] = str(i) + ' : ' + re_data[i - 2] + '\n'
+                        end_data += str(i) + ' : ' + re_in_data + '\n'
                         include_ins = 0
                     elif re.search('(<\/ins>|<\/del>)', re_in_data):
-                        re_data[i - 2] = str(i) + ' : ' + re_data[i - 2] + '\n'
+                        end_data += str(i) + ' : ' + re_in_data + '\n'
                         include_ins = 0
                     elif re.search('(<ins |<del )', re_in_data) or include_ins == 1:
-                        re_data[i - 2] = str(i) + ' : ' + re_data[i - 2] + '\n'
+                        end_data += str(i) + ' : ' + re_in_data + '\n'
                         include_ins = 1
                     else:
-                        re_data[i - 2] = ''
                         include_ins = 0
 
                     i += 1
                     
-                result = '<pre>' + ''.join(re_data) + '</pre>'
+                result = '<pre>' + end_data + '</pre>'
 
             return easy_minify(flask.render_template(skin_check(),
                 imp = [name, wiki_set(), custom(), other2([' (' + load_lang('compare') + ')', 0])],

+ 2 - 2
route/view_read.py

@@ -36,7 +36,7 @@ def view_read_2(conn, name):
 
     m = re.search("^(.*)\/(.*)$", name)
     if m:
-        uppage = m.groups()[0]
+        uppage = m.group(1)
     else:
         uppage = 0
 
@@ -207,7 +207,7 @@ def view_read_2(conn, name):
 
     match = re.search("^user:([^/]*)", name)
     if match:
-        user_name = match.groups()[0]
+        user_name = match.group(1)
         div = '''
             <div id="get_user_info"></div>
             <script>load_user_info("''' + user_name + '''");</script>

+ 2 - 2
version.json

@@ -1,10 +1,10 @@
 {
     "master" : {
-        "r_ver" : "v3.1.7-stable-02",
+        "r_ver" : "v3.1.7-stable-03",
         "c_ver" : "3180200",
         "s_ver" : "8"
     }, "stable" : {
-        "r_ver" : "v3.1.7-stable-02",
+        "r_ver" : "v3.1.7-stable-03",
         "c_ver" : "3180200",
         "s_ver" : "8"
     }

+ 2 - 1
views/main_css/css/main.css

@@ -49,4 +49,5 @@ blockquote { background-image: url(/views/acme/img/quote.png); background-positi
 div#topic_scroll { max-height: 500px; overflow: scroll; -ms-overflow-style: none; scrollbar-width: none; }
 div#topic_scroll::-webkit-scrollbar { display: none; }
 .hidden_link { filter: blur(3px); }
-.hidden_link:hover { filter: none; }
+.hidden_link:hover { filter: none; }
+#content { height: 500px; }

+ 59 - 0
views/main_css/js/do_upload_paste.js

@@ -0,0 +1,59 @@
+/*
+window.addEventListener('DOMContentLoaded', function() {
+    if(window.location.pathname.match(/^\/edit\//i)) {
+        const textarea = document.querySelector("textarea");
+        if (textarea) {
+            textarea.addEventListener("paste", pasteListener);
+        }
+    }
+});
+
+function pasteListener(e) {
+    // find file
+    if (e.clipboardData && e.clipboardData.items) {
+        const items = e.clipboardData.items;
+        let haveImageInClipboard = false;
+        const formData = new FormData();
+        for (let i = 0; i < items.length; i++) {
+            if (items[i].type.indexOf("image") !== -1) {
+                const file = items[i].getAsFile();
+                const customName = prompt("파일 이름을 설정해주세요. (확장자는 생략)");
+                
+                if (!customName) {
+                    return alert("취소되었습니다.");
+                }
+                
+                const customFile = new File([file], customName + ".png", { type: file.type });
+                formData.append("f_data[]", customFile);
+                haveImageInClipboard = true;
+                e.preventDefault();
+                
+                break;
+            }
+        }
+        if (!haveImageInClipboard) {
+            return;
+        }
+
+        // send to server
+        fetch("/upload", {
+            method: "POST",
+            body: formData,
+        }).then((res) => {
+            if (res.status === 200 || res.status === 201) {
+                const url = res.url;
+                alert(
+                    '클립보드의 이미지를 성공적으로 업로드했습니다. 아래 텍스트로 본문에 삽입할 수 있습니다.' +
+                    '[[' + decodeURIComponent(url.replace(/.*\/w\/file/, "file")) + ']]'
+                );
+            } else {
+                console.error("[ERROR] PasteUpload Fail :", res.statusText);
+                alert("클립보드의 이미지를 업로드하는데 실패했습니다. 파일 이름 중복일 수 있습니다.");
+            }
+        }).catch((err) => {
+            console.error("[ERROR] PasteUpload Fail :", JSON.stringify(err), err);
+            alert("클립보드의 이미지를 업로드하는데 실패했습니다. 파일 이름 중복일 수 있습니다.");
+        });
+    }
+}
+*/

+ 0 - 22
views/main_css/js/dummy_do_open_foot.js

@@ -1,22 +0,0 @@
-function dummy_do_open_foot(name) {
-    var found_include = name.match(/^(include_(?:[0-9]+)\-)/);
-    if(found_include) {
-        var o_data = document.getElementById(found_include[1] + 'c' + name.replace(/^(?:include_(?:[0-9]+)\-)/, ''));
-    } else {
-        var o_data = document.getElementById('c' + name);
-    }
-
-    var g_data = document.getElementById(name.replace(/\.([0-9]+)$/, ''));
-
-    if(o_data.innerHTML === '') {
-        o_data.style.display = 'block';
-        o_data.innerHTML += '' +
-            '<div class="foot_in">' +
-                '<a onclick="do_open_foot(\'' + name + '\')" href="javascript:void(0);">(X)</a> <a onclick="do_open_foot(\'' + name + '\')" href="#' + name + '">(Go)</a> ' + g_data.innerHTML +
-            '</div>' +
-        '';
-    } else {
-        o_data.style.display = 'none';
-        o_data.innerHTML = '';
-    }
-}

+ 4 - 4
views/main_css/js/load_namumark.js

@@ -1,6 +1,8 @@
 function get_link_state(data, i = 0) { 
     if(document.getElementsByClassName(data + 'link_finder')[i]) {
         var link_data = document.getElementsByClassName(data + 'link_finder')[i];
+        
+        get_link_state(data, i + 1);
 
         var xhr = new XMLHttpRequest();
         xhr.open("GET", link_data.href.replace('/w/', '/api/w/').replace(/#([^#]*)/, '') + "?exist=1", true);
@@ -13,8 +15,6 @@ function get_link_state(data, i = 0) {
                 } else {
                     document.getElementsByClassName(data + 'link_finder')[i].id = "";
                 }
-
-                get_link_state(data, i + 1);
             }
         }
     }
@@ -23,6 +23,8 @@ function get_link_state(data, i = 0) {
 function get_file_state(data, i = 0) {       
     if(document.getElementsByClassName(data + 'file_finder_1')[i]) {
         var file_data = document.getElementsByClassName(data + 'file_finder_1')[i];
+        
+        get_file_state(data, i + 1);
 
         var xhr = new XMLHttpRequest();
         xhr.open("GET", file_data.src.replace('/image/', '/api/image/'), true);
@@ -35,8 +37,6 @@ function get_file_state(data, i = 0) {
                 } else {
                     document.getElementsByClassName(data + 'file_finder_2')[i].innerHTML = "";
                 }
-            
-                get_file_state(data, i + 1);
             }
         }
     }

+ 23 - 29
views/main_css/js/load_skin_set.js

@@ -1,6 +1,4 @@
-function main_css_get_post() {
-    var cookies = document.cookie;
-    
+function main_css_get_post() {    
     var check = document.getElementById('strike');
     if(check.value === 'normal') {
         document.cookie = 'main_css_del_strike=0;';
@@ -36,36 +34,34 @@ function main_css_get_post() {
     history.go(0);
 }
 
-function main_css_skin_load() {
-    var cookies = document.cookie;
-    
+function main_css_skin_load() {    
     var head_data = document.querySelector('head');
-    if(cookies.match(main_css_regex_data('main_css_del_strike'))) {
-        if(cookies.match(main_css_regex_data('main_css_del_strike'))[1] === '1') {
+    if(document.cookie.match(main_css_regex_data('main_css_del_strike'))) {
+        if(document.cookie.match(main_css_regex_data('main_css_del_strike'))[1] === '1') {
             head_data.innerHTML += '<style>s { text-decoration: none; } s:hover { background-color: transparent; }</style>';
-        } else if(cookies.match(main_css_regex_data('main_css_del_strike'))[1] === '2') {
+        } else if(document.cookie.match(main_css_regex_data('main_css_del_strike'))[1] === '2') {
             head_data.innerHTML += '<style>s { display: none; }</style>';
         }
     }
 
-    if(cookies.match(main_css_regex_data('main_css_del_bold'))) {
-        if(cookies.match(main_css_regex_data('main_css_del_bold'))[1] === '1') {
+    if(document.cookie.match(main_css_regex_data('main_css_del_bold'))) {
+        if(document.cookie.match(main_css_regex_data('main_css_del_bold'))[1] === '1') {
             head_data.innerHTML += '<style>b { font-weight: normal; }</style>';
-        } else if(cookies.match(main_css_regex_data('main_css_del_bold'))[1] === '2') {
+        } else if(document.cookie.match(main_css_regex_data('main_css_del_bold'))[1] === '2') {
             head_data.innerHTML += '<style>b { display: none; }</style>';
         }
     }
 
     if(
-        cookies.match(main_css_regex_data('main_css_include_link')) &&
-        cookies.match(main_css_regex_data('main_css_include_link'))[1] === '1'
+        document.cookie.match(main_css_regex_data('main_css_include_link')) &&
+        document.cookie.match(main_css_regex_data('main_css_include_link'))[1] === '1'
     ) {
         head_data.innerHTML += '<style>#include_link { display: inline; }</style>';
     }
 
     if(
-        cookies.match(main_css_regex_data('main_css_category_set')) &&
-        cookies.match(main_css_regex_data('main_css_category_set'))[1] === '1'
+        document.cookie.match(main_css_regex_data('main_css_category_set')) &&
+        document.cookie.match(main_css_regex_data('main_css_category_set'))[1] === '1'
     ) {
         var get_category = document.getElementById('cate_all');
         if(get_category) {
@@ -84,9 +80,7 @@ function main_css_regex_data(data) {
     return new RegExp('(?:^|; )' + data + '=([^;]*)');
 }
 
-function main_css_skin_set() {
-    var cookies = document.cookie;
-    
+function main_css_skin_set() {    
     if(window.location.pathname === '/main_skin_set') {
         var set_language = {
             "en-US" : {
@@ -116,8 +110,8 @@ function main_css_skin_set() {
             }
         }
 
-        var language = cookies.match(main_css_regex_data('language'))[1];
-        var user_language = cookies.match(main_css_regex_data('user_language'))[1];
+        var language = document.cookie.match(main_css_regex_data('language'))[1];
+        var user_language = document.cookie.match(main_css_regex_data('user_language'))[1];
         if(user_language in set_language) {
             language = user_language;
         }
@@ -139,8 +133,8 @@ function main_css_skin_set() {
         while(1) {
             if(strike_list[i]) {
                 if(
-                    cookies.match(main_css_regex_data('main_css_del_strike')) && 
-                    cookies.match(main_css_regex_data('main_css_del_strike'))[1] === strike_list[i][0]
+                    document.cookie.match(main_css_regex_data('main_css_del_strike')) && 
+                    document.cookie.match(main_css_regex_data('main_css_del_strike'))[1] === strike_list[i][0]
                 ) {
                     set_data["strike"] = '<option value="' + strike_list[i][1] + '">' + strike_list[i][2] + '</option>' + set_data["strike"];
                 } else {
@@ -163,8 +157,8 @@ function main_css_skin_set() {
         while(1) {
             if(bold_list[i]) {
                 if(
-                    cookies.match(main_css_regex_data('main_css_del_bold')) && 
-                    cookies.match(main_css_regex_data('main_css_del_bold'))[1] === bold_list[i][0]
+                    document.cookie.match(main_css_regex_data('main_css_del_bold')) && 
+                    document.cookie.match(main_css_regex_data('main_css_del_bold'))[1] === bold_list[i][0]
                 ) {
                     set_data["bold"] = '<option value="' + bold_list[i][1] + '">' + bold_list[i][2] + '</option>' + set_data["bold"];
                 } else {
@@ -178,8 +172,8 @@ function main_css_skin_set() {
         }
 
         if(
-            cookies.match(main_css_regex_data('main_css_include_link')) &&
-            cookies.match(main_css_regex_data('main_css_include_link'))[1] === '1'
+            document.cookie.match(main_css_regex_data('main_css_include_link')) &&
+            document.cookie.match(main_css_regex_data('main_css_include_link'))[1] === '1'
         ) {
             set_data["include"] = "checked";
         }
@@ -193,8 +187,8 @@ function main_css_skin_set() {
         while(1) {
             if(category_list[i]) {
                 if(
-                    cookies.match(main_css_regex_data('main_css_category_set')) && 
-                    cookies.match(main_css_regex_data('main_css_category_set'))[1] === category_list[i][0]
+                    document.cookie.match(main_css_regex_data('main_css_category_set')) && 
+                    document.cookie.match(main_css_regex_data('main_css_category_set'))[1] === category_list[i][0]
                 ) {
                     set_data["category"] = '<option value="' + category_list[i][1] + '">' + category_list[i][2] + '</option>' + set_data["category"];
                 } else {

+ 87 - 26
views/main_css/js/render_html.js

@@ -6,39 +6,100 @@ function render_html(name = '') {
         if(document.getElementById(name + '_' + String(num))) {
             data = document.getElementById(name + '_' + String(num)).innerHTML;
 
-            var t_data = ['b', 'i', 's', 'del', 'strong', 'bold', 'em', 'sub', 'sup']
+            var src_list = ['www.youtube.com', 'www.google.com', 'play-tv.kakao.com'];
+            var t_data = [
+                'b', 'i', 's', 'del', 'strong', 'bold', 'em', 'sub', 'sup', 
+                'div', 'span', 
+                'a',
+                'iframe',
+                'video'
+            ];
             for(var key in t_data) {
-                var patt = new RegExp('&lt;' + t_data[key] + '&gt;((?:(?!&lt;\/' + t_data[key] + '&gt;).)*)&lt;\/' + t_data[key] + '&gt;', 'ig');
-                data = data.replace(patt, '<' + t_data[key] + '>$1</' + t_data[key] + '>');
-            }
+                patt = new RegExp(
+                    '&lt;' + t_data[key] + '( (?:(?:(?!&gt;).)+))?&gt;((?:(?!&lt;\/' + t_data[key] + '&gt;).)*)&lt;\/' + t_data[key] + '&gt;',
+                    'ig'
+                );
+                
+                data = data.replace(patt, function(full, in_data, in_data_2) {
+                    if(['b', 'i', 's', 'del', 'strong', 'bold', 'em', 'sub', 'sup'].includes(t_data[key])) {
+                        return '<' + t_data[key] + '>' + in_data_2 + '</' + t_data[key] + '>'
+                    } else if(t_data[key] === 'div' || t_data[key] === 'span') {
+                        var style_data = in_data.match(/ style=['"]([^'"]*)['"]/);
+                        if(style_data) {
+                            style_data = style_data[1].replace(/position/ig, '');
+                        } else {
+                            style_data = '';
+                        }
 
-            var src_list = {
-                'www.youtube.com' : '1',
-                'www.google.com' : '1'
-            }
-            data = data.replace(/&lt;iframe( (?:(?:(?!&gt;).)+))&gt;&lt;\/iframe&gt;/ig, function(full, in_data) {
-                var src_data = in_data.match(/ src=['"]https:\/\/([^/'"]+)(?:[^'"]+)['"](?: |$)/);
-                if(src_data) {
-                    if(src_list[src_data[1]]) {
-                        return '<iframe' + in_data + '></iframe>';
+                        return '<' + t_data[key] + ' style="' + style_data + '">' + in_data_2 + '</' + t_data[key] + '>';
+                    } else if(t_data[key] === 'a') {
+                        var link_data = in_data.match(/ href=['"]([^'"]*)['"]/);
+                        if(link_data) {
+                            link_data = link_data[1].replace(/^javascript:/ig, '');
+                        } else {
+                            link_data = '';
+                        }
+
+                        return '<' + t_data[key] + ' id="out_link" href="' + link_data + '">' + in_data_2 + '</' + t_data[key] + '>';
+                    } else if(t_data[key] === 'iframe') {
+                        var src_data = in_data.match(/ src=['"]([^'"]*)['"]/);
+                        if(src_data) {
+                            src_data = src_data[1];
+
+                            var src_check = src_data.match(/^http(?:s)?:\/\/([^/]+)/);
+                            if(src_check) { 
+                                if(!src_list.includes(src_check[1])) {
+                                    src_data = '';
+                                }
+                            } else {
+                                src_data = '';
+                            }
+                        } else {
+                            src_data = '';
+                        }
+
+                        var width_data = in_data.match(/ width=['"]([^'"]*)['"]/);
+                        if(width_data) {
+                            width_data = width_data[1];
+                        } else {
+                            width_data = '';
+                        }
+
+                        var height_data = in_data.match(/ height=['"]([^'"]*)['"]/);
+                        if(height_data) {
+                            height_data = height_data[1];
+                        } else {
+                            height_data = '';
+                        }
+
+                        return '<' + t_data[key] + ' src="' + src_data + '" width="' + width_data + '" height="' + height_data + '" allowfullscreen frameborder="0">' + in_data_2 + '</' + t_data[key] + '>';
                     } else {
-                        return full;
-                    }
-                }
-            });
+                        var src_data = in_data.match(/ src=['"]([^'"]*)['"]/);
+                        if(src_data) {
+                            src_data = src_data[1];
+                        } else {
+                            src_data = '';
+                        }
 
-            t_data = ['div', 'span']
-            for(var key in t_data) {
-                patt = new RegExp('&lt;' + t_data[key] + ' style=["\']((?:(?!["\']).)+)["\']&gt;((?:(?!&lt;\/' + t_data[key] + '&gt;).)*)&lt;\/' + t_data[key] + '&gt;', 'ig');
-                data = data.replace(patt, function(full, in_data, in_data_2) {
-                    return '<' + t_data[key] + ' style="' + in_data.replace(/position/ig, '') + '">' + in_data_2 + '</' + t_data[key] + '>'
+                        var width_data = in_data.match(/ width=['"]([^'"]*)['"]/);
+                        if(width_data) {
+                            width_data = width_data[1];
+                        } else {
+                            width_data = '';
+                        }
+
+                        var height_data = in_data.match(/ height=['"]([^'"]*)['"]/);
+                        if(height_data) {
+                            height_data = height_data[1];
+                        } else {
+                            height_data = '';
+                        }
+
+                        return '<' + t_data[key] + ' src="' + src_data + '" width="' + width_data + '" height="' + height_data + '">' + in_data_2 + '</' + t_data[key] + '>';
+                    }
                 });
             }
 
-            data = data.replace(/&lt;a href=["\']((?:(?!["\']).)+)["\']&gt;((?:(?!&lt;\/a&gt;).)*)&lt;\/a&gt;/ig, function(full, in_data, in_data_2) {
-                return '<a id="out_link" href="' + in_data.replace(/^javascript/ig, '') + '">' + in_data_2 + '</a>'
-            });
-
             document.getElementById(name + '_' + String(num)).innerHTML = data;
         } else {
             break;

+ 17 - 13
views/marisa/css/main.css

@@ -1,9 +1,12 @@
+@import url(https://fonts.googleapis.com/earlyaccess/nanumgothic.css);
+
 body {
     max-width: 100%;
     word-break: break-all;
     word-wrap: break-word;
     margin: 0;
-    font-family: '나눔고딕', 'Nanum Gothic', 'Noto Sans KR', 'Malgun Gothic', '맑은 고딕', sans-serif;
+    font-family: "나눔고딕", "Nanum Gothic", sans-serif;
+    font-size: 14px;
 }
 
 ul {
@@ -22,7 +25,7 @@ hr {
 
 button {
     padding: 10px;
-    border: 2px solid gainsboro;
+    border: 1px solid gainsboro;
     background: white;
     cursor: pointer;
 }
@@ -42,7 +45,7 @@ li {
 #top {
     width: 100%;
     background: #4a4a4a;
-    padding: 20px;
+    padding: 15px;
     padding-right: 0;
     padding-left: 0;
     position: relative;
@@ -206,11 +209,11 @@ h6 {
 #top_tool {
     float: right;
     color: white;
-    margin-top: -8px;
+    margin-top: -9px;
 }
 
 .cel_in_cel {
-    border: 2px solid gainsboro;
+    border: 1px solid gainsboro;
     padding: 10px;
     position: absolute;
     background: white;
@@ -255,7 +258,7 @@ h6 {
 
 div#pre_search {
     padding: 10px;
-    border: 2px solid #4a4a4a;
+    border: 1px solid #4a4a4a;
     width: 200px;
     color: black;
     margin-top: 5px;
@@ -306,11 +309,11 @@ a {
 }
 
 pre, #toc, #cate, #redirect {
-    border: 2px solid gainsboro;
+    border: 1px solid gainsboro;
 }
 
 textarea, input {
-    border: 2px solid gainsboro;
+    border: 1px solid gainsboro;
     font-family: '나눔고딕', 'Nanum Gothic', 'Noto Sans KR', 'Malgun Gothic', '맑은 고딕', sans-serif;
     padding: 5px;
     margin-left: -5px;
@@ -345,7 +348,7 @@ textarea, input {
     position: fixed;
     bottom: 0;
     right: 0;
-    border: 2px solid #4a4a4a;
+    border: 1px solid #4a4a4a;
     background: #4a4a4a;
     text-align: center;
 }
@@ -358,7 +361,7 @@ blockquote {
     padding: 1em calc(2em + 25px) 1em 1em;
     margin: 1em 0em 0em;
     background: #eeeeee;
-    border: 2px dashed #ccc;
+    border: 1px dashed #ccc;
     border-left: 5px solid black;
 }
 
@@ -389,9 +392,6 @@ div#last_edit {
     color: limegreen;
 }
 
-#mobile_search_input {
-}
-
 #top_menu_groups {
     display: inline-block;
 }
@@ -422,4 +422,8 @@ div#last_edit {
 
 button#save {
     background: #c6ffc6;
+}
+
+div#top_tool_cel a:hover, #logo a:hover, #search button:hover, div#nav_bar a:hover {
+    color: #aaa;
 }

+ 2 - 2
views/marisa/index.html

@@ -8,10 +8,10 @@
             <title>{{imp[0]}} - {{imp[1][0]}}</title>
         {% endif %}
         {{imp[3][3]|safe}}
-        <link rel="stylesheet" href="/views/marisa/css/main.css?ver=8">
+        <link rel="stylesheet" href="/views/marisa/css/main.css?ver=9">
         <script src="/views/marisa/js/skin_set.js?ver=4"></script>
         <script src="/views/marisa/js/main.js?ver=3"></script>
-        <script>main_load(); window.onload = function () { skin_set(); }</script>
+        <script>main_load(); window.addEventListener('DOMContentLoaded', function() { skin_set(); });</script>
         <script src="https://code.iconify.design/1/1.0.3/iconify.min.js"></script>
         <link rel="shortcut icon" href="/views/main_css/file/favicon.ico?ver=1">
         {{imp[1][5]|safe}}

+ 1 - 1
views/marisa/info.json

@@ -1,5 +1,5 @@
 {
     "name" : "Marisa",
-    "skin_ver" : "v1.2.0",
+    "skin_ver" : "v1.2.1",
     "require_ver" : "8"
 }