diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..b947f47 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,29 @@ +name: release + +on: + workflow_dispatch: + inputs: + version: + description: "Next Version" + required: true + default: "x.y.z" + release_note: + description: "release note" + required: false +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Create Release + id: create_release + uses: actions/create-release@v1 + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + with: + tag_name: v${{ github.event.inputs.version }} + release_name: Release v${{ github.event.inputs.version }} + body: | + ${{ github.event.inputs.release_note }} + draft: false + prerelease: false diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..dfcfd56 --- /dev/null +++ b/.gitignore @@ -0,0 +1,350 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..342ab57 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Takashi_Uno + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..2d000a1 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# d-party Chrome Extension + +d-partyのchrome extension部分 + +## 使用ライブラリ + +- [jQuery-3.6.0](https://jquery.com/) + - +- [Font Awesome](https://fontawesome.com/) +- [Awesome Notifications](https://f3oall.github.io/awesome-notifications/) +- [Knopf.css](https://knopf.dev/) + - + +## 開発 diff --git a/assets/font/MaterialIcons-Regular.ttf b/assets/font/MaterialIcons-Regular.ttf new file mode 100644 index 0000000..7015564 Binary files /dev/null and b/assets/font/MaterialIcons-Regular.ttf differ diff --git a/assets/font/MaterialIcons-Regular.woff b/assets/font/MaterialIcons-Regular.woff new file mode 100644 index 0000000..b648a3e Binary files /dev/null and b/assets/font/MaterialIcons-Regular.woff differ diff --git a/assets/font/MaterialIcons-Regular.woff2 b/assets/font/MaterialIcons-Regular.woff2 new file mode 100644 index 0000000..9fa2112 Binary files /dev/null and b/assets/font/MaterialIcons-Regular.woff2 differ diff --git a/css/library/awesome-notification.css b/css/library/awesome-notification.css new file mode 100644 index 0000000..0e03415 --- /dev/null +++ b/css/library/awesome-notification.css @@ -0,0 +1 @@ +@keyframes awn-fade-in{0%{opacity:0}to{opacity:1}}@keyframes awn-fade-out{0%{opacity:1}to{opacity:0}}@keyframes awn-slide-right{0%{left:100%;opacity:0}to{left:0;opacity:1}}@keyframes awn-slide-left{0%{opacity:0;right:100%}to{opacity:1;right:0}}@keyframes awn-bar{0%{right:100%}to{right:0}}.awn-popup-loading-dots,.awn-popup-loading-dots:after,.awn-popup-loading-dots:before{animation-fill-mode:both;animation:awn-loading-dots 1s ease-in-out infinite;background:#fff;border-radius:50%;height:6px;width:6px}.awn-popup-loading-dots{animation-delay:-.16s;color:#fff;display:inline-block;margin-left:24px;position:relative}.awn-popup-loading-dots:after,.awn-popup-loading-dots:before{content:"";position:absolute;top:0}.awn-popup-loading-dots:before{animation-delay:-.32s;left:-16px}.awn-popup-loading-dots:after{left:16px}@keyframes awn-loading-dots{0%,80%,to{box-shadow:0 0 0 0}40%{box-shadow:0 0 0 2px}}#awn-popup-wrapper{align-items:center;animation-fill-mode:both;animation-name:awn-fade-in;animation-timing-function:ease-out;background:rgba(0,0,0,.7);bottom:0;display:flex;justify-content:center;left:0;opacity:0;position:fixed;right:0;top:0;z-index:99999}#awn-popup-wrapper.awn-hiding{animation-name:awn-fade-out}#awn-popup-wrapper .awn-popup-body{background:#fff;border-radius:6px;font-size:14px;max-width:500px;min-width:320px;padding:24px;position:relative;word-break:break-word}#awn-popup-wrapper .awn-popup-body.awn-popup-confirm{align-items:center;display:flex;flex-direction:column}#awn-popup-wrapper .awn-popup-body.awn-popup-confirm .fa{color:#c26700;font-size:44px}#awn-popup-wrapper .awn-popup-body.awn-popup-async-block{background:transparent;color:#fff;font-size:32px;font-weight:700;text-align:center}#awn-popup-wrapper .awn-popup-title{font-size:14px;font-weight:700;margin-top:8px;text-transform:uppercase}#awn-popup-wrapper .awn-buttons{display:flex;justify-content:space-between;margin-top:24px;width:100%}#awn-popup-wrapper .awn-buttons .awn-btn{border:0;border-radius:4px;color:#fff;font-size:14px;font-weight:700;line-height:32px;transition:background .2s linear;width:45%}#awn-popup-wrapper .awn-buttons-1 .awn-btn{width:100%}#awn-popup-wrapper .awn-buttons .awn-btn-success{background:#40871d}#awn-popup-wrapper .awn-buttons .awn-btn-success:hover{background:#367218}#awn-popup-wrapper .awn-buttons .awn-btn-cancel{background:#1c76a6}#awn-popup-wrapper .awn-buttons .awn-btn-cancel:hover{background:#186690}#awn-toast-container{bottom:24px;box-sizing:border-box;position:fixed;right:24px;z-index:99998}#awn-toast-container.awn-top-left,#awn-toast-container.awn-top-right{bottom:auto;top:24px}#awn-toast-container.awn-top-left .awn-toast:first-child,#awn-toast-container.awn-top-right .awn-toast:first-child{margin-top:16px}#awn-toast-container.awn-bottom-left,#awn-toast-container.awn-top-left{left:24px;right:auto}#awn-toast-container.awn-bottom-left .awn-toast,#awn-toast-container.awn-top-left .awn-toast{animation-name:awn-slide-left;right:100%}#awn-toast-container.awn-bottom-left .awn-toast.awn-hiding,#awn-toast-container.awn-top-left .awn-toast.awn-hiding{right:0}#awn-toast-container.awn-bottom-right .awn-toast,#awn-toast-container.awn-top-right .awn-toast{animation-name:awn-slide-right;left:100%}#awn-toast-container.awn-bottom-right .awn-toast.awn-hiding,#awn-toast-container.awn-top-right .awn-toast.awn-hiding{left:0}.awn-toast{animation-fill-mode:both;animation-timing-function:linear;background:#ebebeb;border-radius:6px;color:gray;cursor:pointer;font-size:14px;margin-top:16px;opacity:0;overflow:hidden;position:relative;width:320px}.awn-toast-content{word-break:break-word}.awn-toast-label{color:gray;display:block;font-size:18px;text-transform:uppercase}.awn-toast-icon{align-items:center;bottom:0;display:flex;justify-content:flex-end;position:absolute;right:16px;top:6px}.awn-toast-icon .fa{color:gray;font-size:44px}.awn-toast-wrapper{border:2px solid #d1d1d1;border-radius:6px;padding:22px 88px 16px 16px}.awn-toast-progress-bar{height:6px;left:0;position:absolute;right:0;top:0}.awn-toast-progress-bar:after{animation-duration:inherit;animation-fill-mode:both;animation-name:awn-bar;animation-timing-function:linear;background:gray;content:" ";height:6px;position:absolute;right:100%;top:0;width:100%}.awn-toast.awn-toast-progress-bar-paused .awn-toast-progress-bar:after{animation-play-state:paused}.awn-toast.awn-hiding{animation-name:awn-fade-out!important}.awn-toast.awn-toast-success{background:#dff8d3;color:#40871d}.awn-toast.awn-toast-success .awn-toast-wrapper{border-color:#a7d590}.awn-toast.awn-toast-success .fa,.awn-toast.awn-toast-success b{color:#40871d}.awn-toast.awn-toast-success .awn-toast-progress-bar:after{background:#40871d}.awn-toast.awn-toast-info{background:#d3ebf8;color:#1c76a6}.awn-toast.awn-toast-info .awn-toast-wrapper{border-color:#9fd3ef}.awn-toast.awn-toast-info .fa,.awn-toast.awn-toast-info b{color:#1c76a6}.awn-toast.awn-toast-info .awn-toast-progress-bar:after{background:#1c76a6}.awn-toast.awn-toast-alert{background:#f8d5d3;color:#a92019}.awn-toast.awn-toast-alert .awn-toast-wrapper{border-color:#f0a29d}.awn-toast.awn-toast-alert .fa,.awn-toast.awn-toast-alert b{color:#a92019}.awn-toast.awn-toast-alert .awn-toast-progress-bar:after{background:#a92019}.awn-toast.awn-toast-warning{background:#ffe7cc;color:#c26700}.awn-toast.awn-toast-warning .awn-toast-wrapper{border-color:#ffc480}.awn-toast.awn-toast-warning .fa,.awn-toast.awn-toast-warning b{color:#c26700}.awn-toast.awn-toast-warning .awn-toast-progress-bar:after{background:#c26700}[class^=awn-]{box-sizing:border-box} \ No newline at end of file diff --git a/css/library/knopf.min.css b/css/library/knopf.min.css new file mode 100644 index 0000000..2385d7f --- /dev/null +++ b/css/library/knopf.min.css @@ -0,0 +1 @@ +html{--knopf-hue:218;--knopf-saturation:77%;--knopf-luminosity:37%;--knopf-white:0 100% 100%;--knopf-font-size-base:1rem;--knopf-font-size-small:calc(var(--knopf-font-size-base) - 0.25rem);--knopf-font-size-large:calc(var(--knopf-font-size-base) + 0.2rem);--knopf-font-size-huge:calc(var(--knopf-font-size-base) + 0.5rem);--knopf-padding-base:.375rem;--knopf-padding-small:calc(var(--knopf-padding-base) - 0.125rem);--knopf-padding-large:calc(var(--knopf-padding-base) + 0.125rem);--knopf-padding-huge:calc(var(--knopf-padding-base) + 0.375rem);--knopf-icon-size-base:1.125rem;--knopf-icon-size-small:calc(var(--knopf-icon-size-base) - 0.25rem);--knopf-icon-size-large:calc(var(--knopf-icon-size-base) + 0.375rem);--knopf-icon-size-huge:calc(var(--knopf-icon-size-base) + 0.625rem);--knopf-font-size:var(--knopf-font-size-base);--knopf-padding:var(--knopf-padding-base);--knopf-icon-size:var(--knopf-icon-size-base);--knopf-border-radius:.33em;--knopf-border-style:solid;--knopf-border-width:1px;--knopf-border-alpha:0;--knopf-font-family:inherit;--knopf-font-weight:500;--knopf-line-height:1.5;--knopf-justify-content:center;--knopf-text-decoration-line:none;--knopf-text-decoration-color:currentColor;--knopf-text-transform:none;--knopf-display:inline-flex;--knopf-flex-direction:row;--knopf-group-direction:row;--knopf-background-image:none;--knopf-box-shadow:none;--knopf-transition:all 150ms ease-in-out}.knopf.knopf{--knopf-color:var(--knopf-hue) var(--knopf-saturation) var(--knopf-luminosity);--knopf-color-light:var(--knopf-hue) var(--knopf-saturation) calc(var(--knopf-luminosity) + 10%);--knopf-color-lighter:var(--knopf-hue) var(--knopf-saturation) calc(var(--knopf-luminosity) + 15%);--knopf-color-dark:var(--knopf-hue) var(--knopf-saturation) calc(var(--knopf-luminosity) - 10%);--knopf-color-darker:var(--knopf-hue) var(--knopf-saturation) calc(var(--knopf-luminosity) - 15%);--knopf-background-alpha:100%;--knopf-background-color:var(--knopf-color);--knopf-border-color:var(--knopf-color);--knopf-gradient-from:var(--knopf-color-light);--knopf-gradient-to:var(--knopf-color-dark);--knopf-text-color:var(--knopf-white);--knopf-hover-background-alpha:100%;--knopf-hover-background-color:var(--knopf-color-dark);--knopf-hover-border-color:var(--knopf-color-dark);--knopf-hover-gradient-from:var(--knopf-color-lighter);--knopf-hover-gradient-to:var(--knopf-color);--knopf-hover-text-color:var(--knopf-white);--knopf-active-background-alpha:100%;--knopf-active-background-color:var(--knopf-color-darker);--knopf-active-border-color:var(--knopf-color-darker);--knopf-active-gradient-from:var(--knopf-color-dark);--knopf-active-gradient-to:var(--knopf-color);--knopf-active-text-color:var(--knopf-white);--knopf-block-padding:var(--knopf-padding);--knopf-inline-padding:calc(var(--knopf-padding) * 2);--knopf-focus-ring:0 0 0 .15em hsl(var(--knopf-color) / 40%);all:unset;-webkit-user-drag:element;align-items:center;background-color:var(--knopf-named-background-color,hsl(var(--knopf-background-color) / var(--knopf-background-alpha)));background-image:var(--knopf-background-image);background-origin:border-box;border-color:hsl(var(--knopf-border-color) / var(--knopf-border-alpha));border-radius:var(--knopf-border-radius);border-style:var(--knopf-border-style);border-width:var(--knopf-border-width);box-shadow:var(--knopf-box-shadow);box-sizing:border-box;color:hsl(var(--knopf-text-color));-webkit-text-fill-color:hsl(var(--knopf-text-color));cursor:pointer;display:var(--knopf-display);flex-direction:var(--knopf-flex-direction);font-family:var(--knopf-font-family);font-size:var(--knopf-font-size,inherit);font-weight:var(--knopf-font-weight);line-height:var(--knopf-line-height);justify-content:var(--knopf-justify-content);padding-block-end:var(--knopf-block-padding);padding-block-start:var(--knopf-block-padding);padding-inline-end:var(--knopf-inline-padding);padding-inline-start:var(--knopf-inline-padding);position:relative;text-decoration-color:var(--knopf-text-decoration-color);text-decoration-line:var(--knopf-text-decoration-line);text-transform:var(--knopf-text-transform);transition:var(--knopf-transition)}.knopf.inverse{--knopf-background-alpha:0;--knopf-text-color:var(--knopf-color);--knopf-hover-background-alpha:100%;--knopf-hover-background-color:var(--knopf-color);--knopf-hover-text-color:var(--knopf-white);--knopf-hover-background-alpha:100%;--knopf-active-background-color:var(--knopf-color-dark);--knopf-active-text-color:var(--knopf-white)}.knopf.flat{--knopf-background-alpha:0;--knopf-text-color:var(--knopf-color);--knopf-hover-background-alpha:15%;--knopf-hover-background-color:var(--knopf-color);--knopf-hover-text-color:var(--knopf-color-dark);--knopf-active-background-alpha:25%;--knopf-active-background-color:var(--knopf-color);--knopf-active-text-color:var(--knopf-color-darker)}.knopf.pale{--knopf-background-alpha:10%;--knopf-background-color:var(--knopf-color);--knopf-text-color:var(--knopf-color);--knopf-hover-background-alpha:20%;--knopf-hover-background-color:var(--knopf-color);--knopf-hover-text-color:var(--knopf-color-dark);--knopf-active-background-alpha:30%;--knopf-active-background-color:var(--knopf-color);--knopf-active-text-color:var(--knopf-color-darker)}.knopf.knopf:disabled,.knopf.disabled{cursor:default;opacity:40%;pointer-events:none}.knopf.knopf:visited{color:hsl(var(--knopf-text-color))}.knopf.knopf:hover,.knopf.knopf:active{--knopf-background-alpha:var(--knopf-hover-background-alpha);--knopf-background-color:var(--knopf-hover-background-color);--knopf-border-color:var(--knopf-hover-border-color);--knopf-gradient-from:var(--knopf-hover-gradient-from);--knopf-gradient-to:var(--knopf-hover-gradient-to);--knopf-text-color:var(--knopf-hover-text-color);--knopf-text-decoration-color:var(--knopf-hover-text-decoration-color)}.knopf.knopf:focus,.knopf.knopf:focus-within{--knopf-box-shadow:var(--knopf-focus-ring);outline:0}.knopf.active{--knopf-background-alpha:var(--knopf-active-background-alpha);--knopf-background-color:var(--knopf-active-background-color);--knopf-border-color:var(--knopf-active-border-color);--knopf-gradient-from:var(--knopf-active-gradient-from);--knopf-gradient-to:var(--knopf-active-gradient-to);--knopf-text-color:var(--knopf-active-text-color);--knopf-text-decoration-color:var(--knopf-active-text-decoration-color)}.knopf.small{--knopf-font-size:var(--knopf-font-size-small);--knopf-padding:var(--knopf-padding-small);--knopf-icon-size:var(--knopf-icon-size-small)}.knopf.large{--knopf-font-size:var(--knopf-font-size-large);--knopf-padding:var(--knopf-padding-large);--knopf-icon-size:var(--knopf-icon-size-large)}.knopf.huge{--knopf-font-size:var(--knopf-font-size-huge);--knopf-padding:var(--knopf-padding-huge);--knopf-icon-size:var(--knopf-icon-size-huge)}.knopf.traced,.knopf.outlined{--knopf-border-color:var(--knopf-color)}.knopf.traced{--knopf-border-alpha:33%}.knopf.outlined{--knopf-border-alpha:100%}.knopf.block{--knopf-display:flex;width:100%}.knopf.start{--knopf-justify-content:flex-start}.knopf.center{--knopf-justify-content:center}.knopf.end{--knopf-justify-content:flex-end}.knopf.even{--knopf-inline-padding:var(--knopf-padding)}.knopf.wide{--knopf-inline-padding:calc(var(--knopf-padding) * 3)}.knopf.pill{--knopf-border-radius:9999px}.knopf.sharp{--knopf-border-radius:0}.knopf.gradient{--knopf-named-background-color:transparent;--knopf-background-image:linear-gradient(hsl(var(--knopf-gradient-from) / var(--knopf-background-alpha)),hsl(var(--knopf-gradient-to) / var(--knopf-background-alpha)))}.knopf.link{--knopf-border-width:0;--knopf-border-radius:0;--knopf-padding:0;--knopf-font-size:null;--knopf-text-decoration-line:underline;--knopf-text-decoration-color:hsl(var(--knopf-color) / 40%);--knopf-hover-text-decoration-color:hsl(var(--knopf-color-dark));--knopf-active-text-decoration-color:hsl(var(--knopf-color-darker));--knopf-background-alpha:0;--knopf-border-alpha:0;--knopf-text-color:var(--knopf-color);--knopf-hover-background-alpha:0;--knopf-hover-border-alpha:0;--knopf-hover-text-color:var(--knopf-color-dark);--knopf-active-background-alpha:0;--knopf-active-border-alpha:0;--knopf-active-text-color:var(--knopf-color-darker)}.knopf>.icon{align-items:center;color:inherit;display:inline-flex;fill:currentColor;flex-shrink:0;font-size:var(--knopf-icon-size);height:var(--knopf-icon-size);justify-content:center;width:var(--knopf-icon-size)}.knopf>.icon.small{--knopf-icon-size:var(--knopf-icon-size-small)}.knopf>.icon.base{--knopf-icon-size:var(--knopf-icon-size-base)}.knopf>.icon.large{--knopf-icon-size:var(--knopf-icon-size-large)}.knopf>.icon.huge{--knopf-icon-size:var(--knopf-icon-size-huge)}.knopf>.icon:first-child:not(:only-child){margin-inline-start:calc(var(--knopf-inline-padding) * -0.25);margin-inline-end:calc(var(--knopf-inline-padding) * 0.5)}.knopf>.icon:last-child:not(:only-child){margin-inline-start:calc(var(--knopf-inline-padding) * 0.5);margin-inline-end:calc(var(--knopf-inline-padding) * -0.25)}@media screen and (min-width:576px){.knopf>.icon.floating:not(:only-child){margin-inline-start:0;margin-inline-end:0;position:absolute}.knopf>.icon.floating:first-child:not(:only-child){left:var(--knopf-inline-padding)}.knopf>.icon.floating:last-child:not(:only-child){right:var(--knopf-inline-padding)}}.knopf-group{align-items:stretch;display:inline-flex;flex-direction:var(--knopf-group-direction);flex-wrap:nowrap;justify-content:center}.knopf-group.vertical{--knopf-group-direction:column}.knopf-group.vertical>.knopf:first-child:not(:last-child){border-bottom-left-radius:0;border-bottom-right-radius:0}.knopf-group.vertical>.knopf:last-child:not(:first-child){border-top-left-radius:0;border-top-right-radius:0}.knopf-group:not(.vertical)>.knopf:first-child:not(:last-child){border-bottom-right-radius:0;border-top-right-radius:0}.knopf-group:not(.vertical)>.knopf:last-child:not(:first-child){border-bottom-left-radius:0;border-top-left-radius:0}.knopf-group>.knopf:not(:first-child):not(:last-child){--knopf-border-radius:0}.knopf-group.vertical>.knopf+.knopf{margin-block-start:calc(var(--knopf-border-width) * -1)}.knopf-group:not(.vertical)>.knopf+.knopf{margin-inline-start:calc(var(--knopf-border-width) * -1)} \ No newline at end of file diff --git a/css/library/material-icons.css b/css/library/material-icons.css new file mode 100644 index 0000000..3e8f709 --- /dev/null +++ b/css/library/material-icons.css @@ -0,0 +1,21 @@ +.material-icons { + font-family: 'Material Icons'; + font-weight: normal; + font-style: normal; + font-size: 24px; /* Preferred icon size */ + display: inline-block; + line-height: 1; + text-transform: none; + letter-spacing: normal; + word-wrap: normal; + white-space: nowrap; + direction: ltr; + /* Support for all WebKit browsers. */ + -webkit-font-smoothing: antialiased; + /* Support for Safari and Chrome. */ + text-rendering: optimizeLegibility; + /* Support for Firefox. */ + -moz-osx-font-smoothing: grayscale; + /* Support for IE. */ + font-feature-settings: 'liga'; +} diff --git a/css/player.css b/css/player.css new file mode 100644 index 0000000..a02ab1a --- /dev/null +++ b/css/player.css @@ -0,0 +1,310 @@ +#awn-toast-container{ + z-index: 2; + right: 270px; +} +.awn-toast-wrapper{ + padding-top: 24px; + padding-bottom: 24px; +} +.awn-toast-label{ + font-size: 0px; +} + +.awn-toast-content{ + font-size: medium; +} + +.playerWrapper{ + display: flex; + width: 100%; + height: 100%; +} +.videoWrapper{ + width:100%; + z-index: 1; +} +.sidebar{ + display: flex; + flex-flow: column; + background-color: RGB(25, 25, 25); + width: 300px; + padding: 5px 10px 5px 10px; + z-index: 5; +} +.sidebar_header{ + display: flex; +} + +.sidebar_create_title{ + color: #cc0033; + font-weight: bold; +} +.sidebar_create_text{ + color: whitesmoke; + margin-top: 0px; + font-weight: bold; +} +.sidebar_is_host_text{ + margin-top: 2px; + margin-bottom: 2px; + color: whitesmoke; + font-size:unset ; +} + +.sidebar_is_host{ + margin-top: 25px; + margin-bottom: 25px; + justify-content: space-between; + display: flex; + width: 100%; +} + +.sidebar_content{ + display: flex; + justify-content: center; + flex-flow: column; + align-items: center; + flex-grow: 8; +} +.sidebar_footer{ + display: flex; + justify-content: flex-end; +} + +.sidebar_footer p{ + font-size: x-small; + color:whitesmoke; +} + + + +.sidebar_header .sidebar_title{ + color: #cc0033; + text-align: right; +} + + + +.sidebar_link{ + max-width: 180px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + margin: 0px 0px 0px 0px; +} + + +.sidebar_copy_link{ + padding: 12px; + margin-top:20px; + margin-bottom: 20px; + border: 2px solid #265d97; + border-radius: 10px; + background-color:#132f4c; + display:flex; + font-size: smaller; + color: #b2bac2; + cursor: pointer; +} +.sidebar_copy_link:hover{ + background-color: rgb(22, 55, 89); + border-color: #66b2ff; +} + +.sidebar_copy_link:hover .sidebar_copy_icons { + color: #66b2ff; +} + +.share_button{ + margin-left: 3px !important; + margin-right: 3px !important; +} + +.line_share{ + background-color: #00b000 !important; +} +.line_share:hover{ + background-color: #00b0008a !important; +} + +.twitter_share{ + background-color: #1c93e4 !important; +} +.twitter_share:hover{ + background-color: #1c94e48c !important; +} + +.facebook_share{ + background-color: #3f62a9 !important; +} +.facebook_share:hover{ + background-color: #3f62a996 !important; +} +.mail_share{ + background-color: #cfaa14 !important; +} +.mail_share:hover{ + background-color: #cfaa1493 !important; +} + +.sidebar_wrap{ + display: flex; +} + +.notification-icon{ + color:dimgray; + margin-left: 5px; + margin-right: 5px; +} + +/*buttonAreaに表示するに関係するスタイル*/ +.controll_button{ + position: relative; + width: 44px; + height: 50px; + display: flex; + justify-content: center; + align-items: center; +} + +.buttonArea_icon{ + color: #b2bac2; + font-size: 21px; +} +.controll_button:hover .buttonArea_icon{ + color: rgb(239, 239, 239); + cursor: pointer; + +} + +.fav_button:hover .buttonArea_icon{ + color: rgb(255, 34, 71); +} + +.midle_finger_button:hover .buttonArea_icon{ + color: rgb(204, 204, 0); +} + +.sync_button:hover { + animation: r1 1.5s linear infinite; + } + @keyframes r1 { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } + } + +/* https://cssbuttons.io/detail/kirzin/selfish-kangaroo-64*/ +/* From cssbuttons.io by @kirzin */ + +.sidebar_create { + text-decoration: none; + position: block; + border: none; + font-size: 14px; + font-family: inherit; + color: #fff; + width: 9em; + height: 3em; + line-height: 2em; + text-align: center; + background: linear-gradient(90deg,#cc0033,#bdaeb2,#eb5528,#cc0033); + background-size: 300%; + border-radius: 30px; + z-index: 1; + font-weight: bold; + margin-top: 15px; + margin-bottom: 15px; + } + + .sidebar_create:hover { + animation: ani 8s linear infinite; + border: none; + } + + @keyframes ani { + 0% { + background-position: 0%; + } + + 100% { + background-position: 400%; + } + } + + .sidebar_create:before { + content: ''; + position: block; + top: -5px; + left: -5px; + right: -5px; + bottom: -5px; + z-index: -1; + background: linear-gradient(90deg,#cc0033,#bdaeb2,#eb5528,#cc0033); + background-size: 400%; + border-radius: 35px; + transition: 1s; + } + + .sidebar_create:hover::before { + filter: blur(20px); + } + + .sidebar_create:active { + background: linear-gradient(32deg,#cc0033,#bdaeb2,#eb5528,#cc0033); + } + +/* From cssbuttons.io by @mrhyddenn */ +/* The switch - the box around the slider */ +.switch { + font-size: 12px; + position: relative; + display: inline-block; + width: 56px; + height: 25px; + margin-left: 10px; + margin-right: 10px; + } + + /* Hide default HTML checkbox */ + .switch input { + opacity: 1; + width: 0; + height: 0; + } + + /* The slider */ + .slider { + position: absolute; + cursor: pointer; + top: 0; + left: 0; + right: 0; + bottom: 0px; + background: #fff; + transition: .4s; + border-radius: 30px; + border: 1px solid #ccc; + } + + .slider:before { + position: absolute; + content: ""; + height: 1.9em; + width: 1.9em; + border-radius: 16px; + left: 1.2px; + top: 0; + bottom: 0; + background-color: white; + box-shadow: 0 2px 5px #999999; + transition: .4s; + } + + input:checked + .slider { + background-color: #eb5528; + border: 1px solid transparent; + } + + input:checked + .slider:before { + transform: translateX(2.5em); + } diff --git a/css/popup/style.css b/css/popup/style.css new file mode 100644 index 0000000..c9152df --- /dev/null +++ b/css/popup/style.css @@ -0,0 +1,4 @@ +body{ + width: 250px; + height: 400px; + } \ No newline at end of file diff --git a/css/style.css b/css/style.css new file mode 100644 index 0000000..2f67f83 --- /dev/null +++ b/css/style.css @@ -0,0 +1,17 @@ + +.itemModule a:first-child { + height: 100%; + width: 80% !important; +} + +.play-btn ,.popper-btn{ + height: 100%; + width: 10% !important; + align-self: flex-end ; +} + +section.clearfix { + display: flex; + width: 100%!important; +} + diff --git a/html/popup.html b/html/popup.html new file mode 100644 index 0000000..51f0090 --- /dev/null +++ b/html/popup.html @@ -0,0 +1,12 @@ + + + + + d-pary popup + + + + + + + \ No newline at end of file diff --git a/icon/dp.png b/icon/dp.png new file mode 100644 index 0000000..7d79281 Binary files /dev/null and b/icon/dp.png differ diff --git a/icon/icon128.png b/icon/icon128.png new file mode 100644 index 0000000..60b7624 Binary files /dev/null and b/icon/icon128.png differ diff --git a/icon/icon16.png b/icon/icon16.png new file mode 100644 index 0000000..1fdd401 Binary files /dev/null and b/icon/icon16.png differ diff --git a/icon/icon48.png b/icon/icon48.png new file mode 100644 index 0000000..ea79b13 Binary files /dev/null and b/icon/icon48.png differ diff --git a/images/Play.svg b/images/Play.svg new file mode 100644 index 0000000..395e23c --- /dev/null +++ b/images/Play.svg @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/images/Popper.svg b/images/Popper.svg new file mode 100644 index 0000000..bab7d28 --- /dev/null +++ b/images/Popper.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/js/background.js b/js/background.js new file mode 100644 index 0000000..53f0f78 --- /dev/null +++ b/js/background.js @@ -0,0 +1 @@ +console.log("background activate"); \ No newline at end of file diff --git a/js/common/settings.js b/js/common/settings.js new file mode 100644 index 0000000..acb1947 --- /dev/null +++ b/js/common/settings.js @@ -0,0 +1,28 @@ +/** + * @description + * d-partyのバックエンドに関する情報 + * localhostで実験する場合は + * d-party.net->localhost + * https -> http + * wss -> ws + * に変更すればよいはず + */ + +let D_PARTY_BACKEND_HOST = "d-party.net/"; + +let D_PARTY_BACKEND_PROTOCOL = "https://"; +let D_PARTY_WEBSOCKET_PROTOCOL = "wss://"; + +let D_PARTY_API_ENDPINT = D_PARTY_BACKEND_PROTOCOL + D_PARTY_BACKEND_HOST + "api/v1/"; +let D_PARTY_VERSION_CHECK_ENDPOINT = D_PARTY_API_ENDPINT + "chrome-extension/version-check"; + +let D_PARTY_ANIMESTORE_HOST = D_PARTY_BACKEND_HOST + "anime-store/"; +let D_ANI_PARTY_WEBSOCKET_ENDPOINT = D_PARTY_WEBSOCKET_PROTOCOL + D_PARTY_ANIMESTORE_HOST + "party/"; +let D_PARTY_ANIMESTORE_REDIRECT_ENDPOINT = D_PARTY_BACKEND_PROTOCOL + D_PARTY_ANIMESTORE_HOST + "lobby/"; + +/** + * @description + * facebookのAPP ID + * これが無いとシェアボタンが使えない + */ +let FACEBOOK_APP_ID = "256850306460920"; \ No newline at end of file diff --git a/js/d-animestore/player.js b/js/d-animestore/player.js new file mode 100644 index 0000000..f0e140b --- /dev/null +++ b/js/d-animestore/player.js @@ -0,0 +1,621 @@ + + +// actionを実行可能であるかを管理する。 +// video_actionを受け取った場合、これをfalseにしないと無限ループに陥る +available_action = true; +in_room = false; + + + +// 画面のload完了時に実行する関数 +window.addEventListener("load", main, false); +function main(e) { + // modeがルームに参加or作成であった場合、autoplayにならないように変更 + if (MODE === "join" || MODE === "create") { + // ルームに入ったタイミングで勝手に再生されないようにvideoタグからautoplayを削除 + document.getElementById("video").removeAttribute("autoplay"); + } + //awesome-notificationの設定 + globalOptions = { + position: "top-right", + maxNotifications: 4, + animationDuration: 200, + durations: { + global: 3000, + } + }; + notifier = new AWN(globalOptions); +}; + +function PlayingVideo(option) { + /** + * @description + * 動画の再生を行う処理 + * もしも、すでに動画が再生状態であれば何もせず終了する + * ユーザーに動作を分かりやすく視認してもらうために、backAreaのクリックを実行 + */ + if (document.getElementById("video").paused) { + onBackArea(option); + } +} + +// 動画の停止処理 +function PauseVideo(option) { + /** + * @description + * 動画の停止を行う処理 + * もしも、すでに動画が停止状態であれば何もせず終了する + * ユーザーに動作を分かりやすく視認してもらうために、backAreaのクリックを実行 + */ + if (!document.getElementById("video").paused) { + onBackArea(option); + } +} + +function onPlayButton(option) { + /** + * @description + * 動画プレイヤー下部にあるコントローラー内の再生/停止ボタンをクリックする関数 + */ + document.getElementsByClassName("playButton")[0].click(); +} + +function onPrevButton(option) { + /** + * @description + * 動画プレイヤー下部にあるコントローラー内の先頭に戻るボタンをクリックする関数 + * 動画の再生時間によって、動画の先頭に戻る場合と前の動画に戻る場合がある + */ + document.getElementsByClassName("prevButton")[0].click(); +} + +function onPrevThumbnailButton(option) { + /** + * @description + * 動画プレイヤー下部にあるコントローラー内の先頭に戻るボタンをマウスオーバーした時に出るサムネイルをクリックする関数 + * 動画の再生時間によって、マウスオーバーしても出てこない場合がある + */ + document.getElementById("prevThumbButton").click() +} + +function onNextButton(option) { + /** + * @description + * 動画プレイヤー下部にあるコントローラー内の次の話ボタンをクリックする関数 + */ + document.getElementsByClassName("nextButton")[0].click(); +} + +function onNextThumbnailButton(option) { + /** + * @description + * 動画プレイヤー下部にあるコントローラー内の次の話ボタンをマウスオーバーした時に出るサムネイルをクリックする関数 + * 動画の再生時間によって、マウスオーバーしても出てこない場合がある + */ + document.getElementById("nextThumbButton").click() +} + +function onBackArea(option) { + /** + * @description + * 動画全体をクリックする + * 一般的には再生/停止を切り替える + */ + document.getElementsByClassName("backArea")[0].click(); +} + + +function onSeek(option) { + /** + * @description + * videoタグのcurrentTimeに時間を設定する + */ + available_action = false; + document.getElementById("video").currentTime = option["time"]; +} + +function onChangeRate(option) { + /** + * @description + * videoタグのplaybackRateを合わせる + * ただし、playbackRateが同一であった場合は、処理は行わない + */ + if (document.getElementById("video").playbackRate != option["rate"]) { + document.getElementById("video").playbackRate = option["rate"]; + }; + document.getElementById("video").paused = option["paused"]; +} + +function onSync(option) { + /** + * @description + * ホストの状態に合わせる関数 + * optionを受け取って全てのステータスを合わせる + */ + onSeek(option); + available_action = false; + onChangeRate(option); + available_action = false; + if (option["paused"] === "False") { + PlayingVideo(option); + } else { + PauseVideo(option); + } +} + + +function onAction(action, option) { + /** + * @description + * video_actionを受け取り、適切な関数を呼び出す + */ + switch (action) { + case "playing": + PlayingVideo(option); + break; + case "pause": + PauseVideo(option); + break; + case "prev": + onPrevButton(option); + case "prev_thumbnail": + onPrevThumbnailButton(option); + break; + case "next": + onNextButton(option); + break; + case "next_thumbnail": + onNextThumbnailButton(option); + break; + case "back_area": + onBackArea(option) + case "seek": + onSeek(option); + break; + case "ratechange": + onChangeRate(option); + break; + case "sync": + onSync(option); + break; + } +}; + +function sendCreateRoom(socket) { + /** + * @description + * ルームの作成依頼をwebsocketで要求する + */ + socket.send(JSON.stringify({ + "method": "create", + "user_name": "user", + "part_id": getParam("partId") + })); +} + +function sendJoinRoom(socket, room_id) { + /** + * @description + * ルームの参加依頼をwebsocketで要求する + * 成功している場合method : join がそのうち帰ってくる + */ + socket.send(JSON.stringify({ + "method": "join", + "user_name": "user", + "room_id": room_id, + })); +} + +async function sendVideoActionRoom(socket, action, option) { + /** + * @description + * video_actionを送信する + * 送信後はavailable_actionをtrueにする + */ + await socket.send(JSON.stringify({ + "method": "video_action", + "user_id": user_id, + "action": action, + "option": option + })); + available_action = true; +} + +async function sendSyncRequest(socket) { + /** + * @description + * ルームに対してsync要求をwebsocketで要求する + * サーバーはホストのみにsync_requestを送信し、送信元にoptionを返す + */ + await socket.send(JSON.stringify({ + "method": "sync_request", + })); + available_action = true; +} + +async function sendSyncResponse(socket, user, option) { + /** + * @description + * sync_requestの返信をwebsocketで送信する + * 上の関数のsendSyncRequestで送信された要求を受け取ったホストがこの関数を呼び出す + */ + await socket.send(JSON.stringify({ + "method": "sync_response", + "to_user": user, + "option": option + })); + available_action = true; +} + +function sendActionNotification(socket, action) { + /** + * @description + * ルームに対してaction_notificationを送信する + * 通知が必要な動作を行った場合に呼び出される + */ + socket.send(JSON.stringify({ + "method": "action_notification", + "action": action, + "user_name": "user", + })); +} + +function getTitle() { + /** + * @description + * ウェブページ向けにタイトルを取得 + */ + let info = ""; + info = document.getElementsByClassName("backInfoTxt1")[0].textContent; + info += " - " + info += document.getElementsByClassName("backInfoTxt2")[0].textContent; + info += " - " + info += document.getElementsByClassName("backInfoTxt3")[0].textContent; + info += " | dアニメストア" + return info +} + +function getActionOption() { + /** + * @description + * プレイヤーの状態を取得し返す関数 + * 普段は取得したoptionをvideo_actionを送る際についでに送信する + * とりあえず、この情報があればプレイヤーの状態を再現できるはず、、、理論上は、、、 + */ + let option = { + "time": document.getElementById("video").currentTime, + "src": document.getElementById("video").getAttribute("src"), + "paused": document.getElementById("video").paused, + "rate": document.getElementById("video").playbackRate, + "part_id": getParam("partId") + }; + return option; +} + +document.getElementById("video").addEventListener('loadeddata', function () { + /** + * @description + * 動画のロードが終了した場合に、行うべき処理を記述する + * ページのタイトルに動画の情報から取得したタイトルを挿入する + */ + document.querySelector('title').textContent = getTitle(); +} +); + + + +document.getElementsByClassName("sidebar_create")[0].onclick = async function () { + change_create(); + //作成ボタンを推された場合の処理 + var url = new URL(window.location.href); + + var params = url.searchParams; + + socket = new WebSocket(D_ANI_PARTY_WEBSOCKET_ENDPOINT); + socket.onmessage = (event) => { + let data = JSON.parse(event.data); + console.log(data) + switch (data["method"]) { + case "create": + user_id = data["user"]["user_id"]; + room_id = data["room_id"]; + document.getElementsByClassName("sidebar_link")[0].innerHTML = D_PARTY_ANIMESTORE_REDIRECT_ENDPOINT + room_id + break; + case "user_add": + notifier.info("『" + data["user"]["user_name"] + "』さんが参加 "); + break; + case "video_action": + available_action = false; + onAction(data["action"], data["option"]); + break; + case "leave": + getActionOption() + notifier.info("『" + data["user"]["user_name"] + "』さんが退室 "); + case "sync_request": + sendSyncResponse(socket, user = data["user"], option = getActionOption()); + break; + case "sync_response": + available_action = false; + onAction("sync", data["option"]); + break; + case "action_notification": + switch (data["action"]) { + case "next": + notifier.info("『" + data["user"]["user_name"] + "』さんから『』を受信"); + break; + case "play": + notifier.info("『" + data["user"]["user_name"] + "』さんから『』を受信"); + break; + case "stop": + notifier.info("『" + data["user"]["user_name"] + "』さんから『』を受信"); + break; + case "skip": + notifier.info("『" + data["user"]["user_name"] + "』さんから『』を受信"); + break; + } + break; + } + window.setTimeout(function () { + available_action = true; + }, 500); + }; + + // WebSocketクローズ時の処理 + socket.onclose = (event) => { + // ウェブページを閉じたとき以外のWebSocketクローズは想定外 + notifier.alert('サーバーとの通信が終了しました'); + }; + add_contoroll_button(); + general_websocket(socket); + + window.setTimeout(function () { + sendCreateRoom(socket) + }, 500); +}; + + +document.getElementById("video").addEventListener('loadeddata', function () { + if (MODE !== "join") { + return + }; + //参加ボタンを推された場合の処理 + var url = new URL(window.location.href); + + var params = url.searchParams; + + socket = new WebSocket(D_ANI_PARTY_WEBSOCKET_ENDPOINT); + socket.onmessage = (event) => { + + let data = JSON.parse(event.data); + console.log(data) + switch (data["method"]) { + case "video_action": + available_action = false; + onAction(data["action"], data["option"]); + break; + case "create": + break; + case "join": + user_id = data["user"]["user_id"]; + room_id = data["room_id"]; + document.getElementsByClassName("sidebar_link")[0].innerHTML = D_PARTY_ANIMESTORE_REDIRECT_ENDPOINT + room_id; + notifier.success("ルームに参加"); + break; + case "user_add": + notifier.info("『" + data["user"]["user_name"] + "』さんが参加 "); + break; + case "leave": + notifier.info("『" + data["user"]["user_name"] + "』さんが退室 "); + break; + case "sync_request": + sendSyncResponse(socket, user = data["user"], option = getActionOption()); + break; + case "sync_response": + available_action = false; + onAction("sync", data["option"]); + notifier.info('再生状況をホストにシンク'); + break; + case "action_notification": + switch (data["action"]) { + case "next": + notifier.info("『" + data["user"]["user_name"] + "』さんから『』を受信"); + break; + case "play": + notifier.info("『" + data["user"]["user_name"] + "』さんから『』を受信"); + break; + case "stop": + notifier.info("『" + data["user"]["user_name"] + "』さんから『』を受信"); + break; + case "skip": + notifier.info("『" + data["user"]["user_name"] + "』さんから『』を受信"); + break; + } + break; + } + window.setTimeout(function () { + available_action = true; + }, 500); + }; + + // WebSocketクローズ時の処理 + socket.onclose = (event) => { + // ウェブページを閉じたとき以外のWebSocketクローズは想定外 + notifier.alert('サーバーとの通信が終了'); + }; + + if (params.get('party') === "true") { + document.querySelector('title').textContent += ' 🎉' + } + add_contoroll_button(); + general_websocket(socket); + room_id = getParam("room_id"); + window.setTimeout(function () { + sendJoinRoom(socket, room_id); + available_action = true; + sendSyncRequest(socket); + }, 500); +} + , { + once: true + }); + +function general_websocket(socket) { + /** + * @description + * 動画の操作を検知して、websocketを送信するためのeventListenerを追加する関数 + */ + // 再生処理 + document.getElementById("video").addEventListener('playing', function () { + document.getElementById("video").setAttribute("autoplay", ""); + if (available_action) { + option = getActionOption(); + sendVideoActionRoom(socket, "playing", option); + }; + available_action = true; + } + ); + // 停止処理 + document.getElementById("video").addEventListener('pause', function () { + console.log(available_action) + if (available_action) { + option = getActionOption(); + sendVideoActionRoom(socket, "pause", option); + }; + available_action = true; + } + ); + // ロード処理 + document.getElementById("video").addEventListener('loadeddata', function () { + if (available_action) { + option = getActionOption(); + sendVideoActionRoom(socket, "loaded", option); + }; + available_action = true; + } + ); + + // seek処理 + document.getElementById("video").addEventListener('seeking', function () { + if (available_action) { + option = getActionOption(); + sendVideoActionRoom(socket, "seek", option); + }; + available_action = true; + } + ); + // 再生速度変更 + document.getElementById("video").addEventListener('ratechange', function () { + if (available_action && document.getElementById("video").playbackRate != 0) { + option = getActionOption(); + sendVideoActionRoom(socket, "ratechange", option); + }; + available_action = true; + } + ); + + //次の話のポップを推された場合の処理 + document.getElementById("nextThumbinner").onclick = function () { + if (available_action) { + option = getActionOption(); + sendVideoActionRoom(socket, "next_thumbnail", option); + available_action = true; + sendActionNotification(socket, "next"); + notifier.success("『』をルームに送信"); + } + }; + + //次の話ボタンを推された場合の処理 + document.getElementsByClassName("nextButton")[0].onclick = function () { + if (available_action) { + option = getActionOption(); + sendVideoActionRoom(socket, "next", option); + available_action = true; + sendActionNotification(socket, "next"); + notifier.success("『』をルームに送信"); + } + }; + + //前の話ボタンを推された場合の処理 + document.getElementsByClassName("prevButton")[0].onclick = function () { + if (available_action) { + option = getActionOption(); + sendVideoActionRoom(socket, "prev", option); + available_action = true; + } + }; + //前の話のポップを推された場合の処理 + document.getElementById("prevThumbinner").onclick = function () { + if (available_action) { + option = getActionOption(); + sendVideoActionRoom(socket, "prev_thumbnail", option); + available_action = true; + } + }; + + document.getElementsByClassName("sync_button")[0].onclick = function () { + if (available_action) { + sendSyncRequest(socket); + available_action = true; + } + }; + document.getElementsByClassName("backArea")[0].onclick = function () { + if (available_action) { + if (!document.getElementById("video").paused) { + sendActionNotification(socket, "play"); + notifier.success("『』をルームに送信"); + } else { + sendActionNotification(socket, "stop"); + notifier.success("『』をルームに送信"); + } + } + }; + + document.getElementsByClassName("seekArea")[0].onclick = function () { + sendActionNotification(socket, "skip"); + notifier.success("『』をルームに送信"); + }; + document.getElementsByClassName("backButton")[0].onclick = function () { + sendActionNotification(socket, "skip"); + notifier.success("『』をルームに送信"); + }; + document.getElementsByClassName("skipButton")[0].onclick = function () { + sendActionNotification(socket, "skip"); + notifier.success("『』をルームに送信"); + }; + document.getElementsByClassName("skip10Button")[0].onclick = function () { + sendActionNotification(socket, "skip"); + notifier.success("『』をルームに送信"); + }; + document.getElementsByClassName("skip30Button")[0].onclick = function () { + sendActionNotification(socket, "skip"); + notifier.success("『』をルームに送信"); + }; + document.getElementsByClassName("back10Button")[0].onclick = function () { + sendActionNotification(socket, "skip"); + notifier.success("『』をルームに送信"); + }; + document.getElementsByClassName("back30Button")[0].onclick = function () { + sendActionNotification(socket, "skip"); + notifier.success("『』をルームに送信"); + }; +}; + +function add_contoroll_button() { + /** + * @description + * 動画下部に存在してるコントロールバーにアイコンを追加する + * 追加するアイコンはホストに同期するためのsyncボタンとリアクションボタンが複数である + */ + // syncボタンの追加 + $(".space").before("
"); + // サムアップのリアクションボタンを追加 + $(".space").before("
"); + // ハートマークのリアクションボタンを追加 + $(".space").before("
"); + // 笑顔のリアクションボタンを追加 + $(".space").before("
"); + // 涙のリアクションボタンを追加 + $(".space").before("
"); + // 中指を立てるリアクションボタンを追加 + $(".space").before("
"); +} diff --git a/js/d-animestore/sidebar.js b/js/d-animestore/sidebar.js new file mode 100644 index 0000000..8ad53b8 --- /dev/null +++ b/js/d-animestore/sidebar.js @@ -0,0 +1,209 @@ +makeFontFace(); + +$(".videoWrapper").wrap("
"); +$(".playerWrapper").append(""); +$(".sidebar").append(""); +$(".sidebar_header").append(""); + +$(".sidebar").append(""); +$(".sidebar_content").append(""); +$(".sidebar_content").append(""); +$(".sidebar_content").append(""); +$(".sidebar_content").append(""); +$(".sidebar_content").append("") +$(".sidebar_is_host").append(""); +$(".sidebar_is_host").append(""); + +$(".sidebar").append(""); +$(".sidebar_footer").append(""); + +$('.create_content').hide(0); +$('.sidebar_header').hide(0); +$('.sidebar_footer').hide(0); +$('.sidebar_header').fadeIn(1000); +$('.sidebar_footer').fadeIn(1000); + +function show_create() { + /** + * @description + * ルーム作成のためのサイドバーの項目を表示 + */ + $('.create_content').fadeIn(1000); +}; +function hide_create() { + /** + * @description + * ルーム作成のためのサイドバーの項目を隠す(フェードアウト) + */ + $('.create_content').fadeOut(1000); +}; + +function change_create() { + /** + * @description + * ルーム作成のためのサイドバーの項目を隠し、ルームに参加後に必要な内容を表示する + */ + $('.create_content').fadeOut(1000, show_join); + window.setTimeout(function () { + notifier.success('ルームの作成に成功しました'); + }, 1000); +} +$(".sidebar_content").append(""); +$(".sidebar_content").append(""); +$(".sidebar_content").append(""); + + +$(".sidebar_content").append(""); +$(".sidebar_share_button").append(""); +$(".sidebar_share_button").append(""); +$(".sidebar_share_button").append(""); +$(".sidebar_share_button").append(""); + +$(".sidebar_content").append(""); +$(".sidebar_copy_link").append(""); +$(".sidebar_copy_link").append("content_copy"); + +// 画面のロード完了時にmain関数を呼び出す +window.addEventListener("load", main, false); + +function main(e) { + document.getElementsByClassName("sidebar_copy_link")[0].onclick = function () { + navigator.clipboard.writeText(D_PARTY_ANIMESTORE_REDIRECT_ENDPOINT + room_id); + document.getElementsByClassName("sidebar_copy_icons")[0].innerHTML = "done"; + window.setTimeout(function () { + document.getElementsByClassName("sidebar_copy_icons")[0].innerHTML = "content_copy"; + }, 1000); + notifier.success('共有リンクをコピーしました'); + }; + + document.getElementsByClassName("twitter_share")[0].onclick = function () { + /** + * @description + * twitter向けのシェアリンクをクリックした場合の処理 + */ + text = "dアニメストアで『" + text += document.getElementsByClassName("backInfoTxt1")[0].textContent; + text += " - "; + text += document.getElementsByClassName("backInfoTxt2")[0].textContent; + text += " - "; + text += document.getElementsByClassName("backInfoTxt3")[0].textContent; + text += "』を一緒に見ませんか? 拡張機能『d-party』を使ってパーティーに参加してください"; + hashtags = "dアニメストア,dパーティー" + encoded_text = encodeURIComponent(text); + encoded_url = encodeURIComponent(D_PARTY_ANIMESTORE_REDIRECT_ENDPOINT + room_id) + encoded_hashtags = encodeURIComponent(hashtags); + url = 'https://twitter.com/intent/tweet?text=' + encoded_text + "&url=" + encoded_url + "&hashtags=" + encoded_hashtags; + window.open(url, null, 'width=700,height=300'); + } + + document.getElementsByClassName("line_share")[0].onclick = function () { + /** + * @description + * Line向けのシェアリンクをクリックした場合の処理 + */ + url = "https://social-plugins.line.me/lineit/share?url=" + encoded_param = encodeURIComponent(D_PARTY_ANIMESTORE_REDIRECT_ENDPOINT + room_id) + + window.open(url + encoded_param, null); + } + + document.getElementsByClassName("facebook_share")[0].onclick = function () { + /** + * @description + * Facebook向けのシェアリンクをクリックした場合の処理 + */ + href = D_PARTY_ANIMESTORE_REDIRECT_ENDPOINT + room_id; + encoded_href = encodeURIComponent(href); + url = "https://www.facebook.com/dialog/share?app_id=" + FACEBOOK_APP_ID + "&href=" + encoded_href; + window.open(url, null, 'width=700,height=400'); + } + + document.getElementsByClassName("mail_share")[0].onclick = function () { + /** + * @description + * mail向けのシェアリンクをクリックした場合の処理 + */ + subject = "dアニメストアで一緒にアニメを観ませんか?"; + encoded_subject = encodeURIComponent(subject); + + text = "dアニメストアで『" + text += document.getElementsByClassName("backInfoTxt1")[0].textContent; + text += " - "; + text += document.getElementsByClassName("backInfoTxt2")[0].textContent; + text += " - "; + text += document.getElementsByClassName("backInfoTxt3")[0].textContent; + text += "』を一緒に見ませんか? 拡張機能『d-party』を使ってパーティーに参加してください\n\n"; + encoded_text = encodeURIComponent(text); + encoded_param = encodeURIComponent(D_PARTY_ANIMESTORE_REDIRECT_ENDPOINT + room_id + "\n"); + + window.open("mailto:?" + "&subject=" + encoded_subject + "&body=" + encoded_text + encoded_param, null); + } +} + +$(".sidebar_content").append(""); +$('.join_content').hide(0); + +function show_join() { + /** + * @description + * ルーム参加後のためのサイドバーの項目を表示する + */ + $('.join_content').fadeIn(1000); +}; +function hide_join() { + /** + * @description + * ルーム参加後のためのサイドバーの項目を隠す(フェードアウト) + */ + $('.join_content').fadeOut(1000); +}; + +function change_join() { + $('.join_content').fadeOut(1000, show_create); +} + +function hide_sidebar() { + /** + * @description + * サイドバー全体を隠す + */ + $('.sidebar').hide(0); +} + +function show_sidebar() { + /** + * @description + * サイドバ全体を表示する + */ + $('.sidebar').show(0); +} + +MODE = "normal" + +if (getParam("party") === "create") { + show_create(); + document.getElementById("video").removeAttribute("autoplay"); + MODE = "create" + +} else if (getParam("party") === "join") { + show_join(); + document.getElementById("video").removeAttribute("autoplay"); + MODE = "join" +} else { + hide_sidebar(); +} + +document.addEventListener('fullscreenchange', (event) => { + /** + * @description + * フルスクリーンのon/offを検出し、 + * サイドバーの表示や通知場所の変更を行うeventListenerを追加する + */ + if (document.fullscreenElement) { + document.getElementById("awn-toast-container").setAttribute("style", "right:24px;"); + hide_sidebar(); + } else { + document.getElementById("awn-toast-container").setAttribute("style", ""); + show_sidebar(); + } +}); \ No newline at end of file diff --git a/js/d-animestore/store.js b/js/d-animestore/store.js new file mode 100644 index 0000000..0092000 --- /dev/null +++ b/js/d-animestore/store.js @@ -0,0 +1,53 @@ +chrome.extension.onRequest.addListener( + function (request, sender, sendResponse) { + } +); + +function makeFontFace() { + /** + * @description + * Material Iconsを読み込むために必要なタグをheaderに追加する + */ + let newStyle = document.createElement('link'); + newStyle.setAttribute("rel", "stylesheet"); + newStyle.setAttribute("type", "text/css"); + newStyle.setAttribute("href", "https://fonts.googleapis.com/icon?family=Material+Icons"); + document.head.appendChild(newStyle); +}; + +makeFontFace(); + +// 画面のロード完了時にmain関数を呼び出す +window.addEventListener("load", main, false); + +function main(e) { + /** + * 画面の呼び込み完了時に、各話にパーティーアイコン(別のタブで動画を開く)とクラッカーアイコン(パーティールームの作成のためのサイドバーを召喚)を追加する + */ + + item_list = document.querySelectorAll(".itemModule.list a"); + + for (var item of item_list) { + if (item.getAttribute("href").match(/cd_pc/)) { + var get_partid = function (raw_href) { + return raw_href.replace(/[^0-9]/g, ''); + } + var partid = get_partid(item.getAttribute("href")); + + var play_icon = document.createElement("a"); + play_icon.textContent = "play_arrow"; + play_icon.setAttribute("class", "material-icons play-btn"); + play_icon.setAttribute("target", "_blank"); + play_icon.setAttribute("href", "sc_d_pc?partId=" + partid); + item.parentNode.appendChild(play_icon); + + var popper_icon = document.createElement("a"); + popper_icon.textContent = "celebration"; + popper_icon.setAttribute("class", "material-icons popper-btn"); + popper_icon.setAttribute("target", "_blank"); + popper_icon.setAttribute("href", "sc_d_pc?partId=" + partid + "&party=create"); + item.parentNode.appendChild(popper_icon); + } + } + +}; \ No newline at end of file diff --git a/js/d-party/version_check.js b/js/d-party/version_check.js new file mode 100644 index 0000000..786ecd9 --- /dev/null +++ b/js/d-party/version_check.js @@ -0,0 +1,27 @@ +VERSION_CHECK = false; + +/** + * + * getManifest + * @description manifest.jsonから値を取得します。 + * + */ +const getManifestData = data => { + let manifestData = chrome.runtime.getManifest(); + return manifestData[data]; +}; + +request = new XMLHttpRequest(); +request.open('GET', D_PARTY_VERSION_CHECK_ENDPOINT + "?extension-version=" + getManifestData('version'), true); +request.responseType = 'json'; +request.onreadystatechange = function () { + if (request.readyState == 4 && request.status == 200) { + data = this.response; + VERSION_CHECK = data["is_possible"]; + } else { + VERSION_CHECK = false; + } + document.getElementsByClassName("chrome_extension_field")[0].innerText = VERSION_CHECK; +}; + +request.send(); \ No newline at end of file diff --git a/js/library/awesome-notification.js b/js/library/awesome-notification.js new file mode 100644 index 0000000..fdfe09d --- /dev/null +++ b/js/library/awesome-notification.js @@ -0,0 +1 @@ +var AWN; (() => { var t = { 628: (t, e, n) => { "use strict"; function o(t) { return o = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (t) { return typeof t } : function (t) { return t && "function" == typeof Symbol && t.constructor === Symbol && t !== Symbol.prototype ? "symbol" : typeof t }, o(t) } function r(t, e) { if (!(t instanceof e)) throw new TypeError("Cannot call a class as a function") } function i(t, e) { for (var n = 0; n < e.length; n++) { var o = e[n]; o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(t, o.key, o) } } n.d(e, { default: () => I }); var a = { maxNotifications: 10, animationDuration: 300, position: "bottom-right", labels: { tip: "Tip", info: "Info", success: "Success", warning: "Attention", alert: "Error", async: "Loading", confirm: "Confirmation required", confirmOk: "OK", confirmCancel: "Cancel" }, icons: { tip: "question-circle", info: "info-circle", success: "check-circle", warning: "exclamation-circle", alert: "exclamation-triangle", async: "cog fa-spin", confirm: "exclamation-triangle", prefix: "", enabled: !0 }, replacements: { tip: null, info: null, success: null, warning: null, alert: null, async: null, "async-block": null, modal: null, confirm: null, general: { "