diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000..21bf08d Binary files /dev/null and b/.DS_Store differ diff --git a/Echo_Z/__pycache__/settings.cpython-39.pyc b/Echo_Z/__pycache__/settings.cpython-39.pyc index 4faaecd..8723b0b 100644 Binary files a/Echo_Z/__pycache__/settings.cpython-39.pyc and b/Echo_Z/__pycache__/settings.cpython-39.pyc differ diff --git a/Echo_Z/settings.py b/Echo_Z/settings.py index b1abd50..b962349 100644 --- a/Echo_Z/settings.py +++ b/Echo_Z/settings.py @@ -34,6 +34,7 @@ INSTALLED_APPS = [ 'simpleui', 'home', 'api', + 'comment', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', diff --git a/README.md b/README.md index 9e6e8fe..32e3957 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,12 @@ # 目录结构描述 ├── ReadMe.md // 帮助文档 - ├── home // 首页app + ├── home // 文章相关app ├── api // 项目api + ├── comment // 评论app + └── Echo-Z // 项目配置等目录 # 使用说明 @@ -35,3 +37,11 @@ python manage.py runserver 9999 ###### 1.2.0 2025年6月19日 完成文章的基础管理、文章的列表显示、文章的详细信息的功能编写,功能有待完善 + +###### 1.2.1 + 2025年6月23日 + 新增 + 1.文章详情页上一篇、下一篇功能 + 2.文章详情页评论列表显示 + 3.文章详情页评论发布 + 4.文章详情页点赞与取消点赞 diff --git a/api/__pycache__/admin.cpython-39.pyc b/api/__pycache__/admin.cpython-39.pyc index 98c6b4a..b2f0163 100644 Binary files a/api/__pycache__/admin.cpython-39.pyc and b/api/__pycache__/admin.cpython-39.pyc differ diff --git a/api/__pycache__/models.cpython-39.pyc b/api/__pycache__/models.cpython-39.pyc index b2043fb..9b01b34 100644 Binary files a/api/__pycache__/models.cpython-39.pyc and b/api/__pycache__/models.cpython-39.pyc differ diff --git a/api/__pycache__/urls.cpython-39.pyc b/api/__pycache__/urls.cpython-39.pyc index db4cb26..572e273 100644 Binary files a/api/__pycache__/urls.cpython-39.pyc and b/api/__pycache__/urls.cpython-39.pyc differ diff --git a/api/__pycache__/views.cpython-39.pyc b/api/__pycache__/views.cpython-39.pyc new file mode 100644 index 0000000..357d7d7 Binary files /dev/null and b/api/__pycache__/views.cpython-39.pyc differ diff --git a/api/urls.py b/api/urls.py index 2b844f6..0309d09 100644 --- a/api/urls.py +++ b/api/urls.py @@ -1,6 +1,9 @@ from django.contrib import admin from django.urls import path - +from . import views as api_views urlpatterns = [ - + path('create_comment/', api_views.add_comment), + path('link//', api_views.like), + path('unlink//', api_views.un_like), + path('check_like//', api_views.check_like), ] \ No newline at end of file diff --git a/api/views.py b/api/views.py index 91ea44a..f48a03a 100644 --- a/api/views.py +++ b/api/views.py @@ -1,3 +1,46 @@ -from django.shortcuts import render +from datetime import datetime +from django.shortcuts import render,HttpResponse +from django.http import JsonResponse,HttpResponseNotAllowed,HttpResponseRedirect,JsonResponse +from django.views.decorators.http import require_http_methods + +from comment.models import comment +from home.models import * # Create your views here. + +@require_http_methods(["POST"]) +def add_comment(request, id): + create_comment = comment.objects.create(comment_Content=request.POST.get("comment"),comment_User=request.POST.get("username"), comment_Time=datetime.now(), archives_Id=id, qq=request.POST.get("qqNum")) + create_comment.save() + return HttpResponseRedirect("/archives/"+id) + +@require_http_methods(["POST"]) +def check_like(request, id, uuid): + articles_likes = ArticlesLike.objects.filter(articles_id=id,uuid=uuid) + if articles_likes: + return JsonResponse({"liked":True}) + else: + return JsonResponse({"liked":False}) + +@require_http_methods(["POST"]) +def like(request, id, uuid): + article_stat = 0 + if not ArticlesLike.objects.filter(articles_id=id, uuid=uuid): + ArticlesLike.objects.create(articles_id=id, uuid=uuid) + article = Articles.objects.get(id=id) + article.stat += 1 + article_stat = article.stat + article.save() + return JsonResponse({"success":True,"new_count": article_stat}) + +@require_http_methods(["POST"]) +def un_like(request,id, uuid): + article_stat = 0 + if ArticlesLike.objects.filter(articles_id=id, uuid=uuid): + ArticlesLike.objects.filter(articles_id=id, uuid=uuid).delete() + article = Articles.objects.get(id=id) + article.stat -= 1 + article_stat = article.stat + article.save() + + return JsonResponse({"success":True,"new_count": article_stat}) diff --git a/comment/__init__.py b/comment/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/comment/__pycache__/__init__.cpython-39.pyc b/comment/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..c7019d8 Binary files /dev/null and b/comment/__pycache__/__init__.cpython-39.pyc differ diff --git a/comment/__pycache__/admin.cpython-39.pyc b/comment/__pycache__/admin.cpython-39.pyc new file mode 100644 index 0000000..4076ffa Binary files /dev/null and b/comment/__pycache__/admin.cpython-39.pyc differ diff --git a/comment/__pycache__/apps.cpython-39.pyc b/comment/__pycache__/apps.cpython-39.pyc new file mode 100644 index 0000000..bdca114 Binary files /dev/null and b/comment/__pycache__/apps.cpython-39.pyc differ diff --git a/comment/__pycache__/models.cpython-39.pyc b/comment/__pycache__/models.cpython-39.pyc new file mode 100644 index 0000000..cfd4d8f Binary files /dev/null and b/comment/__pycache__/models.cpython-39.pyc differ diff --git a/comment/admin.py b/comment/admin.py new file mode 100644 index 0000000..dbef896 --- /dev/null +++ b/comment/admin.py @@ -0,0 +1,7 @@ +from django.contrib import admin +from comment.models import comment +# Register your models here. + +@admin.register(comment) +class CommentAdmin(admin.ModelAdmin): + list_display = ["comment_Content","comment_User","comment_Time", "archives_Id"] \ No newline at end of file diff --git a/comment/apps.py b/comment/apps.py new file mode 100644 index 0000000..b50dc0a --- /dev/null +++ b/comment/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class CommentConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'comment' + verbose_name = "评论" diff --git a/comment/migrations/0001_initial.py b/comment/migrations/0001_initial.py new file mode 100644 index 0000000..f0233ed --- /dev/null +++ b/comment/migrations/0001_initial.py @@ -0,0 +1,27 @@ +# Generated by Django 4.2.23 on 2025-06-20 02:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='comment', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('comment_Content', models.TextField(verbose_name='评论内容')), + ('comment_User', models.CharField(max_length=100, verbose_name='评论者')), + ('comment_Time', models.DateTimeField(verbose_name='评论时间')), + ], + options={ + 'verbose_name': '评论', + 'verbose_name_plural': '评论管理', + }, + ), + ] diff --git a/comment/migrations/0002_comment_archives_id.py b/comment/migrations/0002_comment_archives_id.py new file mode 100644 index 0000000..49d86e2 --- /dev/null +++ b/comment/migrations/0002_comment_archives_id.py @@ -0,0 +1,18 @@ +# Generated by Django 4.2.23 on 2025-06-20 03:01 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('comment', '0001_initial'), + ] + + operations = [ + migrations.AddField( + model_name='comment', + name='archives_Id', + field=models.IntegerField(default=0, verbose_name='文章idßß'), + ), + ] diff --git a/comment/migrations/0003_comment_qq_alter_comment_archives_id.py b/comment/migrations/0003_comment_qq_alter_comment_archives_id.py new file mode 100644 index 0000000..10b3355 --- /dev/null +++ b/comment/migrations/0003_comment_qq_alter_comment_archives_id.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.23 on 2025-06-20 04:53 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('comment', '0002_comment_archives_id'), + ] + + operations = [ + migrations.AddField( + model_name='comment', + name='qq', + field=models.IntegerField(default=1000, verbose_name='评论者qq号'), + ), + migrations.AlterField( + model_name='comment', + name='archives_Id', + field=models.IntegerField(default=0, verbose_name='文章id'), + ), + ] diff --git a/comment/migrations/__init__.py b/comment/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/comment/migrations/__pycache__/0001_initial.cpython-39.pyc b/comment/migrations/__pycache__/0001_initial.cpython-39.pyc new file mode 100644 index 0000000..3df6a79 Binary files /dev/null and b/comment/migrations/__pycache__/0001_initial.cpython-39.pyc differ diff --git a/comment/migrations/__pycache__/0002_comment_archives_id.cpython-39.pyc b/comment/migrations/__pycache__/0002_comment_archives_id.cpython-39.pyc new file mode 100644 index 0000000..45c923e Binary files /dev/null and b/comment/migrations/__pycache__/0002_comment_archives_id.cpython-39.pyc differ diff --git a/comment/migrations/__pycache__/0003_comment_qq_alter_comment_archives_id.cpython-39.pyc b/comment/migrations/__pycache__/0003_comment_qq_alter_comment_archives_id.cpython-39.pyc new file mode 100644 index 0000000..189ec9c Binary files /dev/null and b/comment/migrations/__pycache__/0003_comment_qq_alter_comment_archives_id.cpython-39.pyc differ diff --git a/comment/migrations/__pycache__/__init__.cpython-39.pyc b/comment/migrations/__pycache__/__init__.cpython-39.pyc new file mode 100644 index 0000000..3f0198a Binary files /dev/null and b/comment/migrations/__pycache__/__init__.cpython-39.pyc differ diff --git a/comment/models.py b/comment/models.py new file mode 100644 index 0000000..fe2f76a --- /dev/null +++ b/comment/models.py @@ -0,0 +1,16 @@ +from django.db import models + +# Create your models here. +class comment(models.Model): + comment_Content = models.TextField(verbose_name="评论内容") + comment_User = models.CharField(max_length=100,verbose_name="评论者") + comment_Time = models.DateTimeField(verbose_name="评论时间") + archives_Id = models.IntegerField(default=0, verbose_name="文章id") + qq = models.IntegerField(default=1000, verbose_name="评论者qq号") + + def __str__(self): + return self.comment_content + + class Meta: + verbose_name = "评论" + verbose_name_plural = "评论管理" \ No newline at end of file diff --git a/comment/tests.py b/comment/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/comment/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/comment/views.py b/comment/views.py new file mode 100644 index 0000000..27cdb63 --- /dev/null +++ b/comment/views.py @@ -0,0 +1,4 @@ +from django.shortcuts import render + +# Create your views here. + diff --git a/db.sqlite3 b/db.sqlite3 index 8a0c581..2860585 100644 Binary files a/db.sqlite3 and b/db.sqlite3 differ diff --git a/home/.DS_Store b/home/.DS_Store new file mode 100644 index 0000000..8d6a5d0 Binary files /dev/null and b/home/.DS_Store differ diff --git a/home/__pycache__/admin.cpython-39.pyc b/home/__pycache__/admin.cpython-39.pyc index 21325e5..69620bd 100644 Binary files a/home/__pycache__/admin.cpython-39.pyc and b/home/__pycache__/admin.cpython-39.pyc differ diff --git a/home/__pycache__/apps.cpython-39.pyc b/home/__pycache__/apps.cpython-39.pyc index 5b97444..b2497b5 100644 Binary files a/home/__pycache__/apps.cpython-39.pyc and b/home/__pycache__/apps.cpython-39.pyc differ diff --git a/home/__pycache__/models.cpython-39.pyc b/home/__pycache__/models.cpython-39.pyc index f040bf1..8eb28ca 100644 Binary files a/home/__pycache__/models.cpython-39.pyc and b/home/__pycache__/models.cpython-39.pyc differ diff --git a/home/__pycache__/views.cpython-39.pyc b/home/__pycache__/views.cpython-39.pyc index 03b5716..59b6c65 100644 Binary files a/home/__pycache__/views.cpython-39.pyc and b/home/__pycache__/views.cpython-39.pyc differ diff --git a/home/admin.py b/home/admin.py index 4d7d617..a4e55c1 100644 --- a/home/admin.py +++ b/home/admin.py @@ -1,10 +1,9 @@ from django.contrib import admin -from home.models import articles - +from home.models import * # Register your models here. -@admin.register(articles) +@admin.register(Articles) class DepartmentAdmin(admin.ModelAdmin): # 要显示的字段 list_display = ('title', 'abstract', 'created','stat') @@ -14,4 +13,13 @@ class DepartmentAdmin(admin.ModelAdmin): # 分页显示,一页的数量 list_per_page = 10 + actions_on_top = True + +@admin.register(ArticlesLike) +class ArticlesLikeAdmin(admin.ModelAdmin): + list_display = ('articles_id', 'uuid') + readonly_fields = ('articles_id', 'uuid') + # 分页显示,一页的数量 + list_per_page = 10 + actions_on_top = True \ No newline at end of file diff --git a/home/apps.py b/home/apps.py index 202404b..5894a0a 100644 --- a/home/apps.py +++ b/home/apps.py @@ -4,4 +4,4 @@ from django.apps import AppConfig class HomeConfig(AppConfig): default_auto_field = 'django.db.models.BigAutoField' name = 'home' - verbose_name = "文章管理" + verbose_name = "文章" diff --git a/home/migrations/0005_articleslike.py b/home/migrations/0005_articleslike.py new file mode 100644 index 0000000..9f0bf33 --- /dev/null +++ b/home/migrations/0005_articleslike.py @@ -0,0 +1,25 @@ +# Generated by Django 4.2.23 on 2025-06-22 18:14 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('home', '0004_articles_author'), + ] + + operations = [ + migrations.CreateModel( + name='ArticlesLike', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('articles_id', models.IntegerField(default=0, verbose_name='文章id')), + ('uuid', models.CharField(max_length=100, verbose_name='点赞用户标识')), + ], + options={ + 'verbose_name': '点赞', + 'verbose_name_plural': '点赞管理', + }, + ), + ] diff --git a/home/migrations/__pycache__/0005_articleslike.cpython-39.pyc b/home/migrations/__pycache__/0005_articleslike.cpython-39.pyc new file mode 100644 index 0000000..57b0e93 Binary files /dev/null and b/home/migrations/__pycache__/0005_articleslike.cpython-39.pyc differ diff --git a/home/models.py b/home/models.py index f3be8f5..d8f8a3a 100644 --- a/home/models.py +++ b/home/models.py @@ -2,7 +2,7 @@ from django.db import models # Create your models here. -class articles(models.Model): +class Articles(models.Model): title = models.CharField(max_length=100,verbose_name="文章标题") content = models.TextField(verbose_name="文章内容") abstract = models.TextField(verbose_name="文章摘要") @@ -20,3 +20,13 @@ class articles(models.Model): def __str__(self): return self.title +class ArticlesLike(models.Model): + articles_id = models.IntegerField(default=0,verbose_name="文章id") + uuid = models.CharField(max_length=100,verbose_name="点赞用户标识") + + class Meta: + verbose_name = "点赞" + verbose_name_plural = "点赞管理" + + def __str__(self): + return self.uuid \ No newline at end of file diff --git a/home/static/aaa.js b/home/static/aaa.js new file mode 100644 index 0000000..898ee05 --- /dev/null +++ b/home/static/aaa.js @@ -0,0 +1,12 @@ +/** + * FingerprintJS v4.6.2 - Copyright (c) FingerprintJS, Inc, 2025 (https://fingerprint.com) + * + * Licensed under Business Source License 1.1 https://mariadb.com/bsl11/ + * Licensor: FingerprintJS, Inc. + * Licensed Work: FingerprintJS browser fingerprinting library + * Additional Use Grant: None + * Change Date: Four years from first release for the specific version. + * Change License: MIT, text at https://opensource.org/license/mit/ with the following copyright notice: + * Copyright 2015-present FingerprintJS, Inc. + */ +var n=function(){return n=Object.assign||function(n){for(var e,t=1,r=arguments.length;t0&&o[o.length-1])||6!==c[0]&&2!==c[0])){a=0;continue}if(3===c[0]&&(!o||c[1]>o[0]&&c[1]=i+o?(i=c,[4,new Promise((function(n){var e=new MessageChannel;e.port1.onmessage=function(){return n()},e.port2.postMessage(null)}))]):[3,3]):[3,4];case 2:t.sent(),t.label=3;case 3:return++a,[3,1];case 4:return[2,e]}}))}))}function u(n){return n.then(void 0,(function(){})),n}function l(n){return parseInt(n)}function s(n){return parseFloat(n)}function d(n,e){return"number"==typeof n&&isNaN(n)?e:n}function f(n){return n.reduce((function(n,e){return n+(e?1:0)}),0)}function m(n,e){if(void 0===e&&(e=1),Math.abs(e)>=1)return Math.round(n/e)*e;var t=1/e;return Math.round(n*t)/t}function v(n,e){var t=n[0]>>>16,r=65535&n[0],o=n[1]>>>16,i=65535&n[1],a=e[0]>>>16,c=65535&e[0],u=e[1]>>>16,l=0,s=0,d=0,f=0;d+=(f+=i+(65535&e[1]))>>>16,f&=65535,s+=(d+=o+u)>>>16,d&=65535,l+=(s+=r+c)>>>16,s&=65535,l+=t+a,l&=65535,n[0]=l<<16|s,n[1]=d<<16|f}function h(n,e){var t=n[0]>>>16,r=65535&n[0],o=n[1]>>>16,i=65535&n[1],a=e[0]>>>16,c=65535&e[0],u=e[1]>>>16,l=65535&e[1],s=0,d=0,f=0,m=0;f+=(m+=i*l)>>>16,m&=65535,d+=(f+=o*l)>>>16,f&=65535,d+=(f+=i*u)>>>16,f&=65535,s+=(d+=r*l)>>>16,d&=65535,s+=(d+=o*u)>>>16,d&=65535,s+=(d+=i*c)>>>16,d&=65535,s+=t*l+r*u+o*c+i*a,s&=65535,n[0]=s<<16|d,n[1]=f<<16|m}function p(n,e){var t=n[0];32===(e%=64)?(n[0]=n[1],n[1]=t):e<32?(n[0]=t<>>32-e,n[1]=n[1]<>>32-e):(e-=32,n[0]=n[1]<>>32-e,n[1]=t<>>32-e)}function b(n,e){0!==(e%=64)&&(e<32?(n[0]=n[1]>>>32-e,n[1]=n[1]<>>1];y(n,e),h(n,g),e[1]=n[0]>>>1,y(n,e),h(n,w),e[1]=n[0]>>>1,y(n,e)}var k=[2277735313,289559509],V=[1291169091,658871167],S=[0,5],W=[0,1390208809],x=[0,944331445];function Z(n,e){var t=function(n){for(var e=new Uint8Array(n.length),t=0;t127)return(new TextEncoder).encode(n);e[t]=r}return e}(n);e=e||0;var r,o=[0,t.length],i=o[1]%16,a=o[1]-i,c=[0,e],u=[0,e],l=[0,0],s=[0,0];for(r=0;r>>0).toString(16)).slice(-8)+("00000000"+(c[1]>>>0).toString(16)).slice(-8)+("00000000"+(u[0]>>>0).toString(16)).slice(-8)+("00000000"+(u[1]>>>0).toString(16)).slice(-8)}function R(n){return"function"!=typeof n}function M(n,r,o,i){var l=Object.keys(n).filter((function(n){return!function(n,e){for(var t=0,r=n.length;t=4}function I(){var n=window,e=navigator;return f(["msWriteProfilerMark"in n,"MSStream"in n,"msLaunchUri"in e,"msSaveBlob"in e])>=3&&!F()}function Y(){var n=window,e=navigator;return f(["webkitPersistentStorage"in e,"webkitTemporaryStorage"in e,0===(e.vendor||"").indexOf("Google"),"webkitResolveLocalFileSystemURL"in n,"BatteryManager"in n,"webkitMediaStream"in n,"webkitSpeechGrammar"in n])>=5}function j(){var n=window;return f(["ApplePayError"in n,"CSSPrimitiveValue"in n,"Counter"in n,0===navigator.vendor.indexOf("Apple"),"RGBColor"in n,"WebKitMediaKeys"in n])>=4}function X(){var n=window,e=n.HTMLElement,t=n.Document;return f(["safari"in n,!("ongestureend"in n),!("TouchEvent"in n),!("orientation"in n),e&&!("autocapitalize"in e.prototype),t&&"pointerLockElement"in t.prototype])>=4}function C(){var n,e=window;return n=e.print,/^function\s.*?\{\s*\[native code]\s*}$/.test(String(n))&&"[object WebPageNamespace]"===String(e.browser)}function P(){var n,e,t=window;return f(["buildID"in navigator,"MozAppearance"in(null!==(e=null===(n=document.documentElement)||void 0===n?void 0:n.style)&&void 0!==e?e:{}),"onmozfullscreenchange"in t,"mozInnerScreenX"in t,"CSSMozDocumentRule"in t,"CanvasCaptureMediaStream"in t])>=4}function E(){var n=window,e=navigator,t=n.CSS,r=n.HTMLButtonElement;return f([!("getStorageUpdates"in e),r&&"popover"in r.prototype,"CSSCounterStyleRule"in n,t.supports("font-size-adjust: ex-height 0.5"),t.supports("text-transform: full-width")])>=4}function H(){var n=document;return n.fullscreenElement||n.msFullscreenElement||n.mozFullScreenElement||n.webkitFullscreenElement||null}function A(){var n=Y(),e=P(),t=window,r=navigator,o="connection";return n?f([!("SharedWorker"in t),r[o]&&"ontypechange"in r[o],!("sinkId"in new Audio)])>=2:!!e&&f(["onorientationchange"in t,"orientation"in t,/android/i.test(r.appVersion)])>=2}function N(){var n=navigator,e=window,t=Audio.prototype,r=e.visualViewport;return f(["srLatency"in t,"srChannelCount"in t,"devicePosture"in n,r&&"segments"in r,"getTextInformation"in Image.prototype])>=3}function J(){var n=window,e=n.OfflineAudioContext||n.webkitOfflineAudioContext;if(!e)return-2;if(j()&&!X()&&!function(){var n=window;return f(["DOMRectList"in n,"RTCPeerConnectionIceEvent"in n,"SVGGeometryElement"in n,"ontransitioncancel"in n])>=3}())return-1;var t=new e(1,5e3,44100),r=t.createOscillator();r.type="triangle",r.frequency.value=1e4;var o=t.createDynamicsCompressor();o.threshold.value=-50,o.knee.value=40,o.ratio.value=12,o.attack.value=0,o.release.value=.25,r.connect(o),o.connect(t.destination),r.start(0);var a=function(n){var e=3,t=500,r=500,o=5e3,a=function(){};return[new Promise((function(c,l){var s=!1,d=0,f=0;n.oncomplete=function(n){return c(n.renderedBuffer)};var m=function(){setTimeout((function(){return l(T("timeout"))}),Math.min(r,f+o-Date.now()))},v=function(){try{var r=n.startRendering();switch(i(r)&&u(r),n.state){case"running":f=Date.now(),s&&m();break;case"suspended":document.hidden||d++,s&&d>=e?l(T("suspended")):setTimeout(v,t)}}catch(o){l(o)}};v(),a=function(){s||(s=!0,f>0&&m())}})),a]}(t),c=a[0],l=a[1],s=u(c.then((function(n){return function(n){for(var e=0,t=0;t.6*t.length}))).sort(),[2,i]}var c}))}))},fontPreferences:function(){return function(n,e){void 0===e&&(e=4e3);return D((function(t,o){var i=o.document,a=i.body,c=a.style;c.width="".concat(e,"px"),c.webkitTextSizeAdjust=c.textSizeAdjust="none",Y()?a.style.zoom="".concat(1/o.devicePixelRatio):j()&&(a.style.zoom="reset");var u=i.createElement("div");return u.textContent=r([],Array(e/20<<0),!0).map((function(){return"word"})).join(" "),a.appendChild(u),n(i,a)}),'')}((function(n,e){for(var t={},r={},o=0,i=Object.keys(vn);o=3)?-4:J();var n,e},screenFrame:function(){var n=this;if(j()&&E()&&C())return function(){return Promise.resolve(void 0)};var r=nn();return function(){return e(n,void 0,void 0,(function(){var n,e;return t(this,(function(t){switch(t.label){case 0:return[4,r()];case 1:return n=t.sent(),[2,[(e=function(n){return null===n?null:m(n,10)})(n[0]),e(n[1]),e(n[2]),e(n[3])]]}}))}))}},canvas:function(){return U(j()&&E()&&C())},osCpu:function(){return navigator.oscpu},languages:function(){var n,e=navigator,t=[],r=e.language||e.userLanguage||e.browserLanguage||e.systemLanguage;if(void 0!==r&&t.push([r]),Array.isArray(e.languages))Y()&&f([!("MediaSettingsRange"in(n=window)),"RTCEncodedAudioFrame"in n,""+n.Intl=="[object Intl]",""+n.Reflect=="[object Reflect]"])>=3||t.push(e.languages);else if("string"==typeof e.languages){var o=e.languages;o&&t.push(o.split(","))}return t},colorDepth:function(){return window.screen.colorDepth},deviceMemory:function(){return d(s(navigator.deviceMemory),void 0)},screenResolution:function(){if(!(j()&&E()&&C()))return K()},hardwareConcurrency:function(){return d(l(navigator.hardwareConcurrency),void 0)},timezone:function(){var n,e=null===(n=window.Intl)||void 0===n?void 0:n.DateTimeFormat;if(e){var t=(new e).resolvedOptions().timeZone;if(t)return t}var r,o=(r=(new Date).getFullYear(),-Math.max(s(new Date(r,0,1).getTimezoneOffset()),s(new Date(r,6,1).getTimezoneOffset())));return"UTC".concat(o>=0?"+":"").concat(o)},sessionStorage:function(){try{return!!window.sessionStorage}catch(n){return!0}},localStorage:function(){try{return!!window.localStorage}catch(n){return!0}},indexedDB:function(){if(!F()&&!I())try{return!!window.indexedDB}catch(n){return!0}},openDatabase:function(){return!!window.openDatabase},cpuClass:function(){return navigator.cpuClass},platform:function(){var n=navigator.platform;return"MacIntel"===n&&j()&&!X()?function(){if("iPad"===navigator.platform)return!0;var n=screen,e=n.width/n.height;return f(["MediaSource"in window,!!Element.prototype.webkitRequestFullscreen,e>.65&&e<1.53])>=2}()?"iPad":"iPhone":n},plugins:function(){var n=navigator.plugins;if(n){for(var e=[],t=0;t .comment-input { + width: 49.5%; + height: 30px; + border: 1px solid #eee; + border-radius: 6px; + margin-bottom: 10px; + padding: 15px; +} + + .submit-btn { background-color: #3498db; color: #fff; diff --git a/home/static/marked.min.js b/home/static/marked.min.js new file mode 100644 index 0000000..717c9fc --- /dev/null +++ b/home/static/marked.min.js @@ -0,0 +1,6 @@ +/** + * marked v15.0.7 - a markdown parser + * Copyright (c) 2011-2025, Christopher Jeffrey. (MIT Licensed) + * https://github.com/markedjs/marked + */ +!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).marked={})}(this,(function(e){"use strict";function t(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}function n(t){e.defaults=t}e.defaults={async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null};const s={exec:()=>null};function r(e,t=""){let n="string"==typeof e?e:e.source;const s={replace:(e,t)=>{let r="string"==typeof t?t:t.source;return r=r.replace(i.caret,"$1"),n=n.replace(e,r),s},getRegex:()=>new RegExp(n,t)};return s}const i={codeRemoveIndent:/^(?: {1,4}| {0,3}\t)/gm,outputLinkReplace:/\\([\[\]])/g,indentCodeCompensation:/^(\s+)(?:```)/,beginningSpace:/^\s+/,endingHash:/#$/,startingSpaceChar:/^ /,endingSpaceChar:/ $/,nonSpaceChar:/[^ ]/,newLineCharGlobal:/\n/g,tabCharGlobal:/\t/g,multipleSpaceGlobal:/\s+/g,blankLine:/^[ \t]*$/,doubleBlankLine:/\n[ \t]*\n[ \t]*$/,blockquoteStart:/^ {0,3}>/,blockquoteSetextReplace:/\n {0,3}((?:=+|-+) *)(?=\n|$)/g,blockquoteSetextReplace2:/^ {0,3}>[ \t]?/gm,listReplaceTabs:/^\t+/,listReplaceNesting:/^ {1,4}(?=( {4})*[^ ])/g,listIsTask:/^\[[ xX]\] /,listReplaceTask:/^\[[ xX]\] +/,anyLine:/\n.*\n/,hrefBrackets:/^<(.*)>$/,tableDelimiter:/[:|]/,tableAlignChars:/^\||\| *$/g,tableRowBlankLine:/\n[ \t]*$/,tableAlignRight:/^ *-+: *$/,tableAlignCenter:/^ *:-+: *$/,tableAlignLeft:/^ *:-+ *$/,startATag:/^/i,startPreScriptTag:/^<(pre|code|kbd|script)(\s|>)/i,endPreScriptTag:/^<\/(pre|code|kbd|script)(\s|>)/i,startAngleBracket:/^$/,pedanticHrefTitle:/^([^'"]*[^\s])\s+(['"])(.*)\2/,unicodeAlphaNumeric:/[\p{L}\p{N}]/u,escapeTest:/[&<>"']/,escapeReplace:/[&<>"']/g,escapeTestNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/,escapeReplaceNoEncode:/[<>"']|&(?!(#\d{1,7}|#[Xx][a-fA-F0-9]{1,6}|\w+);)/g,unescapeTest:/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/gi,caret:/(^|[^\[])\^/g,percentDecode:/%25/g,findPipe:/\|/g,splitPipe:/ \|/,slashPipe:/\\\|/g,carriageReturn:/\r\n|\r/g,spaceLine:/^ +$/gm,notSpaceStart:/^\S*/,endingNewline:/\n$/,listItemRegex:e=>new RegExp(`^( {0,3}${e})((?:[\t ][^\\n]*)?(?:\\n|$))`),nextBulletRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:[*+-]|\\d{1,9}[.)])((?:[ \t][^\\n]*)?(?:\\n|$))`),hrRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}((?:- *){3,}|(?:_ *){3,}|(?:\\* *){3,})(?:\\n+|$)`),fencesBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}(?:\`\`\`|~~~)`),headingBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}#`),htmlBeginRegex:e=>new RegExp(`^ {0,${Math.min(3,e-1)}}<(?:[a-z].*>|!--)`,"i")},l=/^ {0,3}((?:-[\t ]*){3,}|(?:_[ \t]*){3,}|(?:\*[ \t]*){3,})(?:\n+|$)/,o=/(?:[*+-]|\d{1,9}[.)])/,a=/^(?!bull |blockCode|fences|blockquote|heading|html|table)((?:.|\n(?!\s*?\n|bull |blockCode|fences|blockquote|heading|html|table))+?)\n {0,3}(=+|-+) *(?:\n+|$)/,c=r(a).replace(/bull/g,o).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/\|table/g,"").getRegex(),h=r(a).replace(/bull/g,o).replace(/blockCode/g,/(?: {4}| {0,3}\t)/).replace(/fences/g,/ {0,3}(?:`{3,}|~{3,})/).replace(/blockquote/g,/ {0,3}>/).replace(/heading/g,/ {0,3}#{1,6}/).replace(/html/g,/ {0,3}<[^\n>]+>\n/).replace(/table/g,/ {0,3}\|?(?:[:\- ]*\|)+[\:\- ]*\n/).getRegex(),p=/^([^\n]+(?:\n(?!hr|heading|lheading|blockquote|fences|list|html|table| +\n)[^\n]+)*)/,u=/(?!\s*\])(?:\\.|[^\[\]\\])+/,g=r(/^ {0,3}\[(label)\]: *(?:\n[ \t]*)?([^<\s][^\s]*|<.*?>)(?:(?: +(?:\n[ \t]*)?| *\n[ \t]*)(title))? *(?:\n+|$)/).replace("label",u).replace("title",/(?:"(?:\\"?|[^"\\])*"|'[^'\n]*(?:\n[^'\n]+)*\n?'|\([^()]*\))/).getRegex(),k=r(/^( {0,3}bull)([ \t][^\n]+?)?(?:\n|$)/).replace(/bull/g,o).getRegex(),d="address|article|aside|base|basefont|blockquote|body|caption|center|col|colgroup|dd|details|dialog|dir|div|dl|dt|fieldset|figcaption|figure|footer|form|frame|frameset|h[1-6]|head|header|hr|html|iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|option|p|param|search|section|summary|table|tbody|td|tfoot|th|thead|title|tr|track|ul",f=/|$))/,x=r("^ {0,3}(?:<(script|pre|style|textarea)[\\s>][\\s\\S]*?(?:[^\\n]*\\n+|$)|comment[^\\n]*(\\n+|$)|<\\?[\\s\\S]*?(?:\\?>\\n*|$)|\\n*|$)|\\n*|$)|)[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|<(?!script|pre|style|textarea)([a-z][\\w-]*)(?:attribute)*? */?>(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$)|(?=[ \\t]*(?:\\n|$))[\\s\\S]*?(?:(?:\\n[ \t]*)+\\n|$))","i").replace("comment",f).replace("tag",d).replace("attribute",/ +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/).getRegex(),b=r(p).replace("hr",l).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("|table","").replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",d).getRegex(),w={blockquote:r(/^( {0,3}> ?(paragraph|[^\n]*)(?:\n|$))+/).replace("paragraph",b).getRegex(),code:/^((?: {4}| {0,3}\t)[^\n]+(?:\n(?:[ \t]*(?:\n|$))*)?)+/,def:g,fences:/^ {0,3}(`{3,}(?=[^`\n]*(?:\n|$))|~{3,})([^\n]*)(?:\n|$)(?:|([\s\S]*?)(?:\n|$))(?: {0,3}\1[~`]* *(?=\n|$)|$)/,heading:/^ {0,3}(#{1,6})(?=\s|$)(.*)(?:\n+|$)/,hr:l,html:x,lheading:c,list:k,newline:/^(?:[ \t]*(?:\n|$))+/,paragraph:b,table:s,text:/^[^\n]+/},m=r("^ *([^\\n ].*)\\n {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)").replace("hr",l).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("blockquote"," {0,3}>").replace("code","(?: {4}| {0,3}\t)[^\\n]").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",d).getRegex(),y={...w,lheading:h,table:m,paragraph:r(p).replace("hr",l).replace("heading"," {0,3}#{1,6}(?:\\s|$)").replace("|lheading","").replace("table",m).replace("blockquote"," {0,3}>").replace("fences"," {0,3}(?:`{3,}(?=[^`\\n]*\\n)|~{3,})[^\\n]*\\n").replace("list"," {0,3}(?:[*+-]|1[.)]) ").replace("html",")|<(?:script|pre|style|textarea|!--)").replace("tag",d).getRegex()},$={...w,html:r("^ *(?:comment *(?:\\n|\\s*$)|<(tag)[\\s\\S]+? *(?:\\n{2,}|\\s*$)|\\s]*)*?/?> *(?:\\n{2,}|\\s*$))").replace("comment",f).replace(/tag/g,"(?!(?:a|em|strong|small|s|cite|q|dfn|abbr|data|time|code|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo|span|br|wbr|ins|del|img)\\b)\\w+(?!:|[^\\w\\s@]*@)\\b").getRegex(),def:/^ *\[([^\]]+)\]: *]+)>?(?: +(["(][^\n]+[")]))? *(?:\n+|$)/,heading:/^(#{1,6})(.*)(?:\n+|$)/,fences:s,lheading:/^(.+?)\n {0,3}(=+|-+) *(?:\n+|$)/,paragraph:r(p).replace("hr",l).replace("heading"," *#{1,6} *[^\n]").replace("lheading",c).replace("|table","").replace("blockquote"," {0,3}>").replace("|fences","").replace("|list","").replace("|html","").replace("|tag","").getRegex()},R=/^( {2,}|\\)\n(?!\s*$)/,S=/[\p{P}\p{S}]/u,T=/[\s\p{P}\p{S}]/u,z=/[^\s\p{P}\p{S}]/u,A=r(/^((?![*_])punctSpace)/,"u").replace(/punctSpace/g,T).getRegex(),_=/(?!~)[\p{P}\p{S}]/u,P=/^(?:\*+(?:((?!\*)punct)|[^\s*]))|^_+(?:((?!_)punct)|([^\s_]))/,I=r(P,"u").replace(/punct/g,S).getRegex(),L=r(P,"u").replace(/punct/g,_).getRegex(),B="^[^_*]*?__[^_*]*?\\*[^_*]*?(?=__)|[^*]+(?=[^*])|(?!\\*)punct(\\*+)(?=[\\s]|$)|notPunctSpace(\\*+)(?!\\*)(?=punctSpace|$)|(?!\\*)punctSpace(\\*+)(?=notPunctSpace)|[\\s](\\*+)(?!\\*)(?=punct)|(?!\\*)punct(\\*+)(?!\\*)(?=punct)|notPunctSpace(\\*+)(?=notPunctSpace)",C=r(B,"gu").replace(/notPunctSpace/g,z).replace(/punctSpace/g,T).replace(/punct/g,S).getRegex(),q=r(B,"gu").replace(/notPunctSpace/g,/(?:[^\s\p{P}\p{S}]|~)/u).replace(/punctSpace/g,/(?!~)[\s\p{P}\p{S}]/u).replace(/punct/g,_).getRegex(),E=r("^[^_*]*?\\*\\*[^_*]*?_[^_*]*?(?=\\*\\*)|[^_]+(?=[^_])|(?!_)punct(_+)(?=[\\s]|$)|notPunctSpace(_+)(?!_)(?=punctSpace|$)|(?!_)punctSpace(_+)(?=notPunctSpace)|[\\s](_+)(?!_)(?=punct)|(?!_)punct(_+)(?!_)(?=punct)","gu").replace(/notPunctSpace/g,z).replace(/punctSpace/g,T).replace(/punct/g,S).getRegex(),Z=r(/\\(punct)/,"gu").replace(/punct/g,S).getRegex(),v=r(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/).replace("scheme",/[a-zA-Z][a-zA-Z0-9+.-]{1,31}/).replace("email",/[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/).getRegex(),D=r(f).replace("(?:--\x3e|$)","--\x3e").getRegex(),M=r("^comment|^|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>|^<\\?[\\s\\S]*?\\?>|^|^").replace("comment",D).replace("attribute",/\s+[a-zA-Z:_][\w.:-]*(?:\s*=\s*"[^"]*"|\s*=\s*'[^']*'|\s*=\s*[^\s"'=<>`]+)?/).getRegex(),O=/(?:\[(?:\\.|[^\[\]\\])*\]|\\.|`[^`]*`|[^\[\]\\`])*?/,Q=r(/^!?\[(label)\]\(\s*(href)(?:\s+(title))?\s*\)/).replace("label",O).replace("href",/<(?:\\.|[^\n<>\\])+>|[^\s\x00-\x1f]*/).replace("title",/"(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)/).getRegex(),j=r(/^!?\[(label)\]\[(ref)\]/).replace("label",O).replace("ref",u).getRegex(),N=r(/^!?\[(ref)\](?:\[\])?/).replace("ref",u).getRegex(),G={_backpedal:s,anyPunctuation:Z,autolink:v,blockSkip:/\[[^[\]]*?\]\((?:\\.|[^\\\(\)]|\((?:\\.|[^\\\(\)])*\))*\)|`[^`]*?`|<[^<>]*?>/g,br:R,code:/^(`+)([^`]|[^`][\s\S]*?[^`])\1(?!`)/,del:s,emStrongLDelim:I,emStrongRDelimAst:C,emStrongRDelimUnd:E,escape:/^\\([!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~])/,link:Q,nolink:N,punctuation:A,reflink:j,reflinkSearch:r("reflink|nolink(?!\\()","g").replace("reflink",j).replace("nolink",N).getRegex(),tag:M,text:/^(`+|[^`])(?:(?= {2,}\n)|[\s\S]*?(?:(?=[\\":">",'"':""","'":"'"},V=e=>K[e];function W(e,t){if(t){if(i.escapeTest.test(e))return e.replace(i.escapeReplace,V)}else if(i.escapeTestNoEncode.test(e))return e.replace(i.escapeReplaceNoEncode,V);return e}function Y(e){try{e=encodeURI(e).replace(i.percentDecode,"%")}catch{return null}return e}function ee(e,t){const n=e.replace(i.findPipe,((e,t,n)=>{let s=!1,r=t;for(;--r>=0&&"\\"===n[r];)s=!s;return s?"|":" |"})).split(i.splitPipe);let s=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length0)return{type:"space",raw:t[0]}}code(e){const t=this.rules.block.code.exec(e);if(t){const e=t[0].replace(this.rules.other.codeRemoveIndent,"");return{type:"code",raw:t[0],codeBlockStyle:"indented",text:this.options.pedantic?e:te(e,"\n")}}}fences(e){const t=this.rules.block.fences.exec(e);if(t){const e=t[0],n=function(e,t,n){const s=e.match(n.other.indentCodeCompensation);if(null===s)return t;const r=s[1];return t.split("\n").map((e=>{const t=e.match(n.other.beginningSpace);if(null===t)return e;const[s]=t;return s.length>=r.length?e.slice(r.length):e})).join("\n")}(e,t[3]||"",this.rules);return{type:"code",raw:e,lang:t[2]?t[2].trim().replace(this.rules.inline.anyPunctuation,"$1"):t[2],text:n}}}heading(e){const t=this.rules.block.heading.exec(e);if(t){let e=t[2].trim();if(this.rules.other.endingHash.test(e)){const t=te(e,"#");this.options.pedantic?e=t.trim():t&&!this.rules.other.endingSpaceChar.test(t)||(e=t.trim())}return{type:"heading",raw:t[0],depth:t[1].length,text:e,tokens:this.lexer.inline(e)}}}hr(e){const t=this.rules.block.hr.exec(e);if(t)return{type:"hr",raw:te(t[0],"\n")}}blockquote(e){const t=this.rules.block.blockquote.exec(e);if(t){let e=te(t[0],"\n").split("\n"),n="",s="";const r=[];for(;e.length>0;){let t=!1;const i=[];let l;for(l=0;l1,r={type:"list",raw:"",ordered:s,start:s?+n.slice(0,-1):"",loose:!1,items:[]};n=s?`\\d{1,9}\\${n.slice(-1)}`:`\\${n}`,this.options.pedantic&&(n=s?n:"[*+-]");const i=this.rules.other.listItemRegex(n);let l=!1;for(;e;){let n=!1,s="",o="";if(!(t=i.exec(e)))break;if(this.rules.block.hr.test(e))break;s=t[0],e=e.substring(s.length);let a=t[2].split("\n",1)[0].replace(this.rules.other.listReplaceTabs,(e=>" ".repeat(3*e.length))),c=e.split("\n",1)[0],h=!a.trim(),p=0;if(this.options.pedantic?(p=2,o=a.trimStart()):h?p=t[1].length+1:(p=t[2].search(this.rules.other.nonSpaceChar),p=p>4?1:p,o=a.slice(p),p+=t[1].length),h&&this.rules.other.blankLine.test(c)&&(s+=c+"\n",e=e.substring(c.length+1),n=!0),!n){const t=this.rules.other.nextBulletRegex(p),n=this.rules.other.hrRegex(p),r=this.rules.other.fencesBeginRegex(p),i=this.rules.other.headingBeginRegex(p),l=this.rules.other.htmlBeginRegex(p);for(;e;){const u=e.split("\n",1)[0];let g;if(c=u,this.options.pedantic?(c=c.replace(this.rules.other.listReplaceNesting," "),g=c):g=c.replace(this.rules.other.tabCharGlobal," "),r.test(c))break;if(i.test(c))break;if(l.test(c))break;if(t.test(c))break;if(n.test(c))break;if(g.search(this.rules.other.nonSpaceChar)>=p||!c.trim())o+="\n"+g.slice(p);else{if(h)break;if(a.replace(this.rules.other.tabCharGlobal," ").search(this.rules.other.nonSpaceChar)>=4)break;if(r.test(a))break;if(i.test(a))break;if(n.test(a))break;o+="\n"+c}h||c.trim()||(h=!0),s+=u+"\n",e=e.substring(u.length+1),a=g.slice(p)}}r.loose||(l?r.loose=!0:this.rules.other.doubleBlankLine.test(s)&&(l=!0));let u,g=null;this.options.gfm&&(g=this.rules.other.listIsTask.exec(o),g&&(u="[ ] "!==g[0],o=o.replace(this.rules.other.listReplaceTask,""))),r.items.push({type:"list_item",raw:s,task:!!g,checked:u,loose:!1,text:o,tokens:[]}),r.raw+=s}const o=r.items.at(-1);if(!o)return;o.raw=o.raw.trimEnd(),o.text=o.text.trimEnd(),r.raw=r.raw.trimEnd();for(let e=0;e"space"===e.type)),n=t.length>0&&t.some((e=>this.rules.other.anyLine.test(e.raw)));r.loose=n}if(r.loose)for(let e=0;e({text:e,tokens:this.lexer.inline(e),header:!1,align:i.align[t]}))));return i}}lheading(e){const t=this.rules.block.lheading.exec(e);if(t)return{type:"heading",raw:t[0],depth:"="===t[2].charAt(0)?1:2,text:t[1],tokens:this.lexer.inline(t[1])}}paragraph(e){const t=this.rules.block.paragraph.exec(e);if(t){const e="\n"===t[1].charAt(t[1].length-1)?t[1].slice(0,-1):t[1];return{type:"paragraph",raw:t[0],text:e,tokens:this.lexer.inline(e)}}}text(e){const t=this.rules.block.text.exec(e);if(t)return{type:"text",raw:t[0],text:t[0],tokens:this.lexer.inline(t[0])}}escape(e){const t=this.rules.inline.escape.exec(e);if(t)return{type:"escape",raw:t[0],text:t[1]}}tag(e){const t=this.rules.inline.tag.exec(e);if(t)return!this.lexer.state.inLink&&this.rules.other.startATag.test(t[0])?this.lexer.state.inLink=!0:this.lexer.state.inLink&&this.rules.other.endATag.test(t[0])&&(this.lexer.state.inLink=!1),!this.lexer.state.inRawBlock&&this.rules.other.startPreScriptTag.test(t[0])?this.lexer.state.inRawBlock=!0:this.lexer.state.inRawBlock&&this.rules.other.endPreScriptTag.test(t[0])&&(this.lexer.state.inRawBlock=!1),{type:"html",raw:t[0],inLink:this.lexer.state.inLink,inRawBlock:this.lexer.state.inRawBlock,block:!1,text:t[0]}}link(e){const t=this.rules.inline.link.exec(e);if(t){const e=t[2].trim();if(!this.options.pedantic&&this.rules.other.startAngleBracket.test(e)){if(!this.rules.other.endAngleBracket.test(e))return;const t=te(e.slice(0,-1),"\\");if((e.length-t.length)%2==0)return}else{const e=function(e,t){if(-1===e.indexOf(t[1]))return-1;let n=0;for(let s=0;s-1){const n=(0===t[0].indexOf("!")?5:4)+t[1].length+e;t[2]=t[2].substring(0,e),t[0]=t[0].substring(0,n).trim(),t[3]=""}}let n=t[2],s="";if(this.options.pedantic){const e=this.rules.other.pedanticHrefTitle.exec(n);e&&(n=e[1],s=e[3])}else s=t[3]?t[3].slice(1,-1):"";return n=n.trim(),this.rules.other.startAngleBracket.test(n)&&(n=this.options.pedantic&&!this.rules.other.endAngleBracket.test(e)?n.slice(1):n.slice(1,-1)),ne(t,{href:n?n.replace(this.rules.inline.anyPunctuation,"$1"):n,title:s?s.replace(this.rules.inline.anyPunctuation,"$1"):s},t[0],this.lexer,this.rules)}}reflink(e,t){let n;if((n=this.rules.inline.reflink.exec(e))||(n=this.rules.inline.nolink.exec(e))){const e=t[(n[2]||n[1]).replace(this.rules.other.multipleSpaceGlobal," ").toLowerCase()];if(!e){const e=n[0].charAt(0);return{type:"text",raw:e,text:e}}return ne(n,e,n[0],this.lexer,this.rules)}}emStrong(e,t,n=""){let s=this.rules.inline.emStrongLDelim.exec(e);if(!s)return;if(s[3]&&n.match(this.rules.other.unicodeAlphaNumeric))return;if(!(s[1]||s[2]||"")||!n||this.rules.inline.punctuation.exec(n)){const n=[...s[0]].length-1;let r,i,l=n,o=0;const a="*"===s[0][0]?this.rules.inline.emStrongRDelimAst:this.rules.inline.emStrongRDelimUnd;for(a.lastIndex=0,t=t.slice(-1*e.length+n);null!=(s=a.exec(t));){if(r=s[1]||s[2]||s[3]||s[4]||s[5]||s[6],!r)continue;if(i=[...r].length,s[3]||s[4]){l+=i;continue}if((s[5]||s[6])&&n%3&&!((n+i)%3)){o+=i;continue}if(l-=i,l>0)continue;i=Math.min(i,i+l+o);const t=[...s[0]][0].length,a=e.slice(0,n+s.index+t+i);if(Math.min(n,i)%2){const e=a.slice(1,-1);return{type:"em",raw:a,text:e,tokens:this.lexer.inlineTokens(e)}}const c=a.slice(2,-2);return{type:"strong",raw:a,text:c,tokens:this.lexer.inlineTokens(c)}}}}codespan(e){const t=this.rules.inline.code.exec(e);if(t){let e=t[2].replace(this.rules.other.newLineCharGlobal," ");const n=this.rules.other.nonSpaceChar.test(e),s=this.rules.other.startingSpaceChar.test(e)&&this.rules.other.endingSpaceChar.test(e);return n&&s&&(e=e.substring(1,e.length-1)),{type:"codespan",raw:t[0],text:e}}}br(e){const t=this.rules.inline.br.exec(e);if(t)return{type:"br",raw:t[0]}}del(e){const t=this.rules.inline.del.exec(e);if(t)return{type:"del",raw:t[0],text:t[2],tokens:this.lexer.inlineTokens(t[2])}}autolink(e){const t=this.rules.inline.autolink.exec(e);if(t){let e,n;return"@"===t[2]?(e=t[1],n="mailto:"+e):(e=t[1],n=e),{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}url(e){let t;if(t=this.rules.inline.url.exec(e)){let e,n;if("@"===t[2])e=t[0],n="mailto:"+e;else{let s;do{s=t[0],t[0]=this.rules.inline._backpedal.exec(t[0])?.[0]??""}while(s!==t[0]);e=t[0],n="www."===t[1]?"http://"+t[0]:t[0]}return{type:"link",raw:t[0],text:e,href:n,tokens:[{type:"text",raw:e,text:e}]}}}inlineText(e){const t=this.rules.inline.text.exec(e);if(t){const e=this.lexer.state.inRawBlock;return{type:"text",raw:t[0],text:t[0],escaped:e}}}}class re{tokens;options;state;tokenizer;inlineQueue;constructor(t){this.tokens=[],this.tokens.links=Object.create(null),this.options=t||e.defaults,this.options.tokenizer=this.options.tokenizer||new se,this.tokenizer=this.options.tokenizer,this.tokenizer.options=this.options,this.tokenizer.lexer=this,this.inlineQueue=[],this.state={inLink:!1,inRawBlock:!1,top:!0};const n={other:i,block:U.normal,inline:J.normal};this.options.pedantic?(n.block=U.pedantic,n.inline=J.pedantic):this.options.gfm&&(n.block=U.gfm,this.options.breaks?n.inline=J.breaks:n.inline=J.gfm),this.tokenizer.rules=n}static get rules(){return{block:U,inline:J}}static lex(e,t){return new re(t).lex(e)}static lexInline(e,t){return new re(t).inlineTokens(e)}lex(e){e=e.replace(i.carriageReturn,"\n"),this.blockTokens(e,this.tokens);for(let e=0;e!!(s=n.call({lexer:this},e,t))&&(e=e.substring(s.raw.length),t.push(s),!0))))continue;if(s=this.tokenizer.space(e)){e=e.substring(s.raw.length);const n=t.at(-1);1===s.raw.length&&void 0!==n?n.raw+="\n":t.push(s);continue}if(s=this.tokenizer.code(e)){e=e.substring(s.raw.length);const n=t.at(-1);"paragraph"===n?.type||"text"===n?.type?(n.raw+="\n"+s.raw,n.text+="\n"+s.text,this.inlineQueue.at(-1).src=n.text):t.push(s);continue}if(s=this.tokenizer.fences(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.heading(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.hr(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.blockquote(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.list(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.html(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.def(e)){e=e.substring(s.raw.length);const n=t.at(-1);"paragraph"===n?.type||"text"===n?.type?(n.raw+="\n"+s.raw,n.text+="\n"+s.raw,this.inlineQueue.at(-1).src=n.text):this.tokens.links[s.tag]||(this.tokens.links[s.tag]={href:s.href,title:s.title});continue}if(s=this.tokenizer.table(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.lheading(e)){e=e.substring(s.raw.length),t.push(s);continue}let r=e;if(this.options.extensions?.startBlock){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startBlock.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(r=e.substring(0,t+1))}if(this.state.top&&(s=this.tokenizer.paragraph(r))){const i=t.at(-1);n&&"paragraph"===i?.type?(i.raw+="\n"+s.raw,i.text+="\n"+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=i.text):t.push(s),n=r.length!==e.length,e=e.substring(s.raw.length)}else if(s=this.tokenizer.text(e)){e=e.substring(s.raw.length);const n=t.at(-1);"text"===n?.type?(n.raw+="\n"+s.raw,n.text+="\n"+s.text,this.inlineQueue.pop(),this.inlineQueue.at(-1).src=n.text):t.push(s)}else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return this.state.top=!0,t}inline(e,t=[]){return this.inlineQueue.push({src:e,tokens:t}),t}inlineTokens(e,t=[]){let n=e,s=null;if(this.tokens.links){const e=Object.keys(this.tokens.links);if(e.length>0)for(;null!=(s=this.tokenizer.rules.inline.reflinkSearch.exec(n));)e.includes(s[0].slice(s[0].lastIndexOf("[")+1,-1))&&(n=n.slice(0,s.index)+"["+"a".repeat(s[0].length-2)+"]"+n.slice(this.tokenizer.rules.inline.reflinkSearch.lastIndex))}for(;null!=(s=this.tokenizer.rules.inline.blockSkip.exec(n));)n=n.slice(0,s.index)+"["+"a".repeat(s[0].length-2)+"]"+n.slice(this.tokenizer.rules.inline.blockSkip.lastIndex);for(;null!=(s=this.tokenizer.rules.inline.anyPunctuation.exec(n));)n=n.slice(0,s.index)+"++"+n.slice(this.tokenizer.rules.inline.anyPunctuation.lastIndex);let r=!1,i="";for(;e;){let s;if(r||(i=""),r=!1,this.options.extensions?.inline?.some((n=>!!(s=n.call({lexer:this},e,t))&&(e=e.substring(s.raw.length),t.push(s),!0))))continue;if(s=this.tokenizer.escape(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.tag(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.link(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.reflink(e,this.tokens.links)){e=e.substring(s.raw.length);const n=t.at(-1);"text"===s.type&&"text"===n?.type?(n.raw+=s.raw,n.text+=s.text):t.push(s);continue}if(s=this.tokenizer.emStrong(e,n,i)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.codespan(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.br(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.del(e)){e=e.substring(s.raw.length),t.push(s);continue}if(s=this.tokenizer.autolink(e)){e=e.substring(s.raw.length),t.push(s);continue}if(!this.state.inLink&&(s=this.tokenizer.url(e))){e=e.substring(s.raw.length),t.push(s);continue}let l=e;if(this.options.extensions?.startInline){let t=1/0;const n=e.slice(1);let s;this.options.extensions.startInline.forEach((e=>{s=e.call({lexer:this},n),"number"==typeof s&&s>=0&&(t=Math.min(t,s))})),t<1/0&&t>=0&&(l=e.substring(0,t+1))}if(s=this.tokenizer.inlineText(l)){e=e.substring(s.raw.length),"_"!==s.raw.slice(-1)&&(i=s.raw.slice(-1)),r=!0;const n=t.at(-1);"text"===n?.type?(n.raw+=s.raw,n.text+=s.text):t.push(s)}else if(e){const t="Infinite loop on byte: "+e.charCodeAt(0);if(this.options.silent){console.error(t);break}throw new Error(t)}}return t}}class ie{options;parser;constructor(t){this.options=t||e.defaults}space(e){return""}code({text:e,lang:t,escaped:n}){const s=(t||"").match(i.notSpaceStart)?.[0],r=e.replace(i.endingNewline,"")+"\n";return s?'
'+(n?r:W(r,!0))+"
\n":"
"+(n?r:W(r,!0))+"
\n"}blockquote({tokens:e}){return`
\n${this.parser.parse(e)}
\n`}html({text:e}){return e}heading({tokens:e,depth:t}){return`${this.parser.parseInline(e)}\n`}hr(e){return"
\n"}list(e){const t=e.ordered,n=e.start;let s="";for(let t=0;t\n"+s+"\n"}listitem(e){let t="";if(e.task){const n=this.checkbox({checked:!!e.checked});e.loose?"paragraph"===e.tokens[0]?.type?(e.tokens[0].text=n+" "+e.tokens[0].text,e.tokens[0].tokens&&e.tokens[0].tokens.length>0&&"text"===e.tokens[0].tokens[0].type&&(e.tokens[0].tokens[0].text=n+" "+W(e.tokens[0].tokens[0].text),e.tokens[0].tokens[0].escaped=!0)):e.tokens.unshift({type:"text",raw:n+" ",text:n+" ",escaped:!0}):t+=n+" "}return t+=this.parser.parse(e.tokens,!!e.loose),`
  • ${t}
  • \n`}checkbox({checked:e}){return"'}paragraph({tokens:e}){return`

    ${this.parser.parseInline(e)}

    \n`}table(e){let t="",n="";for(let t=0;t${s}`),"\n\n"+t+"\n"+s+"
    \n"}tablerow({text:e}){return`\n${e}\n`}tablecell(e){const t=this.parser.parseInline(e.tokens),n=e.header?"th":"td";return(e.align?`<${n} align="${e.align}">`:`<${n}>`)+t+`\n`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${W(e,!0)}`}br(e){return"
    "}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:n}){const s=this.parser.parseInline(n),r=Y(e);if(null===r)return s;let i='
    ",i}image({href:e,title:t,text:n}){const s=Y(e);if(null===s)return W(n);let r=`${n}{const r=e[s].flat(1/0);n=n.concat(this.walkTokens(r,t))})):e.tokens&&(n=n.concat(this.walkTokens(e.tokens,t)))}}return n}use(...e){const t=this.defaults.extensions||{renderers:{},childTokens:{}};return e.forEach((e=>{const n={...e};if(n.async=this.defaults.async||n.async||!1,e.extensions&&(e.extensions.forEach((e=>{if(!e.name)throw new Error("extension name required");if("renderer"in e){const n=t.renderers[e.name];t.renderers[e.name]=n?function(...t){let s=e.renderer.apply(this,t);return!1===s&&(s=n.apply(this,t)),s}:e.renderer}if("tokenizer"in e){if(!e.level||"block"!==e.level&&"inline"!==e.level)throw new Error("extension level must be 'block' or 'inline'");const n=t[e.level];n?n.unshift(e.tokenizer):t[e.level]=[e.tokenizer],e.start&&("block"===e.level?t.startBlock?t.startBlock.push(e.start):t.startBlock=[e.start]:"inline"===e.level&&(t.startInline?t.startInline.push(e.start):t.startInline=[e.start]))}"childTokens"in e&&e.childTokens&&(t.childTokens[e.name]=e.childTokens)})),n.extensions=t),e.renderer){const t=this.defaults.renderer||new ie(this.defaults);for(const n in e.renderer){if(!(n in t))throw new Error(`renderer '${n}' does not exist`);if(["options","parser"].includes(n))continue;const s=n,r=e.renderer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n||""}}n.renderer=t}if(e.tokenizer){const t=this.defaults.tokenizer||new se(this.defaults);for(const n in e.tokenizer){if(!(n in t))throw new Error(`tokenizer '${n}' does not exist`);if(["options","rules","lexer"].includes(n))continue;const s=n,r=e.tokenizer[s],i=t[s];t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.tokenizer=t}if(e.hooks){const t=this.defaults.hooks||new ae;for(const n in e.hooks){if(!(n in t))throw new Error(`hook '${n}' does not exist`);if(["options","block"].includes(n))continue;const s=n,r=e.hooks[s],i=t[s];ae.passThroughHooks.has(n)?t[s]=e=>{if(this.defaults.async)return Promise.resolve(r.call(t,e)).then((e=>i.call(t,e)));const n=r.call(t,e);return i.call(t,n)}:t[s]=(...e)=>{let n=r.apply(t,e);return!1===n&&(n=i.apply(t,e)),n}}n.hooks=t}if(e.walkTokens){const t=this.defaults.walkTokens,s=e.walkTokens;n.walkTokens=function(e){let n=[];return n.push(s.call(this,e)),t&&(n=n.concat(t.call(this,e))),n}}this.defaults={...this.defaults,...n}})),this}setOptions(e){return this.defaults={...this.defaults,...e},this}lexer(e,t){return re.lex(e,t??this.defaults)}parser(e,t){return oe.parse(e,t??this.defaults)}parseMarkdown(e){return(t,n)=>{const s={...n},r={...this.defaults,...s},i=this.onError(!!r.silent,!!r.async);if(!0===this.defaults.async&&!1===s.async)return i(new Error("marked(): The async option was set to true by an extension. Remove async: false from the parse options object to return a Promise."));if(null==t)return i(new Error("marked(): input parameter is undefined or null"));if("string"!=typeof t)return i(new Error("marked(): input parameter is of type "+Object.prototype.toString.call(t)+", string expected"));r.hooks&&(r.hooks.options=r,r.hooks.block=e);const l=r.hooks?r.hooks.provideLexer():e?re.lex:re.lexInline,o=r.hooks?r.hooks.provideParser():e?oe.parse:oe.parseInline;if(r.async)return Promise.resolve(r.hooks?r.hooks.preprocess(t):t).then((e=>l(e,r))).then((e=>r.hooks?r.hooks.processAllTokens(e):e)).then((e=>r.walkTokens?Promise.all(this.walkTokens(e,r.walkTokens)).then((()=>e)):e)).then((e=>o(e,r))).then((e=>r.hooks?r.hooks.postprocess(e):e)).catch(i);try{r.hooks&&(t=r.hooks.preprocess(t));let e=l(t,r);r.hooks&&(e=r.hooks.processAllTokens(e)),r.walkTokens&&this.walkTokens(e,r.walkTokens);let n=o(e,r);return r.hooks&&(n=r.hooks.postprocess(n)),n}catch(e){return i(e)}}}onError(e,t){return n=>{if(n.message+="\nPlease report this to https://github.com/markedjs/marked.",e){const e="

    An error occurred:

    "+W(n.message+"",!0)+"
    ";return t?Promise.resolve(e):e}if(t)return Promise.reject(n);throw n}}}const he=new ce;function pe(e,t){return he.parse(e,t)}pe.options=pe.setOptions=function(e){return he.setOptions(e),pe.defaults=he.defaults,n(pe.defaults),pe},pe.getDefaults=t,pe.defaults=e.defaults,pe.use=function(...e){return he.use(...e),pe.defaults=he.defaults,n(pe.defaults),pe},pe.walkTokens=function(e,t){return he.walkTokens(e,t)},pe.parseInline=he.parseInline,pe.Parser=oe,pe.parser=oe.parse,pe.Renderer=ie,pe.TextRenderer=le,pe.Lexer=re,pe.lexer=re.lex,pe.Tokenizer=se,pe.Hooks=ae,pe.parse=pe;const ue=pe.options,ge=pe.setOptions,ke=pe.use,de=pe.walkTokens,fe=pe.parseInline,xe=pe,be=oe.parse,we=re.lex;e.Hooks=ae,e.Lexer=re,e.Marked=ce,e.Parser=oe,e.Renderer=ie,e.TextRenderer=le,e.Tokenizer=se,e.getDefaults=t,e.lexer=we,e.marked=pe,e.options=ue,e.parse=xe,e.parseInline=fe,e.parser=be,e.setOptions=ge,e.use=ke,e.walkTokens=de})); \ No newline at end of file diff --git a/home/templates/archives.html b/home/templates/archives.html index 5fe23c3..0175edd 100644 --- a/home/templates/archives.html +++ b/home/templates/archives.html @@ -2,8 +2,12 @@ {% load static %} {#文章详情#} +{% block sitename %} + {{ title }}-我的个人博客 +{% endblock %} {% block stylesheethref %} - + + {% endblock %} {% block content %}
    @@ -13,24 +17,21 @@

    {{ title }}

    +
    {{ title }}封面图
    @@ -39,6 +40,145 @@
    {{ content|safe }}
    +
    +
    +
    + + + +
    +

    {{ stat }}

    +
    +
    + +
    + +
    +

    评论

    +
    +
    + {% csrf_token %} +
    + + + +
    + +
    +
    +
    + {% for comment in comments %} +
    +
    + 用户头像 +
    +
    +
    + {{ comment.comment_User }} + {{ comment.comment_Time|timesince }} +
    +

    {{ comment.comment_Content }}

    + {% if comment.replies.all %} +
    + {% for reply in comment.replies.all %} +
    +
    + 用户头像 +
    +
    +
    + {{ reply.user.username }} + {{ reply.created_at|timesince }}前 +
    +

    {{ reply.content }}

    +
    +
    + {% endfor %} +
    + {% endif %} +
    +
    + {% empty %} +
    暂无评论,快来抢沙发吧~
    + {% endfor %} +
    +
    +{% endblock %} + +{% block script %} + const no_active_path = "M512 928c-28.928 0-57.92-12.672-86.624-41.376L106.272 564C68.064 516.352 32 471.328 32 384c0-141.152 114.848-256 256-256 53.088 0 104 16.096 147.296 46.592 14.432 10.176 17.92 30.144 7.712 44.608-10.176 14.432-30.08 17.92-44.608 7.712C366.016 204.064 327.808 192 288 192c-105.888 0-192 86.112-192 192 0 61.408 20.288 90.112 59.168 138.688l315.584 318.816C486.72 857.472 499.616 863.808 512 864c12.704.192 24.928-6.176 41.376-22.624l316.672-319.904C896.064 493.28 928 445.696 928 384c0-105.888-86.112-192-192-192-48.064 0-94.08 17.856-129.536 50.272l-134.08 134.112c-12.512 12.512-32.736 12.512-45.248 0s-12.512-32.736 0-45.248L562.24 196c48.32-44.192 109.664-68 173.76-68 141.152 0 256 114.848 256 256 0 82.368-41.152 144.288-75.68 181.696l-317.568 320.8C569.952 915.328 540.96 928 512 928z" + const active_path = "M736 128c-65.952 0-128.576 25.024-176.384 70.464-4.576 4.32-28.672 28.736-47.328 47.68L464.96 199.04C417.12 153.216 354.272 128 288 128 146.848 128 32 242.848 32 384c0 82.432 41.184 144.288 76.48 182.496l316.896 320.128C450.464 911.68 478.304 928 512 928s61.568-16.32 86.752-41.504l316.736-320 2.208-2.464C955.904 516.384 992 471.392 992 384c0-141.152-114.848-256-256-256z" + const path = document.getElementById('link_svg'); + + const fpPromise = import('{% static "aaa.js" %}').then(FingerprintJS => FingerprintJS.load()); + fpPromise.then(fp => fp.get()).then(result => {localStorage.setItem("uuid",result.visitorId)}); + + document.addEventListener('DOMContentLoaded', function() { + const likeBtn = document.getElementsByClassName('dz')[0]; + if (!likeBtn) return; + fetch(`/api/check_like/{{ id }}/${localStorage.getItem("uuid")}`,{ + method: 'POST', + headers: { + 'X-CSRFToken': '{{ csrf_token }}' + }, + credentials: 'include' + }).then(response => response.json()).then(data => { + if (data.liked) { + path.setAttribute('d', active_path) + likeBtn.classList.add('active'); + } + }); + const dz_icon = document.getElementsByClassName('dz-icon')[0]; + dz_icon.addEventListener('click', function() { + if (likeBtn.classList.contains('active')) { + fetch(`/api/unlink/{{ id }}/${localStorage.getItem("uuid")}`,{ + method: 'POST', + headers: { + 'X-CSRFToken': '{{ csrf_token }}' + }, + credentials: 'include' + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + likeBtn.classList.remove('active'); + path.setAttribute('d', no_active_path) + document.getElementById('likeCount').textContent = data.new_count; + } + }); + } else { + fetch(`/api/link/{{ id }}/${localStorage.getItem("uuid")}`, { + method: 'POST', + headers: { + 'X-CSRFToken': '{{ csrf_token }}' + }, + credentials: 'include' + }) + .then(response => response.json()) + .then(data => { + if (data.success) { + likeBtn.classList.add('active'); + path.setAttribute('d', active_path) + document.getElementById('likeCount').textContent = data.new_count; + } + }); + } + }) + }); + {% endblock %} \ No newline at end of file diff --git a/home/templates/bottom.html b/home/templates/bottom.html index a57515f..2910a96 100644 --- a/home/templates/bottom.html +++ b/home/templates/bottom.html @@ -4,7 +4,9 @@ - 我的个人博客 + {% block sitename %} + 我的个人博客 + {% endblock %} {% block stylesheethref %} {% endblock %} @@ -92,40 +94,8 @@ \ No newline at end of file diff --git a/home/templates/index.html b/home/templates/index.html index e677d9e..07d813a 100644 --- a/home/templates/index.html +++ b/home/templates/index.html @@ -34,4 +34,40 @@ {% endfor %} +{% endblock %} +{% block script %} + document.addEventListener('DOMContentLoaded', function() { + // 搜索功能 + const searchBtn = document.querySelector('.search-box button'); + const searchInput = document.querySelector('.search-box input'); + + searchBtn.addEventListener('click', function() { + const query = searchInput.value.trim(); + if (query) { + alert('搜索: ' + query); + // 实际应用中这里应该是跳转到搜索页面或执行搜索操作 + } + }); + + searchInput.addEventListener('keypress', function(e) { + if (e.key === 'Enter') { + const query = searchInput.value.trim(); + if (query) { + alert('搜索: ' + query); + // 实际应用中这里应该是跳转到搜索页面或执行搜索操作 + } + } + }); + + // 文章卡片交互 + const articleCards = document.querySelectorAll('.article-card'); + articleCards.forEach(card => { + card.addEventListener('click', function(e) { + if (!e.target.closest('.read-more')) { + const id = this.querySelector('.article-id').textContent; + window.open(`/archives/${id}`) + } + }); + }); + }); {% endblock %} \ No newline at end of file diff --git a/home/views.py b/home/views.py index 10a5d31..50f9e9f 100644 --- a/home/views.py +++ b/home/views.py @@ -1,11 +1,12 @@ from django.shortcuts import render, HttpResponse -from . import models +from home.models import * +from comment.models import comment # Create your views here. def index(request): - artchle = models.articles.objects.all() + artchle = Articles.objects.all() + artchles = {"artchles": []} - print(artchle) for i in artchle: a = { "id":i.id, @@ -17,13 +18,19 @@ def index(request): } artchles["artchles"].append(a) return render(request, 'index.html', context=artchles) - # return HttpResponse("Hello, world. You're at the polls index.") def archives(request, id): - i = models.articles.objects.get(id=id) + i = Articles.objects.get(id=id) i.read += 1 i.save() + previous_article = Articles.objects.filter(id__lt=id).order_by('-id').first() + previous_id = previous_article.id if previous_article else id + previous_id_title = previous_article.title if previous_article else "暂无上一篇" + next_article = Articles.objects.filter(id__gt=id).order_by('id').first() + next_id = next_article.id if next_article else id + next_id_title = next_article.title if next_article else "暂无下一篇" + comments = comment.objects.filter(archives_Id=id).order_by("-comment_Time").all() a = { "id": i.id, "title": i.title, @@ -32,6 +39,22 @@ def archives(request, id): "stat": i.stat, "read": i.read, "content": i.content, - "author": i.author + "author": i.author, + "previous":{ + "id": previous_id, + "title": previous_id_title, + },"next":{ + "id": next_id, + "title": next_id_title, + },"comments": [] } + for c in comments: + com = { + "id": c.id, + "comment_Content": c.comment_Content, + "comment_User": c.comment_User, + "comment_Time": c.comment_Time, + "qq": c.qq, + } + a["comments"].append(com) return render(request, 'archives.html', a) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..6b240b0 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,15 @@ +asgiref==3.8.1 +diff-match-patch==20241021 +Django==4.2.23 +django-import-export==4.3.7 +django-mdeditor==0.1.20 +django-simpleui==2025.5.262 +docutils==0.21.2 +importlib_metadata==8.7.0 +Markdown==3.8 +pillow==11.2.1 +Pygments==2.19.1 +sqlparse==0.5.3 +tablib==3.8.0 +typing_extensions==4.14.0 +zipp==3.23.0