Dette scriptet burde ikke installeres direkte. Det er et bibliotek for andre script å inkludere med det nye metadirektivet // @require https://update.greasyfork.ip-ddns.com/scripts/539094/1606939/dav.js
// ==UserScript==
// @name dav
// @namespace dav
// @version 1.0.0
// @description Henry Toys dav api
// @author Henry
// @grant unsafeWindow
// @grant GM_download
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @license CC-BY-4.0
// ==/UserScript==
const dav = {
login: function () {
// 添加样式,使用 !important 确保优先级
GM_addStyle(`
.modal_bg {
position: fixed !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
background-color: rgba(0, 0, 0, 0.5) !important;
justify-content: center !important;
align-items: center !important;
z-index: 9999 !important;
}
.modal_content {
background-color: white !important;
padding: 20px !important;
border-radius: 10px !important;
width: 300px !important;
max-width: 90% !important;
max-height: 80vh !important;
overflow-y: auto !important;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2) !important;
box-sizing: border-box !important;
}
.modal_input {
width: 100% !important;
padding: 8px 10px !important;
border: 1px solid #ddd !important;
border-radius: 5px !important;
display: block !important;
box-sizing: border-box !important;
font-family: inherit !important;
font-size: inherit !important;
line-height: 1.4 !important;
height: 36px !important;
-webkit-appearance: none !important;
-moz-appearance: none !important;
appearance: none !important;
background-color: white !important;
vertical-align: middle !important;
outline: none !important;
margin: 0 0 12px 0 !important;
box-shadow: none !important;
letter-spacing: normal !important;
word-spacing: normal !important;
text-transform: none !important;
text-indent: 0px !important;
text-shadow: none !important;
text-align: start !important;
-webkit-rtl-ordering: logical !important;
cursor: text !important;
}
.modal_input:focus {
border-color: #4CAF50 !important;
box-shadow: 0 0 0 2px rgba(76, 175, 80, 0.2) !important;
}
.button_container {
display: flex !important;
gap: 10px !important;
margin-top: 20px !important;
}
.modal_button {
color: white !important;
border: none !important;
padding: 10px 15px !important;
border-radius: 3px !important;
cursor: pointer !important;
flex: 1 !important;
font-family: inherit !important;
font-size: inherit !important;
}
.modal_button.login {
color: #111 !important;
background-color: #FFD814 !important;
}
.error_msg {
color: red !important;
margin-bottom: 15px !important;
}
`)
// 创建模态框HTML结构
const create_login_modal = function () {
// 检查登录模态框是否已存在
if (document.querySelector('.modal_bg')) {
document.querySelector('.modal_bg').remove()
}
// 创建模态框容器
const modal_bg = document.createElement('div')
modal_bg.className = 'modal_bg'
modal_bg.style.display = 'none'
// 创建模态框内容
const modal_content = document.createElement('div')
modal_content.className = 'modal_content'
const modal_title = document.createElement('h1')
modal_title.textContent = '欢迎登录'
const error_msg = document.createElement('div')
error_msg.className = 'error_msg'
error_msg.style.display = 'none'
const loginurl_input = document.createElement('input')
loginurl_input.type = 'text'
loginurl_input.className = 'modal_input'
loginurl_input.placeholder = '输入网址'
// 添加用户名输入
const username_input = document.createElement('input')
username_input.type = 'text'
username_input.className = 'modal_input'
username_input.placeholder = '输入用户名'
// 添加密码输入
const password_input = document.createElement('input')
password_input.type = 'password'
password_input.className = 'modal_input'
password_input.placeholder = '输入密码'
// 添加按钮容器
const button_container = document.createElement('div')
button_container.className = 'button_container'
// 添加提交按钮
const submit_button = document.createElement('button')
submit_button.type = 'submit'
submit_button.textContent = '登录'
submit_button.className = 'modal_button login'
button_container.appendChild(submit_button)
modal_content.appendChild(modal_title)
modal_content.appendChild(error_msg)
modal_content.appendChild(loginurl_input)
modal_content.appendChild(username_input)
modal_content.appendChild(password_input)
modal_content.appendChild(button_container)
modal_bg.appendChild(modal_content)
document.body.appendChild(modal_bg)
return {
modal_bg,
error_msg,
loginurl_input,
username_input,
password_input,
submit_button,
}
}
// 创建模态框并添加事件处理
const {
modal_bg,
error_msg,
loginurl_input,
username_input,
password_input,
submit_button,
} = create_login_modal()
// 显示模态框
const show_login_modal = function () {
modal_bg.style.display = 'flex'
loginurl_input.focus()
}
// 隐藏模态框
const hide_login_modal = function () {
modal_bg.style.display = 'none'
loginurl_input.value = ''
username_input.value = ''
password_input.value = ''
error_msg.style.display = 'none'
}
// 验证登录
const validate_login = function () {
const loginurl = loginurl_input.value.trim()
const username = username_input.value.trim()
const password = password_input.value.trim()
if (!loginurl || !username || !password) {
error_msg.textContent = '请填写所有字段'
error_msg.style.display = 'block'
return false
}
// 验证URL格式
try { new URL(loginurl) }
catch (e) {
error_msg.textContent = '无效的URL格式'
error_msg.style.display = 'block'
return false
}
try {
GM_xmlhttpRequest({
method: "PROPFIND",
url: loginurl,
headers: { 'Authorization': 'Basic ' + btoa(username + ':' + password), 'Depth': '0' },
// responseType: responseType,
timeout: 10000, // 设置默认超时时间为10秒
onload: async (response) => {
if (response.status == 207) {
console.log('Request successful:', response)
const dav_login_data = {
"loginurl": loginurl,
"username": username,
"password": password,
"expires": new Date().getTime() + 1000 * 60 * 60 * 24 * 7 // 7天后过期
}
error_msg.style.display = 'none'
GM_setValue("dav_login_data", JSON.stringify(dav_login_data))
hide_login_modal()
return true
} else {
GM_deleteValue('dav_login_data')
error_msg.textContent = '登录失败,请检查地址,用户名和密码'
error_msg.style.display = 'block'
return false
}
},
onerror: (error) => {
GM_deleteValue('dav_login_data')
error_msg.textContent = 'Request error:' + error
error_msg.style.display = 'block'
return false
},
ontimeout: () => {
// console.log('Request timed out')
GM_deleteValue('dav_login_data')
error_msg.textContent = 'Request timed out'
error_msg.style.display = 'block'
return false
}
})
} catch (requestError) {
GM_deleteValue('dav_login_data')
error_msg.textContent = 'GM_xmlhttpRequest error:' + requestError
error_msg.style.display = 'block'
return false
}
}
// 添加事件监听
submit_button.addEventListener('click', validate_login)
modal_bg.addEventListener('click', (e) => {
if (e.target === modal_bg) {
hide_login_modal()
}
})
// 添加键盘事件监听器,按ESC键关闭模态框
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && modal_bg.style.display === 'block') {
hide_login_modal()
}
})
loginurl_input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
validate_login()
}
})
username_input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
validate_login()
}
})
password_input.addEventListener('keypress', (e) => {
if (e.key === 'Enter') {
validate_login()
}
})
GM_registerMenuCommand("登录", show_login_modal)
},
logout: function () {
const confirm = window.confirm('确定要退出登录吗?')
if (!confirm) { return }
// 如果用户确认退出登录,则删除登录数据
GM_deleteValue('dav_login_data')
alert('已退出登录')
},
get_login_data: function () {
let dav_login_data = JSON.parse(GM_getValue("dav_login_data", null))
const self = this
// 检查数据是否存在或已过期
if (!dav_login_data ||
!dav_login_data.loginurl ||
!dav_login_data.username ||
!dav_login_data.password ||
dav_login_data.expires < new Date().getTime()) {
GM_deleteValue('dav_login_data')
self.login()
} else {
GM_registerMenuCommand("退出登录", self.logout)
}
return JSON.parse(GM_getValue("dav_login_data", null))
},
request: async function (method, path, callback, responseType = 'text', data = null) {
const login_data = JSON.parse(GM_getValue("dav_login_data", null))
if (!login_data ||
!login_data.loginurl ||
!login_data.username ||
!login_data.password ||
login_data.expires < new Date().getTime()) {
GM_deleteValue('dav_login_data')
this.get_login_data()
throw new Error('Invalid or missing login credentials')
} else {
const updated_dav_login_data = {
"loginurl": login_data.loginurl,
"username": login_data.username,
"password": login_data.password,
"expires": new Date().getTime() + 1000 * 60 * 60 * 24 * 7 // 7天后过期
}
GM_setValue("dav_login_data", JSON.stringify(updated_dav_login_data))
}
return new Promise((resolve) => {
try {
GM_xmlhttpRequest({
method: method,
data: data,
url: login_data.loginurl + path,
headers: { 'Authorization': 'Basic ' + btoa(login_data.username + ':' + login_data.password), },
responseType: responseType,
timeout: 30000, // 设置默认超时时间为30秒
onload: async (response) => { await callback(response, resolve) },
onerror: (error) => {
console.log('Request error:', error)
GM_deleteValue('dav_login_data')
this.get_login_data()
},
ontimeout: () => {
console.log('Request timed out')
GM_deleteValue('dav_login_data')
this.get_login_data()
}
})
} catch (requestError) {
console.log('GM_xmlhttpRequest error:', requestError)
GM_deleteValue('dav_login_data')
this.get_login_data()
}
})
},
get_file_data: function (file_path) {
let callback = function (response, resolve) { resolve(response.responseText) }
return this.request('GET', file_path, callback, 'text')
},
get_file_last_modified_time: function (file_path) {
let callback = function (response, resolve) { resolve(response.responseXML.getElementsByTagName('d:getlastmodified')[0].textContent) }
return this.request('PROPFIND', file_path, callback, 'document', '')
},
upload_file_through_file_data: function (file_path, file_data) {
let callback = function () { }
this.request('PUT', file_path, callback, 'text', file_data)
},
upload_file_through_url: async function (file_path, url) {
function get_url_file_data(url) {
return new Promise((resolve, reject) => {
GM_xmlhttpRequest({
method: "GET",
url: url,
responseType: "arraybuffer",
onload: (response) => { resolve(new Blob([response.response])) },
onerror: () => { reject('error'); }
})
})
}
let file_data = await get_url_file_data(url)
this.upload_file_through_file_data(file_path, file_data)
},
check_path_exists: function (path) {
let callback = function (response, resolve) {
if (response.status === 404) { resolve(false) }
else { resolve(true) }
}
return this.request('PROPFIND', path, callback, 'text', '')
},
create_dir_path: async function (dir_path) {
let callback = function () { }
try {
let dir_exits = await this.check_path_exists(dir_path)
if (dir_exits) { return }
let currentPath = dir_path
while (currentPath) {
dir_exits = await this.check_path_exists(currentPath);
if (dir_exits) { break }
// 尝试创建目录
await this.request('MKCOL', currentPath, callback, 'text', '');
// 截取到上一级目录
const lastIndex = currentPath.lastIndexOf('/');
if (lastIndex === -1) { break }
currentPath = currentPath.slice(0, lastIndex)
}
} catch (error) {
console.error(`Error creating directory ${dir_path}:`, error);
}
},
get_latest_modified_file: async function (dir_path) {
let callback = function (response, resolve) {
if (response.status === 404) { resolve("error") }
else { resolve(response.responseXML.firstChild.lastChild.firstElementChild.innerHTML) }
}
return this.request('PROPFIND', dir_path, callback, 'text', '')
}
}
dav.get_login_data()