This repository has been archived on 2023-08-30. You can view files and clone it, but cannot push or open issues/pull-requests.
appcat
/
sonist
Archived
1
0
Fork 0

Merge branch 'master' of github.com:yutent/sonist

2.x
宇天 2019-09-09 20:21:52 +08:00
commit 691ef9f437
44 changed files with 6361 additions and 291 deletions

View File

@ -1,6 +1,6 @@
{ {
"name": "sonist", "name": "sonist",
"version": "1.1.0", "version": "1.2.0",
"description": "Music Player", "description": "Music Player",
"main": "src/main.js", "main": "src/main.js",
"scripts": { "scripts": {
@ -18,7 +18,7 @@
"iofs": "^1.1.0" "iofs": "^1.1.0"
}, },
"devDependencies": { "devDependencies": {
"electron": "^4.0.2", "electron": "^6.0.0",
"electron-builder": "^20.38.5" "electron-builder": "^20.38.5"
}, },
"build": { "build": {

View File

@ -1,14 +1,13 @@
$ct: #3fc2a7 #19b491 #16967a; $ct: #3fc2a7 #19b491 #16967a;
$cg: #58d68d #2ecc71 #27ae60; $cg: #58d68d #2ecc71 #27ae60;
$cpp: #ac61ce #9b59b6 #8e44ad; $cpp: #ac61ce #9b59b6 #8e44ad;
$cb: #52a3de #2d8dd6 #2776b1; $cb: #66b1ff #409eff #3a8ee6;
$cr: #ff5061 #eb3b48 #ce3742; $cr: #ff5061 #eb3b48 #ce3742;
$co: #ffb618 #f39c12 #e67e22; $co: #ffb618 #f39c12 #e67e22;
$cp: #f3f5fb #e8ebf4 #dae1e9; $cp: #f3f5fb #e8ebf4 #dae1e9;
$cgr: #98acae #8a9b9c #748182; $cgr: #aabac3 #90a3ae #7e909a;
$cd: #62778d #526273 #425064; $cd: #62778d #526273 #425064;
@mixin ts($c: all, $t: .2s, $m: ease-in-out){ @mixin ts($c: all, $t: .2s, $m: ease-in-out){
transition:$c $t $m; transition:$c $t $m;
} }

View File

@ -3,11 +3,9 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<link href="lib/css/reset-basic.css" rel="stylesheet"> <link href="lib/css/reset-basic.css" rel="stylesheet">
<link href="lib/css/elem-ui.css" rel="stylesheet">
<link href="css/common.css" rel="stylesheet"> <link href="css/common.css" rel="stylesheet">
<link href="css/app.css" rel="stylesheet"> <link href="css/app.css" rel="stylesheet">
<link href="css/modules.css" rel="stylesheet"> <link href="css/modules.css" rel="stylesheet">
<script>window.LIBS_BASE_URL = location.origin + '/lib';</script>
<script type="module" src="js/app.js"></script> <script type="module" src="js/app.js"></script>
</head> </head>
<body class="do-fn-noselect" anot="app" :css="{'background-image': coverBG}"> <body class="do-fn-noselect" anot="app" :css="{'background-image': coverBG}">

View File

@ -5,8 +5,8 @@
*/ */
import '/lib/anot.js' import '/lib/anot.js'
import layer from '/lib/layer/index.js' import '/lib/layer/index.js'
import store from '/lib/store/index.js' import '/lib/store/index.js'
import AudioPlayer from '/lib/audio/index.js' import AudioPlayer from '/lib/audio/index.js'
import Lyrics from '/lib/lyrics/index.js' import Lyrics from '/lib/lyrics/index.js'
@ -25,15 +25,14 @@ const log = console.log
const fs = require('iofs') const fs = require('iofs')
const path = require('path') const path = require('path')
const { remote, ipcRenderer, screen } = require('electron') const { remote, ipcRenderer } = require('electron')
const { createDesktopLrcWindow, createMiniWindow } = remote.require( const { createDesktopLrcWindow, createMiniWindow } = remote.require(
'./tools/windows' './tools/windows'
) )
const MAIN_SCREEN = screen.getPrimaryDisplay()
const WIN = remote.getCurrentWindow() const WIN = remote.getCurrentWindow()
const __LRC__ = createDesktopLrcWindow(MAIN_SCREEN) const __LRC__ = createDesktopLrcWindow(screen)
const __MINI__ = createMiniWindow(MAIN_SCREEN) const __MINI__ = createMiniWindow(screen)
const PLAY_MODE = { const PLAY_MODE = {
0: 'all', 0: 'all',
@ -41,6 +40,10 @@ const PLAY_MODE = {
2: 'random' 2: 'random'
} }
// window.onblur = function() {
// location.reload()
// }
// 本地音乐和试用音乐列表 // 本地音乐和试用音乐列表
window.LS = store.collection('local') window.LS = store.collection('local')
window.TS = store.collection('temp') window.TS = store.collection('temp')
@ -48,7 +51,7 @@ window.TS = store.collection('temp')
window.SONIST = new AudioPlayer() window.SONIST = new AudioPlayer()
window.LYRICS = new Lyrics() window.LYRICS = new Lyrics()
let appInit = ipcRenderer.sendSync('get-init') let appInit = ipcRenderer.sendSync('sonist', { type: 'get-init' })
Anot.ss('app-init', appInit) Anot.ss('app-init', appInit)

View File

@ -95,13 +95,17 @@ export default {
SONIST.clear() SONIST.clear()
SONIST.push(LS.getAll()) SONIST.push(LS.getAll())
ipcRenderer.send('set-music', LS.getAll()) ipcRenderer.send('sonist', { type: 'set-music', data: LS.getAll() })
this.updateCurr(song) this.updateCurr(song)
this.draw(true) this.draw(true)
} }
ipcRenderer.send('save-lrc', { id, lrc: json.lyrics }) ipcRenderer.send('sonist', {
type: 'save-lrc',
id,
data: json.lyrics
})
LYRICS.__init__(id) LYRICS.__init__(id)
layer.toast('歌词应用成功...') layer.toast('歌词应用成功...')

View File

@ -34,7 +34,7 @@ export default Anot({
}, },
mounted() { mounted() {
appInit = JSON.parse(Anot.ss('app-init')) appInit = JSON.parse(Anot.ss('app-init'))
dbCache = ipcRenderer.sendSync('get-music') dbCache = ipcRenderer.sendSync('sonist', { type: 'get-music' })
LS.insert(dbCache) LS.insert(dbCache)
@ -112,11 +112,15 @@ export default Anot({
this.__APP__.updateCurr(it) this.__APP__.updateCurr(it)
this.__APP__.draw(true) this.__APP__.draw(true)
ipcRenderer.send('save-lrc', { ipcRenderer.send('sonist', {
type: 'save-lrc',
id: it.id, id: it.id,
lrc: json.lyrics data: json.lyrics
})
ipcRenderer.send('sonist', {
type: 'set-music',
data: LS.getAll()
}) })
ipcRenderer.send('set-music', LS.getAll())
LYRICS.__init__(it.id) LYRICS.__init__(it.id)
}) })
@ -145,7 +149,7 @@ export default Anot({
SONIST.clear() SONIST.clear()
SONIST.push(dbCache) SONIST.push(dbCache)
ipcRenderer.send('set-music', dbCache) ipcRenderer.send('sonist', { type: 'set-music', data: dbCache })
dbCache = null dbCache = null
layer.toast(`刷新缓存完成,新增${this.__NEW_NUM__}`) layer.toast(`刷新缓存完成,新增${this.__NEW_NUM__}`)
@ -188,7 +192,10 @@ export default Anot({
return return
} }
if (appInit.musicPath) { if (appInit.musicPath) {
this.__LIST__ = ipcRenderer.sendSync('scan-dir', appInit.musicPath) this.__LIST__ = ipcRenderer.sendSync('sonist', {
type: 'scan-dir',
path: appInit.musicPath
})
if (this.__LIST__) { if (this.__LIST__) {
this.__tmp__ = [] //创建一个临时的数组, 用于存放扫描的音乐 this.__tmp__ = [] //创建一个临时的数组, 用于存放扫描的音乐
this.__APP__.loading = true this.__APP__.loading = true
@ -235,7 +242,7 @@ export default Anot({
SONIST.clear() SONIST.clear()
SONIST.push(LS.getAll()) SONIST.push(LS.getAll())
ipcRenderer.send('set-music', LS.getAll()) ipcRenderer.send('sonist', { type: 'set-music', data: LS.getAll() })
}, },
handleMenu(it, idx, ev) { handleMenu(it, idx, ev) {
let that = this let that = this
@ -278,7 +285,10 @@ export default Anot({
SONIST.clear() SONIST.clear()
SONIST.push(LS.getAll()) SONIST.push(LS.getAll())
ipcRenderer.send('set-music', LS.getAll()) ipcRenderer.send('sonist', {
type: 'set-music',
data: LS.getAll()
})
} }
) )
} else { } else {

View File

@ -6,7 +6,9 @@
'use strict' 'use strict'
import '/lib/form/index.js' import '/lib/form/input.js'
import '/lib/form/button.js'
import '/lib/form/select.js'
const { const {
remote: { app, dialog }, remote: { app, dialog },
@ -15,7 +17,7 @@ const {
const log = console.log const log = console.log
let appInit = ipcRenderer.sendSync('get-init') let appInit = ipcRenderer.sendSync('sonist', { type: 'get-init' })
export default Anot({ export default Anot({
$id: 'profile', $id: 'profile',
@ -24,7 +26,8 @@ export default Anot({
allowPlayOnBack: appInit.allowPlayOnBack, allowPlayOnBack: appInit.allowPlayOnBack,
autoLrc: appInit.autoLrc, autoLrc: appInit.autoLrc,
theme: appInit.theme || 1, theme: appInit.theme || 1,
musicPath: appInit.musicPath || '' musicPath: appInit.musicPath || '',
allowGS: !!appInit.allowGS
}, },
version: app.getVersion(), version: app.getVersion(),
cmd: process.platform === 'darwin' ? '⌘' : 'Ctrl', cmd: process.platform === 'darwin' ? '⌘' : 'Ctrl',
@ -34,6 +37,15 @@ export default Anot({
'setting.theme'(v) { 'setting.theme'(v) {
v = +v v = +v
this.__APP__.theme = v this.__APP__.theme = v
},
'setting.allowGS'(v) {
if (v) {
log('++++++++++++')
ipcRenderer.send('sonist', { type: 'enable-gs' })
} else {
log('-----------')
ipcRenderer.send('sonist', { type: 'disable-gs' })
}
} }
}, },
methods: { methods: {
@ -58,7 +70,7 @@ export default Anot({
Object.assign(appInit, setting) Object.assign(appInit, setting)
ipcRenderer.send('set-init', appInit) ipcRenderer.send('sonist', { type: 'set-init', data: appInit })
Anot.ss('app-init', appInit) Anot.ss('app-init', appInit)

View File

@ -23,7 +23,7 @@ export default Anot({
list: [] // 搜索结果列表 list: [] // 搜索结果列表
}, },
mounted() { mounted() {
dict.audition = ipcRenderer.sendSync('get-temp') dict.audition = ipcRenderer.sendSync('sonist', { type: 'get-temp' })
TS.insert(dict.audition) TS.insert(dict.audition)
this.__APP__ = Anot.vmodels.app this.__APP__ = Anot.vmodels.app
@ -91,14 +91,18 @@ export default Anot({
song.cover = json.img song.cover = json.img
ipcRenderer.send('save-lrc', { id: song.id, lrc: json.lyrics }) ipcRenderer.send('sonist', {
type: 'save-lrc',
id: song.id,
data: json.lyrics
})
request.get(json.play_url, { dataType: 'arraybuffer' }).then(res => { request.get(json.play_url, { dataType: 'arraybuffer' }).then(res => {
song.path = ipcRenderer.sendSync('save-cache', { song.path = ipcRenderer.sendSync('sonist', {
buff: Buffer.from(res.body), type: 'save-cache',
data: Buffer.from(res.body),
file: song.kgHash file: song.kgHash
}) })
log(song)
TS.insert(song) TS.insert(song)
dict.audition.push(song) dict.audition.push(song)
@ -108,7 +112,7 @@ export default Anot({
this.curr = it.id this.curr = it.id
}) })
ipcRenderer.send('set-temp', TS.getAll()) ipcRenderer.send('sonist', { type: 'set-temp', data: TS.getAll() })
}) })
}) })
}, },

File diff suppressed because one or more lines are too long

View File

@ -1 +1,15 @@
"use strict";function arraySum(t){var e=0;return t.forEach(function(t){e+=+t}),e}export const create=(t,e)=>{if(!t)return this.defafultImg;(!e||e<100)&&(e=100);var l,a=document.createElement("canvas"),r=a.getContext("2d"),c=t.slice(-3),i=t.slice(-9,-6),f=t.slice(0,8).match(/([\w]{1})/g),n=t.slice(8,16).match(/([\w]{1})/g),s=t.slice(16,24).match(/([\w]{1})/g),m=e/10;a.width=e,a.height=e,f=f.map(t=>(t=parseInt(t,16))%8),n=n.map(t=>(t=parseInt(t,16))%4),s=s.map(t=>(t=parseInt(t,16))%4),l=arraySum(f)>32?c:i,r.fillStyle="#"+c,r.fillRect(0,0,e,e);for(var u=1;u<9;u++){var o=f[u-1],h=n[u-1],p=s[u-1];o+h>8&&(o=8-h),r.fillStyle="#"+i,r.fillRect((h+1)*m,u*m,o*m,m),r.fillStyle="#"+i,r.fillRect((9-h-o)*m,u*m,o*m,m),r.fillStyle="#"+l,r.fillRect((p+1)*m,u*m,m,m),r.fillStyle="#"+l,r.fillRect((8-p)*m,u*m,m,m)}return a.toDataURL()}; /**
*
* @authors yutent (yutent@doui.cc)
* @date 2019-09-01 23:16:06
* @version v2.0.1
*
*/
'use strict'
const COLORS=["#3fc2a7","#58d68d","#ac61ce","#66b1ff","#ff5061","#ffb618","#62778d"],COLOR_NAME=["teal","green","purple","blue","red","orange","dark"];var canvas=document.createElement("canvas"),ctx=canvas.getContext("2d"),size=500;function sum(e){var t=0;return e.forEach(e=>t+=e),t}function createImage(e){if(!e)return"./def.jpg";var t,s=COLORS[parseInt(e.slice(-1),16)%7],r=e.slice(0,8).split(""),i=e.slice(8,16).split(""),o=e.slice(16,24).split(""),a=size/10;r=r.map(e=>(e=parseInt(e,16))%8),i=i.map(e=>(e=parseInt(e,16))%4),o=o.map(e=>(e=parseInt(e,16))%4),t=sum(r)>32?s:"#fff",ctx.fillStyle=s,ctx.fillRect(0,0,size,size);for(var c=1;c<9;c++){var l=r[c-1],n=i[c-1],h=o[c-1];l+n>8&&(l=8-n),ctx.fillStyle="#fff",ctx.fillRect((n+1)*a,c*a,l*a,a),ctx.fillStyle="#fff",ctx.fillRect((9-n-l)*a,c*a,l*a,a),ctx.fillStyle=t,ctx.fillRect((h+1)*a,c*a,a,a),ctx.fillStyle=t,ctx.fillRect((8-h)*a,c*a,a,a)}return canvas.toDataURL("image/webp")}canvas.width=size,canvas.height=size;export default class Avatar extends HTMLElement{static get observedAttributes(){return["hash","src","fit"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{hash:"",src:"",fit:""},writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML="<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{overflow:hidden;display:flex;align-items:center;justify-content:center;width:var(--size, 32px);height:var(--size, 32px);line-height:1;font-size:var(--size, 32px);border-radius:4px;color:#fff;user-select:none;-moz-user-select:none}:host img{display:none;width:100%;height:100%}:host slot{display:block;transform:scale(0.8)}:host([src]) slot,:host([hash]) slot{display:none}:host([src]) img,:host([hash]) img{display:block}:host([color='red']){background:#ff5061}:host([color='teal']){background:#3fc2a7}:host([color='purple']){background:#ac61ce}:host([color='green']){background:#58d68d}:host([color='orange']){background:#ffb618}:host([color='dark']){background:#62778d}:host([color='blue']){background:#66b1ff}:host([size='large']){width:50px;height:50px;font-size:36px}:host([size='medium']){width:40px;height:40px;font-size:28px}:host([size='mini']){width:24px;height:24px;font-size:16px}:host([circle]){border-radius:50%}\n</style> <slot></slot> <img /> ",this.__IMG__=this.root.lastElementChild;var e=this.textContent.slice(0,1);this.setAttribute("color",COLOR_NAME[e.charCodeAt(0)%7]),this.textContent=e}attributeChangedCallback(e,t,s){if(t!==s)switch(e){case"src":this.removeAttribute("hash"),this.__IMG__.src=s||"./def.jpg";break;case"hash":this.removeAttribute("src"),this.__IMG__.src=createImage(s);break;case"fit":s&&(this.__IMG__.style["object-fit"]=s)}}};
if(!customElements.get('wc-avatar')){
customElements.define('wc-avatar', Avatar)
}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
.do-pager{display:block;height:auto;text-align:center;font-size:14px;color:#8a9b9c}.do-pager.mini{line-height:30px}.do-pager.mini .button,.do-pager.mini .page{min-width:30px;height:30px}.do-pager.medium{line-height:35px}.do-pager.medium .button,.do-pager.medium .page{min-width:35px;height:35px}.do-pager.large{line-height:40px}.do-pager.large .button,.do-pager.large .page{min-width:40px;height:40px}.do-pager .button,.do-pager .page{display:inline-block;border:0;color:#8a9b9c;text-decoration:none;cursor:pointer;vertical-align:top;font-size:14px;font-weight:100;-webkit-appearance:none;-moz-appearance:none;appearance:none}.do-pager .button{font-size:18px}.do-pager .curr,.do-pager .disabled{cursor:default}.do-pager.skin-1{width:100%}.do-pager.skin-1 .page,.do-pager.skin-1 .button,.do-pager.skin-1 .disabled,.do-pager.skin-1 .curr{padding:0 8px;margin:0 3px}.do-pager.skin-1 .curr{font-weight:bold;font-size:15px}.do-pager.skin-1 .page.disabled{min-width:0;padding:0;background:none;color:#8a9b9c}.do-pager.skin-1 .page.disabled:hover,.do-pager.skin-1 .page.disabled:active{background:none}.do-pager.skin-1 .page.curr{background:none;color:#8a9b9c}.do-pager.skin-1 .page.curr:hover,.do-pager.skin-1 .page.curr:active{background:none}.do-pager.skin-1 .button[disabled]{cursor:not-allowed}.do-pager.skin-1 .total-box,.do-pager.skin-1 .input-box{display:inline-block;padding:0 8px}.do-pager.skin-1 .input-box input{display:inline-block;width:40px;height:30px;padding:0 3px;font-size:14px;background:#fff;border:1px solid #ddd;text-align:center}.do-pager.skin-2{float:right;width:auto}.do-pager.skin-2 .page,.do-pager.skin-2 .button,.do-pager.skin-2 .disabled,.do-pager.skin-2 .curr{float:left;margin:0;padding:0 5px;color:#fff}.do-pager.skin-2 .page.disabled{display:none}.do-pager.skin-2 .button[disabled]{cursor:not-allowed}.do-pager.skin-2 .input-box{display:none}.do-pager.skin-2 .total-box{float:left;display:inline-block;padding:0 8px}.do-pager.plain .page,.do-pager.plain .button{background:#e8ebf4;color:#8a9b9c}.do-pager.plain .page:hover,.do-pager.plain .button:hover{background:#f3f5fb}.do-pager.plain .page:active,.do-pager.plain .button:active{background:#dae1e9}.do-pager.plain .button[disabled]{background:#e8ebf4}.do-pager.plain.skin-2 .curr{background:#dae1e9}.do-pager.grey .page,.do-pager.grey .button{background:#8a9b9c;color:#fff}.do-pager.grey .page:hover,.do-pager.grey .button:hover{background:#98acae}.do-pager.grey .page:active,.do-pager.grey .button:active{background:#748182}.do-pager.grey .button[disabled]{background:#8a9b9c}.do-pager.grey.skin-2 .curr{background:#748182}.do-pager.dark .page,.do-pager.dark .button{background:#526273;color:#fff}.do-pager.dark .page:hover,.do-pager.dark .button:hover{background:#526273}.do-pager.dark .page:active,.do-pager.dark .button:active{background:#425064}.do-pager.dark .button[disabled]{background:#526273}.do-pager.dark.skin-2 .curr{background:#425064}.do-pager.red .page,.do-pager.red .button{background:#eb3b48;color:#fff}.do-pager.red .page:hover,.do-pager.red .button:hover{background:#ff5061}.do-pager.red .page:active,.do-pager.red .button:active{background:#ce3742}.do-pager.red .button[disabled]{background:#eb3b48}.do-pager.red.skin-2 .curr{background:#ce3742}.do-pager.orange .page,.do-pager.orange .button{background:#f39c12;color:#fff}.do-pager.orange .page:hover,.do-pager.orange .button:hover{background:#ffb618}.do-pager.orange .page:active,.do-pager.orange .button:active{background:#e67e22}.do-pager.orange .button[disabled]{background:#f39c12}.do-pager.orange.skin-2 .curr{background:#e67e22}.do-pager.green .page,.do-pager.green .button{background:#2ecc71;color:#fff}.do-pager.green .page:hover,.do-pager.green .button:hover{background:#58d68d}.do-pager.green .page:active,.do-pager.green .button:active{background:#27ae60}.do-pager.green .button[disabled]{background:#2ecc71}.do-pager.green.skin-2 .curr{background:#27ae60}.do-pager.teal .page,.do-pager.teal .button{background:#19b491;color:#fff}.do-pager.teal .page:hover,.do-pager.teal .button:hover{background:#3fc2a7}.do-pager.teal .page:active,.do-pager.teal .button:active{background:#16967a}.do-pager.teal .button[disabled]{background:#19b491}.do-pager.teal.skin-2 .curr{background:#16967a}.do-pager.blue .page,.do-pager.blue .button{background:#2d8dd6;color:#fff}.do-pager.blue .page:hover,.do-pager.blue .button:hover{background:#52a3de}.do-pager.blue .page:active,.do-pager.blue .button:active{background:#2776b1}.do-pager.blue .button[disabled]{background:#2d8dd6}.do-pager.blue.skin-2 .curr{background:#2776b1}.do-pager.purple .page,.do-pager .button .page{background:#9b59b6;color:#fff}.do-pager.purple .page:hover,.do-pager .button .page:hover{background:#ac61ce}.do-pager.purple .page:active,.do-pager .button .page:active{background:#8e44ad}.do-pager.purple .button[disabled],.do-pager .button .button[disabled]{background:#9b59b6}.do-pager.purple.skin-2 .curr,.do-pager .button.skin-2 .curr{background:#8e44ad}

File diff suppressed because one or more lines are too long

1
src/lib/drag/core.js Normal file
View File

@ -0,0 +1 @@
"use strict";import{bind,unbind}from"../utils.js";const DEF_OPT={axis:"",limit:!1,overflow:!0};export default class Drag{constructor(t){this.$elem=t,this._init()}_init(){this.$elem.style.transform="";var{x:t,y:s}=this.$elem.getBoundingClientRect();this.pos={x:t,y:s,_x:0,_y:0}}by(t,s={}){return this.$drag=t,this.opt=Object.assign(Object.create(null),DEF_OPT,s),!1!==this.opt.limit&&(this.opt.overflow=!1),t.style.cursor="move",this._handleResize=bind(window,"resize",this._init.bind(this)),this._handleMousedown=bind(t,"mousedown",t=>{if(this.disabled)return;var s=this.$elem.getBoundingClientRect();s.x-this.pos._x!==this.pos.x&&(this.pos.x=s.x-this.pos._x),s.y-this.pos._y!==this.pos.y&&(this.pos.y=s.y-this.pos._y);let e=t.pageX,i=t.pageY,o=document.documentElement.clientWidth,n=document.documentElement.clientHeight,h=s.width,d=s.height,p=[0,o-h,n-d,0];if("parent"===this.opt.limit){let t=this.$elem.parentNode.getBoundingClientRect();p=[t.top,t.right-h,t.bottom-d,t.left]}let l=bind(document,"mousemove",t=>{t.preventDefault();let o=t.pageX-e+(s.x-this.pos.x),n=t.pageY-i+(s.y-this.pos.y);"x"===this.opt.axis&&(n=0),"y"===this.opt.axis&&(o=0),!1===this.opt.overflow&&(o<p[3]-this.pos.x?o=p[3]-this.pos.x:o>p[1]-this.pos.x&&(o=p[1]-this.pos.x),n<p[0]-this.pos.y?n=p[0]-this.pos.y:n>p[2]-this.pos.y&&(n=p[2]-this.pos.y)),this.pos._x=o,this.pos._y=n,this.$elem.dispatchEvent(new CustomEvent("dragging",{detail:{offset:{x:this.pos.x+o,y:this.pos.y+n},move:{x:o,y:n}}})),this.$elem.style.transform=`translate(${o}px, ${n}px)`}),m=bind(document,"mouseup",t=>{this.$elem.dispatchEvent(new CustomEvent("dragged",{detail:{offset:{x:this.pos.x+this.pos._x,y:this.pos.y+this.pos._y},move:{x:this.pos._x,y:this.pos._y}}})),unbind(document,"mousemove",l),unbind(document,"mouseup",m)})}),this}on(t,s){if(t&&"function"==typeof s)return bind(this,t,s)}off(t,s){unbind(this,t,s)}destroy(){unbind(window,"resize",this._handleResize),unbind(this.$drag,"mousedown",this._handleMousedown),delete this.$elem,delete this.$drag}};

View File

@ -1,85 +0,0 @@
# 拖拽插件
> 该插件可以让任意一个元素可以被拖拽,而不需要该元素是否具有定位属性。
> 使用时,在目标元素上添加`:drag`属性即可以实现拖拽功能。
## 依赖
> 依赖`Anot`框架
## 浏览器兼容性
+ chrome
+ firefox
+ safari
+ IE10+
## 用法
> 只需要在要拖拽的元素上添加`:drag`即可;
> 如果要拖拽的元素不是当前元素,只需要给该属性增加一个值为想要拖拽元素的类名或ID。
> 具体请看示例:
> **注意:** `拖拽的元素不是本身时,只会往父级一级一级找相匹配的`
```html
<!DOCTYPE html>
<html>
<head>
<style>
* {margin:0;padding:0}
.box {width:200px;height:100px;background:#aaa;}
.box .handle {width:200px;height:30px;background:#f30;}
</style>
</head>
<body :controller="test">
<div class="box" :drag></div>
<div class="box">
<div class="handle" :drag="box"></div>
</div>
<script>
import Anot from 'lib/drag/index.js'
Anot({
$id: 'test'
})
</script>
</body>
</html>
```
## 额外参数
### `data-limit`
> 用于限制元素的拖动范围,默认没有限制。 可选值为 "window"和"parent", 分别为 "限制在可视区"和"限制在父级元素的范围"
### `data-axis`
> 用于限制拖动的方向, 默认值为 "xy",即不限制方向。可选值为 "x"和"y", 即只能在"x轴"或"y轴"方向拖动。
### `data-beforedrag`
> 拖动前的回调,如果有设置回调方法, 则该回调的返回值,可决定该元素是否能被拖拽, 可用于在特殊场景下,临时禁用拖拽。
> `注:`
> 1. 该回调方法,会传入3个参数, 第1个为被拖拽的元素(dom对象), 第2个参数为 该元素的x轴绝对坐标, 第3个元素为y轴绝对坐标;
> 2. 该回调方法, 返回false时, 本次拖拽将临时失效, 返回其他值,或没有返回值,则忽略。
### `data-dragging`
> 元素被拖动时的回调。
> `注:`
> 1.该回调方法,会传入3个参数, 第1个为被拖拽的元素(dom对象), 第2个参数为 该元素的x轴绝对坐标, 第3个元素为y轴绝对坐标;
### `data-dragged`
> 元素被拖动结束后的回调。
> `注:`
> 1. 该回调方法,会传入3个参数, 第1个为被拖拽的元素(dom对象), 第2个参数为 该元素的x轴绝对坐标, 第3个元素为y轴绝对坐标;

View File

@ -1 +1 @@
"use strict";function getBindingCallback(e,t,i){var n=e.getAttribute(t);if(n)for(var a,o=0;a=i[o++];)if(a.hasOwnProperty(n)&&"function"==typeof a[n])return a[n]}Anot.ui.drag="1.0.0",Anot.directive("drag",{priority:1500,init:function(e){e.expr='"'+e.expr+'"';let t=document.documentMode?"move":"grab";window.sidebar?t="-moz-"+t:window.chrome&&(t="-webkit-"+t),Anot(e.element).css("cursor",t),e.beforedrag=getBindingCallback(e.element,"data-beforedrag",e.vmodels),e.dragging=getBindingCallback(e.element,"data-dragging",e.vmodels),e.dragged=getBindingCallback(e.element,"data-dragged",e.vmodels),e.overflow=!0,e.axis="xy",e.element.dataset.axis&&(e.axis=e.element.dataset.axis,delete e.element.dataset.axis),e.limit=!1,e.element.dataset.limit&&(e.limit=e.element.dataset.limit,e.overflow=!1,delete e.element.dataset.limit),delete e.element.dataset.beforedrag,delete e.element.dataset.dragging,delete e.element.dataset.dragged},update:function(e){let t,i,n,a,o,l,r,d,s,g,m,u,c,f,p,b=this,x=e?this.element.parentNode:this.element,v=Anot(this.element),h=Anot(document),w=null,y=null;for(;e&&x&&(x.classList||Anot.error(`${this.name}=${this.expr}, 解析异常[元素不存在]`),!x.classList.contains(e)&&x.id!==e);)x=x.parentNode;w=Anot(x),"parent"===this.limit&&(y=x.parentNode),v.bind("mousedown",function(e){let v=getComputedStyle(x),A=v.transform.replace(/matrix\((.*)\)/,"$1"),C=w.offset();if("0s"!==v.transitionDuration&&(p=v.transitionDuration,x.style.transitionDuration="0s"),(A="none"!==A?A.split(", "):[1,0,0,1,0,0])[4]-=0,A[5]-=0,t=A[4],i=A[5],c=h.scrollTop(),f=h.scrollLeft(),o=C.left-t-f,l=C.top-i-c,n=e.pageX,a=e.pageY,m=window.innerWidth,u=window.innerHeight,s=x.clientWidth,g=x.clientHeight,b.beforedrag){if(!1===b.beforedrag.call(b.vmodels[0],x,o+t,l+i))return}let k=[0,u-g,0,m-s];if("parent"===b.limit){let e=getComputedStyle(y).transform.replace(/matrix\((.*)\)/,"$1"),t=Anot(y).offset();e="none"!==e?e.split(", "):[1,0,0,1,0,0];let i=t.left-e[4]-f,n=t.top-e[5]-c;k=[n,n+y.clientHeight-g,i,i+y.clientWidth-s]}let D=h.bind("mousemove",function(e){e.preventDefault(),"y"!==b.axis&&(A[4]=e.pageX-n+t),"x"!==b.axis&&(A[5]=e.pageY-a+i),r=o+A[4],d=l+A[5],b.overflow||("y"!==b.axis&&(r<=k[2]&&(r=k[2],A[4]=r-o),r>=k[3]&&(r=k[3],A[4]=r-o)),"x"!==b.axis&&(d<=k[0]&&(d=k[0],A[5]=d-l),d>=k[1]&&(d=k[1],A[5]=d-l))),w.css({transform:"matrix("+A.join(", ")+")"}),b.dragging&&b.dragging.call(b.vmodels[0],x,r,d)}),B=h.bind("mouseup",function(e){h.unbind("mousemove",D),h.unbind("mouseup",B),x.style.transitionDuration=p,b.dragged&&b.dragged.call(b.vmodels[0],x,r,d,A[4],A[5])})})}}); "use strict";import Drag from"./core.js";Anot.directive("drag",{priority:1500,init:function(e){e.expr='"'+e.expr+'"',e.overflow=!0,e.axis="xy",e.element.dataset.axis&&(e.axis=e.element.dataset.axis,delete e.element.dataset.axis),e.limit=!1,e.element.dataset.limit&&(e.limit=e.element.dataset.limit,e.overflow=!1,delete e.element.dataset.limit)},update:function(e){var t=this.element;if(e)for(t=this.element.parentNode;t&&(t.classList||Anot.error(`${this.name}=${this.expr}, 解析异常[元素不存在]`),!t.classList.contains(e)&&t.id!==e);)t=t.parentNode;new Drag(t).by(this.element,{limit:this.limit,axis:this.axis,overflow:this.overflow})}});

15
src/lib/form/button.js Normal file

File diff suppressed because one or more lines are too long

15
src/lib/form/checkbox.js Normal file
View File

@ -0,0 +1,15 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2019-09-07 23:35:03
* @version v2.0.1
*
*/
'use strict'
import"../icon/index.js";import{bind,unbind}from"../utils.js";export default class Checkbox extends HTMLElement{static get observedAttributes(){return["label","color","value","checked","readonly","disabled"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{label:"",color:"",value:[],checked:!1,readonly:!1,disabled:!1},writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML="<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:inline-block;line-height:1;font-size:14px}:host label{display:flex;justify-content:center;align-items:center;min-width:32px;height:32px;padding:0 5px;user-select:none;-moz-user-select:none;cursor:inherit;color:#7e909a}:host .dot{--size: 18px;padding:2px;margin-right:3px}:host([readonly]){opacity:0.8}:host([disabled]){cursor:not-allowed;opacity:0.6}:host([size='large']){font-size:16px}:host([size='large']) label{height:42px}:host([size='large']) .dot{--size: 22px}:host([size='medium']) label{height:38px}:host([size='medium']) .dot{--size: 20px}:host([size='mini']){font-size:12px}:host([size='mini']) label{height:20px}:host([size='mini']) .dot{--size: 14px}:host([color='red']) label.checked{color:#ff5061}:host([color='red']) label.checked .dot{border-color:#ff5061}:host([color='red']) label.checked .dot::after{background:#ff5061}:host([color='blue']) label.checked{color:#66b1ff}:host([color='blue']) label.checked .dot{border-color:#66b1ff}:host([color='blue']) label.checked .dot::after{background:#66b1ff}:host([color='green']) label.checked{color:#58d68d}:host([color='green']) label.checked .dot{border-color:#58d68d}:host([color='green']) label.checked .dot::after{background:#58d68d}:host([color='teal']) label.checked{color:#3fc2a7}:host([color='teal']) label.checked .dot{border-color:#3fc2a7}:host([color='teal']) label.checked .dot::after{background:#3fc2a7}:host([color='orange']) label.checked{color:#ffb618}:host([color='orange']) label.checked .dot{border-color:#ffb618}:host([color='orange']) label.checked .dot::after{background:#ffb618}:host([color='dark']) label.checked{color:#62778d}:host([color='dark']) label.checked .dot{border-color:#62778d}:host([color='dark']) label.checked .dot::after{background:#62778d}:host([color='purple']) label.checked{color:#ac61ce}:host([color='purple']) label.checked .dot{border-color:#ac61ce}:host([color='purple']) label.checked .dot::after{background:#ac61ce}\n</style> <label> <wc-icon class=\"dot\" is=\"checkbox-off\"></wc-icon> <slot></slot> </label> ",this.__SWITCH__=this.root.lastElementChild,this.__ICO__=this.__SWITCH__.children[0]}get value(){return this.props.value}set value(e){if(!Array.isArray(e))throw TypeError(":duplex指令需要传入一个数组, 当前为: "+typeof e);this.props.value=e,this.checked=this.props.value.includes(this.props.label)}get checked(){return this.props.checked}set checked(e){this.props.checked=!!e;var{value:o,checked:t,label:l,color:r}=this.props;this.__SWITCH__.classList.toggle("checked",t),this.__ICO__.setAttribute("is","checkbox-"+(t?"on":"off"));var c=o.indexOf(l);t?(this.__ICO__.setAttribute("color",r),c<0&&o.push(l)):(this.__ICO__.removeAttribute("color"),~c&&o.splice(c,1))}get readonly(){return this.props.readonly}set readonly(e){var o=typeof e;e!==this.props.readonly&&("boolean"===o&&e||"boolean"!==o?(this.props.readonly=!0,this.setAttribute("readonly","")):(this.props.readonly=!1,this.removeAttribute("readonly")))}get disabled(){return this.props.disabled}set disabled(e){var o=typeof e;e!==this.props.disabled&&("boolean"===o&&e||"boolean"!==o?(this.props.disabled=!0,this.setAttribute("disabled","")):(this.props.disabled=!1,this.removeAttribute("disabled")))}connectedCallback(){this._handlClick=bind(this,"click",e=>{e.preventDefault(),this.disabled||this.readonly||(this.checked=!this.checked,this.dispatchEvent(new CustomEvent("input")))})}disconnectedCallback(){unbind(this,"click",this._handlClick)}attributeChangedCallback(e,o,t){if(null!==t&&o!==t)switch(e){case"label":case"color":this.props[e]=t;break;case"checked":case"readonly":case"disabled":this[e]=!0}}};
if(!customElements.get('wc-checkbox')){
customElements.define('wc-checkbox', Checkbox)
}

View File

@ -1 +0,0 @@
importCss("/css/form.css");const log=console.log;Anot.ui.form="0.1.0",Anot.component("button",{__init__(e,s,t){s.text=this.text(),s.style={"border-radius":e.radius},this.classList.add("do-fn-noselect"),this.classList.add("do-button"),this.classList.add(e.color||"grey"),this.setAttribute(":click","onClick"),this.setAttribute(":class","{disabled: disabled}"),this.setAttribute(":css","style"),e.size&&this.classList.add(e.size),e.hasOwnProperty("disabled")&&(s.disabled=!0),delete e.disabled,delete e.color,delete e.size,t()},render(e){let s="";return this.props.icon&&(s=`<i class="do-button__icon do-icon-${this.props.icon}"></i>`),`${s}<span class="do-button__text" :text="text"></span>`},state:{text:"",disabled:!1,style:{}},props:{click:Anot.PropsTypes.isFunction()},skip:["style"],watch:{},methods:{onClick(){this.disabled||"function"==typeof this.props.click&&this.props.click(this.props.prop)}}}),Anot.component("radio",{__init__(e,s,t){e.hasOwnProperty("disabled")&&(s.disabled=!0),e.hasOwnProperty("checked")&&null===s.value&&(s.value=e.label),s.text=this.text(),s.checked=s.value===e.label,this.classList.add("do-radio"),this.classList.add("do-fn-noselect"),this.classList.add(e.color||"grey"),this.setAttribute(":class","{disabled: disabled, checked: checked}"),this.setAttribute(":click","onClick"),delete e.disabled,delete e.color,t()},render:()=>'\n <span class="do-radio__box"></span>\n <span class="do-radio__text" :text="text"></span>\n ',state:{value:null,text:"",checked:!1,disabled:!1},props:{label:""},watch:{value(e){this.checked=this.props.label===e}},methods:{onClick(){this.disabled||this.checked||(this.checked=!0,this.value=this.props.label)}}}),Anot.component("switch",{__init__(e,s,t){e.hasOwnProperty("disabled")&&(s.disabled=!0),e.hasOwnProperty("checked")&&null===s.value&&(s.value=!0),s.value=!!s.value,this.classList.add("do-switch"),this.classList.add("do-fn-noselect"),this.classList.add(e.color||"grey"),this.setAttribute(":class","{disabled: disabled, checked: value}"),this.setAttribute(":click","onClick"),delete e.disabled,delete e.color,t()},render:()=>'\n <span class="do-switch__label"><i class="do-switch__dot"></i></span>\n ',state:{value:null,disabled:!1},methods:{onClick(){this.disabled||(this.value=!this.value)}}}),Anot.component("checkbox",{__init__(e,s,t){Array.isArray(s.value)||(this.parentNode.removeChild(this),Anot.error("多选框的传入值必须一个数组",TypeError)),e.hasOwnProperty("disabled")&&(s.disabled=!0),e.hasOwnProperty("checked")&&Anot.Array.ensure(s.value,e.label),s.text=this.text(),s.checked=s.value.indexOf(e.label)>-1,this.classList.add("do-checkbox"),this.classList.add("do-fn-noselect"),this.classList.add(e.color||"grey"),this.setAttribute(":class","{disabled: disabled, checked: checked}"),this.setAttribute(":click","onClick"),delete e.disabled,delete e.color,t()},render:()=>'\n <span class="do-checkbox__box">\n <i class="do-icon-get" :visible="checked"></i>\n </span>\n <span class="do-checkbox__text" :text="text"></span>\n ',state:{value:[],text:"",checked:!1,disabled:!1},props:{label:""},watch:{"value.*"(e,s,t,i){this.checked=this.value.indexOf(this.props.label)>-1},"value.length"(e,s,t,i){this.checked=this.value.indexOf(this.props.label)>-1},value(e,s,t,i){this.checked=this.value.indexOf(this.props.label)>-1}},methods:{onClick(){if(this.disabled)return;let{label:e}=this.props,s=this.value.$model;for(let t in s)if(s[t]===e)return this.checked=!1,void this.value.removeAt.call(this.value,t);this.checked=!0,this.value.push(e)}}}),Anot.component("input",{__init__(e,s,t){e.hasOwnProperty("disabled")&&(s.disabled=!0),e.iconR&&(s.pos="right",e.icon=e.iconR,delete e.iconR),this.classList.add("do-input"),this.classList.add("do-fn-noselect"),this.classList.add(e.color||"grey"),e.icon&&this.classList.add("icon-"+s.pos),this.setAttribute(":class","{disabled: disabled, active: active}"),this.setAttribute(":css","{width: props.width}"),delete e.disabled,delete e.color,t()},render(){let{icon:e,placeholder:s}=this.props;return'\n <span \n class="do-input__holder"\n :class="{visible: !value || active}"\n :text="props.placeholder"></span>\n <input \n class="do-input__input"\n :attr="{disabled: disabled, type: props.type }"\n :duplex="value" \n :keyup="onKeyup"\n :blur="onBlur"\n :focus="onFocus" />'+(e?`<i class="do-input__icon do-icon-${e}"></i>`:"")},state:{pos:"left",value:"",disabled:!1,active:!1},skip:["pos"],props:{type:"text",width:180,placeholder:"",default:"",submit:Anot.PropsTypes.isFunction()},methods:{onFocus(){this.active=!0},onBlur(){this.active=!1},onKeyup(e){this.disabled||13===e.keyCode&&"function"==typeof this.props.submit&&this.props.submit()}}});export default Anot;

15
src/lib/form/input.js Normal file

File diff suppressed because one or more lines are too long

15
src/lib/form/number.js Normal file

File diff suppressed because one or more lines are too long

15
src/lib/form/progress.js Normal file
View File

@ -0,0 +1,15 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2019-09-07 23:35:03
* @version v2.0.1
*
*/
'use strict'
export default class Progress extends HTMLElement{static get observedAttributes(){return["value","max"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{value:0,max:1},writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML="<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:flex;align-items:center}:host label{flex:1;height:var(--size, 10px);border-radius:9px;background:#e8ebf4}:host label span{display:block;width:0;height:100%;border-radius:9px;background:#3fc2a7}:host([size='large']) label{height:18px}:host([size='medium']) label{height:14px}:host([size='mini']) label{height:6px}:host([color='red']) label span{background:#ff5061}:host([color='blue']) label span{background:#66b1ff}:host([color='green']) label span{background:#58d68d}:host([color='orange']) label span{background:#ffb618}:host([color='dark']) label span{background:#62778d}:host([color='purple']) label span{background:#ac61ce}\n</style> <label><span></span></label> ",this.__THUMB__=this.root.children[1].lastElementChild}get value(){return this.props.value}set value(e){this.props.value=+e,this.calculate()}calculate(){var{max:e,value:a}=this.props;this.__THUMB__.style.width=`${100*a/e}%`}connectedCallback(){this.calculate()}attributeChangedCallback(e,a,l){if(null!==l&&a!==l)switch(e){case t:var t=+l;(t!=t||t<1)&&(t=1),this.props.max=t,this.calculate();break;case"value":var r=+l;r==r&&(this.props.value=r,this.calculate())}}};
if(!customElements.get('wc-progress')){
customElements.define('wc-progress', Progress)
}

15
src/lib/form/radio.js Normal file
View File

@ -0,0 +1,15 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2019-09-07 23:35:03
* @version v2.0.1
*
*/
'use strict'
import{bind,unbind}from"../utils.js";export default class Radio extends HTMLElement{static get observedAttributes(){return["label","checked","readonly","disabled"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{label:"",checked:!1,readonly:!1,disabled:!1},writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML="<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:inline-block;line-height:1;font-size:14px}:host label{display:flex;justify-content:center;align-items:center;min-width:32px;height:32px;padding:0 5px;user-select:none;-moz-user-select:none;cursor:inherit;color:#7e909a}:host label.checked .dot::after{display:block;width:12px;height:12px;border-radius:50%;background:#aabac3;content:''}:host .dot{width:18px;height:18px;padding:2px;margin-right:3px;border:1px solid #aabac3;border-radius:50%;background:#fff}:host([readonly]){opacity:0.8}:host([disabled]){cursor:not-allowed;opacity:0.6}:host([size='large']) label{width:58px;height:32px}:host([size='large']) .dot{width:26px;height:26px}:host([size='medium']) label{width:50px;height:28px}:host([size='medium']) .dot{width:22px;height:22px}:host([size='mini']) label{width:22px;height:14px;padding:2px}:host([size='mini']) .dot{width:10px;height:10px}:host([color='red']) label.checked{color:#ff5061}:host([color='red']) label.checked .dot{border-color:#ff5061}:host([color='red']) label.checked .dot::after{background:#ff5061}:host([color='blue']) label.checked{color:#66b1ff}:host([color='blue']) label.checked .dot{border-color:#66b1ff}:host([color='blue']) label.checked .dot::after{background:#66b1ff}:host([color='green']) label.checked{color:#58d68d}:host([color='green']) label.checked .dot{border-color:#58d68d}:host([color='green']) label.checked .dot::after{background:#58d68d}:host([color='teal']) label.checked{color:#3fc2a7}:host([color='teal']) label.checked .dot{border-color:#3fc2a7}:host([color='teal']) label.checked .dot::after{background:#3fc2a7}:host([color='orange']) label.checked{color:#ffb618}:host([color='orange']) label.checked .dot{border-color:#ffb618}:host([color='orange']) label.checked .dot::after{background:#ffb618}:host([color='dark']) label.checked{color:#62778d}:host([color='dark']) label.checked .dot{border-color:#62778d}:host([color='dark']) label.checked .dot::after{background:#62778d}:host([color='purple']) label.checked{color:#ac61ce}:host([color='purple']) label.checked .dot{border-color:#ac61ce}:host([color='purple']) label.checked .dot::after{background:#ac61ce}\n</style> <label> <span class=\"dot\"></span> <slot></slot> </label> ",this.__SWITCH__=this.root.lastElementChild}get value(){return this.props.label}set value(e){this.checked=this.props.label===e}get checked(){return this.props.checked}set checked(e){this.props.checked=!!e,this.__SWITCH__.classList.toggle("checked",this.props.checked)}get readonly(){return this.props.readonly}set readonly(e){var o=typeof e;e!==this.props.readonly&&("boolean"===o&&e||"boolean"!==o?(this.props.readonly=!0,this.setAttribute("readonly","")):(this.props.readonly=!1,this.removeAttribute("readonly")))}get disabled(){return this.props.disabled}set disabled(e){var o=typeof e;e!==this.props.disabled&&("boolean"===o&&e||"boolean"!==o?(this.props.disabled=!0,this.setAttribute("disabled","")):(this.props.disabled=!1,this.removeAttribute("disabled")))}connectedCallback(){this._handleClick=bind(this,"click",e=>{this.disabled||this.readonly||this.checked||(this.checked=!0,this.dispatchEvent(new CustomEvent("input")))})}disconnectedCallback(){unbind(this,"click",this._handleClick)}attributeChangedCallback(e,o,t){if(null!==t&&o!==t)switch(e){case"label":this.props.label=t;break;case"checked":case"readonly":case"disabled":this[e]=!0}}};
if(!customElements.get('wc-radio')){
customElements.define('wc-radio', Radio)
}

15
src/lib/form/select.js Normal file

File diff suppressed because one or more lines are too long

15
src/lib/form/switch.js Normal file
View File

@ -0,0 +1,15 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2019-09-07 23:35:03
* @version v2.0.1
*
*/
'use strict'
import{bind,unbind}from"../utils.js";export default class Switch extends HTMLElement{static get observedAttributes(){return["checked","disabled"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{checked:!1,disabled:!1},writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML="<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:inline-block}:host section{display:flex;justify-content:center;align-items:center}:host label{display:flex;width:38px;height:22px;padding:3px;margin:5px;border-radius:21px;background:#dae1e9;cursor:inherit}:host label.checked{flex-direction:row-reverse;background:#7e909a}:host .dot{width:16px;height:16px;border-radius:50%;background:#fff}:host([disabled]){cursor:not-allowed;opacity:0.6}:host([size='large']) label{width:58px;height:32px}:host([size='large']) .dot{width:26px;height:26px}:host([size='medium']) label{width:50px;height:28px}:host([size='medium']) .dot{width:22px;height:22px}:host([size='mini']) label{width:22px;height:14px;padding:2px}:host([size='mini']) .dot{width:10px;height:10px}:host([color='red']) label.checked{background:#ff5061}:host([color='blue']) label.checked{background:#66b1ff}:host([color='green']) label.checked{background:#58d68d}:host([color='teal']) label.checked{background:#3fc2a7}:host([color='orange']) label.checked{background:#ffb618}:host([color='dark']) label.checked{background:#62778d}:host([color='purple']) label.checked{background:#ac61ce}\n</style> <section> <label> <span class=\"dot\"></span> </label> <slot></slot> </section> ",this.__SWITCH__=this.root.lastElementChild.firstElementChild}get value(){return this.props.checked}set value(e){this.checked=e}get checked(){return this.props.checked}set checked(e){this.props.checked=!!e,this.__SWITCH__.classList.toggle("checked",this.props.checked)}get disabled(){return this.props.disabled}set disabled(e){var t=typeof e;e!==this.props.disabled&&("boolean"===t&&e||"boolean"!==t?(this.props.disabled=!0,this.setAttribute("disabled","")):(this.props.disabled=!1,this.removeAttribute("disabled")))}connectedCallback(){this._handleClick=bind(this,"click",e=>{this.disabled||(this.checked=!this.checked,this.dispatchEvent(new CustomEvent("input")))})}disconnectedCallback(){unbind(this,"click",this._handleClick)}attributeChangedCallback(e,t,i){if(null!==i&&t!==i)switch(e){case"checked":case"disabled":this[e]=!0}}};
if(!customElements.get('wc-switch')){
customElements.define('wc-switch', Switch)
}

15
src/lib/icon/index.js Normal file
View File

@ -0,0 +1,15 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2019-09-07 23:35:03
* @version v2.0.1
*
*/
'use strict'
import SVG_DICT from"./svg.js";export default class Icon extends HTMLElement{static get observedAttributes(){return["is"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{is:""},writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML="<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:inline-block;color:#526273}:host(:not([is])){display:none}.icon{display:block;width:var(--size, 32px);height:var(--size, 32px);fill:currentColor}.icon.load{animation:load 1.5s linear infinite}.icon circle{stroke:currentColor;animation:circle 1.5s ease-in-out infinite}:host([size='large']) .icon{width:42px;height:42px}:host([size='medium']) .icon{width:38px;height:38px}:host([size='mini']) .icon{width:20px;height:20px}:host([color='red']){color:#ff5061}:host([color='blue']){color:#66b1ff}:host([color='green']){color:#58d68d}:host([color='teal']){color:#3fc2a7}:host([color='orange']){color:#ffb618}:host([color='dark']){color:#62778d}:host([color='purple']){color:#ac61ce}:host([color='grey']){color:#aabac3}@keyframes circle{0%{stroke-dasharray:0, 3812px;stroke-dashoffset:0}50%{stroke-dasharray:1906px, 3812px;stroke-dashoffset:-287px}100%{stroke-dasharray:1906px, 3812px;stroke-dashoffset:-2393px}}@keyframes load{to{transform:rotate(360deg)}}\n</style> <svg class=\"icon\" viewBox=\"0 0 1024 1024\"></svg> ",this.__ICO__=this.root.lastElementChild,this.drawPath()}get is(){return this.props.is}drawPath(){var{is:o}=this.props,t=SVG_DICT[o];this.__ICO__&&o&&t&&(this.__ICO__.innerHTML="loading"===o?t:`<path d="${t}" />`,this.__ICO__.classList.toggle("load","loading"===o))}attributeChangedCallback(o,t,e){if(null!==e&&t!==e)switch(o){case"is":this.props.is=e,e?this.drawPath():this.removeAttribute("is")}}};
if(!customElements.get('wc-icon')){
customElements.define('wc-icon', Icon)
}

1
src/lib/icon/svg.js Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,30 +0,0 @@
v1.0.0-base / 2017-09-20
==================
+ 统一字体图标
+ 精简动画类型
+ 优化样式
v0.0.4-base / 2017-04-20
==================
+ 优化offset的处理
+ 优化样式
v0.0.3-base / 2017-04-15
==================
+ 重构wrap方式创建弹窗实例的实现
v0.0.2-base / 2017-04-13
==================
+ 修复:layer方式创建实例时,漏掉自身的bug;
+ 修复layer.open()方法打开已有实例时不返回id的bug;
+ 修复layer.close()方法关闭实例时,未修改实例状态的bug;
+ 修改特殊模式下的实例的最小宽度为10px;
+ 优化:layer方式创建实例的逻辑处理;
+ 优化layer.alert()方法参数的处理;
v0.0.1-base / 2017-04-06
==================
+ 完成layer base版移植

File diff suppressed because one or more lines are too long

View File

@ -21,7 +21,7 @@ class Lyrics {
r: { bg: '', txt: '' } r: { bg: '', txt: '' }
} }
let lrc = ipcRenderer.sendSync('read-lrc', id) let lrc = ipcRenderer.sendSync('sonist', { type: 'read-lrc', id })
if (!id || lrc === null) { if (!id || lrc === null) {
log('no lrc file', id) log('no lrc file', id)
return false return false
@ -29,8 +29,6 @@ class Lyrics {
this.__ID__ = id this.__ID__ = id
log(id)
this.lib = lrc this.lib = lrc
.split('\n') .split('\n')
.map(it => { .map(it => {
@ -106,7 +104,11 @@ class Lyrics {
// 延时3秒写入 // 延时3秒写入
this.__TIMER__ = setTimeout(() => { this.__TIMER__ = setTimeout(() => {
ipcRenderer.send('save-lrc', { id: this.__ID__, lrc }) ipcRenderer.send('sonist', {
type: 'save-lrc',
id: this.__ID__,
data: lrc
})
}, 3000) }, 3000)
} }

View File

@ -1 +1,15 @@
"use strict";importCss("/css/pager.css");function calculate({currPage:t,maxPageShow:e,totalPage:a}){let s=[],r=0,o=t<e/2?e-t:Math.floor(e/2);if(a<2)return s.push(1),s;t-o>1&&s.push("..."),a-t<o&&(r=o-a+t);for(let e=t-o-r;e<t+o+1&&e<=a;e++)e>0&&s.push(e);return t+o<a&&s.push("..."),s}function update(t,e){const{totalPage:a,props:{maxPageShow:s}}=e;e.currPage!==t&&(e.currPage=e.inputPage=t,"function"==typeof e.props.pageChanged&&e.props.pageChanged(t)),e.pageList.clear(),a>1?e.pageList.pushArray(calculate({currPage:t,totalPage:a,maxPageShow:s})):e.pageList.pushArray([1])}Anot.ui.pager="1.0.0";const tmpls={home:'<button class="do-icon-dbl-left button"\n :css="{\'border-radius\': props.radius}"\n :attr="{disabled: currPage === 1}"\n :data="{to: parseUrl(1)}"\n :click="go(1, $event)"></button>',end:'<button class="do-icon-dbl-right button"\n :css="{\'border-radius\': props.radius}"\n :attr="{disabled: currPage === totalPage}"\n :data="{to: parseUrl(totalPage)}"\n :click="go(totalPage, $event)"></button>',prev:'<button class="do-icon-left button"\n :css="{\'border-radius\': props.radius}"\n :attr="{disabled: currPage < 2}"\n :data="{to: parseUrl(currPage - 1)}"\n :click="go(currPage - 1, $event)"></button>',next:'<button class="do-icon-right button"\n :css="{\'border-radius\': props.radius}"\n :attr="{disabled: currPage >= totalPage}"\n :data="{to: parseUrl(currPage + 1)}"\n :click="go(currPage + 1, $event)"></button>',pager:'<button class="page"\n :for="pageList"\n :css="{\'border-radius\': props.radius}"\n :attr="{disabled: \'...\' === el || currPage === el}"\n :data="{to: parseUrl(el)}"\n :class="{disabled: \'...\' === el, curr: currPage === el}"\n :text="el"\n :click="go(el, $event)"></button>',curr:'<button class="page curr" :text="currPage"></button>',total:'<span class="total-box">共 {{totalPage}} 页 {{totalItem}} 条</span>',jumper:'<div class="input-box">前往\n <input type="text" :duplex="inputPage" :keyup="go(null, $event)"> 页\n </div>',slot:""};export default Anot.component("pager",{__init__:function(t,e,a){this.classList.add("do-pager"),this.classList.add("do-fn-noselect"),this.setAttribute(":class","{{classList.join(' ')}}"),t.theme=+t.theme||1,t.simpleMode&&(t.theme=1),e.classList=e.classList.concat("skin-"+t.theme,t.color||"plain",t.size||"mini"),t.total&&(e.totalItem=+t.total),t.pageSize&&(e.pageSize=+t.pageSize),t.layout||(t.layout="total,home,prev,pager,next,end,jumper"),2===t.theme&&(t.radius=null),delete t.total,delete t.pageSize,delete t.color,delete t.size,a()},render:function(t){let{layout:e,theme:a,simpleMode:s}=this.props;return s?e=["prev","curr","next"]:(e=e.replace(/\s/g,""),2===a&&(e=e.replace(/total|jumper/g,"")),e=e.split(",")),(e=e.map(e=>"slot"!==e?tmpls[e]||"":t&&t.extra?t.extra.join(""):void 0)).join("\n")},componentWillMount:function(){const{currPage:t,totalPage:e,props:a}=this;this.pageList.clear(),this.pageList.pushArray(calculate({currPage:t,totalPage:e,maxPageShow:a.maxPageShow}))},componentDidMount:function(){"function"==typeof this.props.created&&this.props.created(this)},state:{classList:[],currPage:1,totalItem:1,pageSize:20,inputPage:1,pageList:[]},computed:{totalPage:function(){return Math.ceil(this.totalItem/this.pageSize)}},props:{url:null,maxPageShow:5,simpleMode:!1,radius:3,pageChanged:Anot.PropsTypes.isFunction(),created:Anot.PropsTypes.isFunction()},skip:["classList"],methods:{parseUrl(t){return(t>>>=0)<1||!this.props.url||this.currPage===t?"":this.props.url.replace("{id}",t)},go(t,e){let{inputPage:a,totalPage:s,currPage:r}=this,o=e&&e.target||null;if(!(o&&o.disabled||r===t))if(t&&o){if("..."!==t){let e=o.dataset.to;e?location.href=e:t>>>=0,update(t,this)}}else if(null===t){if(a>>>=0,e&&13===e.keyCode){if(a<1||r===a)return this.inputPage=r;a>s&&(a=s),this.inputPage=a,update(a,this)}}else update(t>>>=0,this)},setSize(t){t=+t,this.pageSize!==t&&(this.pageSize=+t,update(1,this))},setTotal(t){t=+t,this.totalItem!==t&&(this.totalItem=+t,update(1,this))}}}); /**
*
* @authors yutent (yutent@doui.cc)
* @date 2019-09-01 23:16:06
* @version v2.0.1
*
*/
'use strict'
function calculate(t,e,r){var o,a=[],s=0,i=t<3?6-t:2;if(e<2||r)return a.push({to:t,txt:t}),a;t-i>1&&e>5&&(o=(o=t-2*i)<1?1:o,a.push({to:o,txt:"..."}));e-t<i&&(s=i-e+t);for(var n=t-i-s;n<t+i+1&&n<=e;n++)n>0&&a.push({to:n,txt:n});t+i<e&&(o=(o=t+2*i)>e?e:o,a.push({to:o,txt:"..."}));return a}export default class Pager extends HTMLElement{static get observedAttributes(){return["layout","total","curr","pagesize","simple"]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{layout:"home, prev, next, end",total:0,curr:1,pagesize:20,simple:!1},writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML='<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{display:block;line-height:1;color:#62778d;font-size:14px;user-select:none;-moz-user-select:none}:host .layout{display:flex;justify-content:center;align-items:center;margin:10px auto}:host button{min-width:32px;height:32px;padding:0 8px;margin:0 3px;background:#f3f5fb;border:0;border-radius:4px;outline:none;font-size:inherit;color:inherit}:host button:hover{background:#e8ebf4}:host button[curr]{background:#3fc2a7;color:#fff}:host([simple]) .home,:host([simple]) .end{display:none}:host([circle]) button{border-radius:50%}:host([color=\'red\']) button[curr]{background:#ff5061}:host([color=\'blue\']) button[curr]{background:#66b1ff}:host([color=\'green\']) button[curr]{background:#58d68d}:host([color=\'teal\']) button[curr]{background:#3fc2a7}:host([color=\'orange\']) button[curr]{background:#ffb618}:host([color=\'dark\']) button[curr]{background:#62778d}:host([color=\'purple\']) button[curr]{background:#ac61ce}\n</style> <div class="layout"> <button data-page="1" class="home">|<</button> <button data-page="prev" class="prev"><</button> <div class="pager"></div> <button data-page="next" class="next">></button> <button data-page="end" class="end">>|</button> </div> ',this.__LAYOUT__=this.root.children[1],this.__HOME__=this.__LAYOUT__.children[0],this.__PREV__=this.__LAYOUT__.children[1],this.__PAGE__=this.__LAYOUT__.children[2],this.__NEXT__=this.__LAYOUT__.children[3],this.__END__=this.__LAYOUT__.children[4]}update(){var{curr:t,totalpage:e,simple:r}=this.props,o=calculate(t,e,r);this.__PAGE__.innerHTML=o.map(e=>`<button ${t===e.to?"curr":""} data-page="${e.to}">${e.txt}</button>`).join("")}connectedCallback(){var{pagesize:t,total:e}=this.props;this.props.totalpage=Math.ceil(e/t),this.update(),this.__LAYOUT__.addEventListener("click",t=>{if("BUTTON"===t.target.tagName){var{curr:e,totalpage:r}=this.props,o=t.target.dataset.page,a=+o;if(a==a){if(a===e)return}else switch(o){case"prev":if((a=e-1)<1)return;break;case"next":if((a=e+1)>r)return;break;case"end":if(r===e)return;a=r}this.props.curr=a,this.update(),this.dispatchEvent(new CustomEvent("pick",{detail:a}))}},!1)}attributeChangedCallback(t,e,r){if(e!==r)switch(t){case"total":case"pagesize":case"curr":this.props[t]=+r||this.props[t];var{pagesize:o,total:a}=this.props;this.props.totalpage=Math.ceil(a/o),this.update();break;case"simple":this.props.simple=!0}}};
if(!customElements.get('wc-pager')){
customElements.define('wc-pager', Pager)
}

File diff suppressed because one or more lines are too long

View File

@ -1 +1,225 @@
"use strict";function serialize(e,t,r){let o;if(Array.isArray(t))t.forEach(function(t,a){o=e?`${e}[${Array.isArray(t)?a:""}]`:a,"object"==typeof t?serialize(o,t,r):r(o,t)});else for(let a in t)o=e?`${e}[${a}]`:a,"object"==typeof t[a]?serialize(o,t[a],r):r(o,t[a])}const toS=Object.prototype.toString,doc=window.document,encode=encodeURIComponent,decode=decodeURIComponent,TagHooks=function(){this.option=doc.createElement("select"),this.thead=doc.createElement("table"),this.td=doc.createElement("tr"),this.area=doc.createElement("map"),this.tr=doc.createElement("tbody"),this.col=doc.createElement("colgroup"),this.legend=doc.createElement("fieldset"),this._default=doc.createElement("div"),this.g=doc.createElementNS("http://www.w3.org/2000/svg","svg"),this.optgroup=this.option,this.tbody=this.tfoot=this.colgroup=this.caption=this.thead,this.th=this.td,"circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use".replace(/,/g,e=>{this[e]=this.g})},Helper={tagHooks:new TagHooks,rtagName:/<([\w:]+)/,rxhtml:/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,scriptTypes:{"text/javascript":1,"text/ecmascript":1,"application/ecmascript":1,"application/javascript":1},rhtml:/<|&#?\w+;/};export default{parseJS:function(code){if(code=(code+"").trim(),code)if(1===code.indexOf("use strict")){let e=doc.createElement("script");e.text=code,doc.head.appendChild(e).parentNode.removeChild(e)}else eval(code)},parseXML:function(e,t,r){try{t=(new DOMParser).parseFromString(e,"text/xml")}catch(e){t=void 0}return t&&t.documentElement&&!t.getElementsByTagName("parsererror").length||console.error("Invalid XML: "+e),t},parseHTML:function(e){let t=doc.createDocumentFragment().cloneNode(!1);if("string"!=typeof e)return t;if(!Helper.rhtml.test(e))return t.appendChild(document.createTextNode(e)),t;e=e.replace(Helper.rxhtml,"<$1></$2>").trim();let r=(Helper.rtagName.exec(e)||["",""])[1].toLowerCase(),o=Helper.tagHooks[r]||Helper.tagHooks._default,a=null;o.innerHTML=e;let i=o.getElementsByTagName("script");if(i.length)for(let e,t=0;e=i[t++];)if(Helper.scriptTypes[e.type]){let t=doc.createElement("script").cloneNode(!1);e.attributes.forEach(function(e){t.setAttribute(e.name,e.value)}),t.text=e.text,e.parentNode.replaceChild(t,e)}for(;a=o.firstChild;)t.appendChild(a);return t},parseForm:function(e){let t={},r=!1;for(let o,a=0;o=e.elements[a++];)switch(o.type){case"select-one":case"select-multiple":if(o.name.length&&!o.disabled)for(let e,r=0;e=o.options[r++];)e.selected&&(t[o.name]=e.value||e.text);break;case"file":o.name.length&&!o.disabled&&(t[o.name]=o.files[0],r=!0);break;case void 0:case"submit":case"reset":case"button":break;case"radio":case"checkbox":if(!o.checked)break;default:o.name.length&&!o.disabled&&(t[o.name]=o.value)}return r?this.mkFormData(t):t},mkFormData(e){let t=new FormData;for(let r in e){let o=e[r];Array.isArray(o)?o.forEach(function(e){t.append(r+"[]",e)}):t.append(r,e[r])}return t},param:function(e){if(!e||"string"==typeof e||"number"==typeof e)return e;let t=[];return"object"==typeof e&&serialize("",e,function(e,r){/native code/.test(r)||(r="function"==typeof r?r():r,r="[object File]"!==toS.call(r)?encode(r):r,t.push(encode(e)+"="+r))}),t.join("&")}}; /**
*
* @authors yutent (yutent@doui.cc)
* @date 2016-11-26 16:35:45
*
*/
'use strict'
function serialize(p, obj, q) {
let k
if (Array.isArray(obj)) {
obj.forEach(function(it, i) {
k = p ? `${p}[${Array.isArray(it) ? i : ''}]` : i
// k = p ? p + '[' + (Array.isArray(it) ? i : '') + ']' : i
if (typeof it === 'object') {
serialize(k, it, q)
} else {
q(k, it)
}
})
} else {
for (let i in obj) {
k = p ? `${p}[${i}]` : i
// k = p ? p + '[' + i + ']' : i
if (typeof obj[i] === 'object') {
serialize(k, obj[i], q)
} else {
q(k, obj[i])
}
}
}
}
const toS = Object.prototype.toString
const doc = window.document
const encode = encodeURIComponent
const decode = decodeURIComponent
const TagHooks = function() {
this.option = doc.createElement('select')
this.thead = doc.createElement('table')
this.td = doc.createElement('tr')
this.area = doc.createElement('map')
this.tr = doc.createElement('tbody')
this.col = doc.createElement('colgroup')
this.legend = doc.createElement('fieldset')
this._default = doc.createElement('div')
this.g = doc.createElementNS('http://www.w3.org/2000/svg', 'svg')
this.optgroup = this.option
this.tbody = this.tfoot = this.colgroup = this.caption = this.thead
this.th = this.td
'circle,defs,ellipse,image,line,path,polygon,polyline,rect,symbol,text,use'.replace(
/,/g,
m => {
this[m] = this.g //处理svg
}
)
}
const Helper = {
tagHooks: new TagHooks(),
rtagName: /<([\w:]+)/,
rxhtml: /<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,
scriptTypes: {
'text/javascript': 1,
'text/ecmascript': 1,
'application/ecmascript': 1,
'application/javascript': 1
},
rhtml: /<|&#?\w+;/
}
export default {
parseJS: function(code) {
code = (code + '').trim()
if (code) {
if (code.indexOf('use strict') === 1) {
let script = doc.createElement('script')
script.text = code
doc.head.appendChild(script).parentNode.removeChild(script)
} else {
eval(code)
}
}
},
parseXML: function(data, xml, tmp) {
try {
tmp = new DOMParser()
xml = tmp.parseFromString(data, 'text/xml')
} catch (e) {
xml = void 0
}
if (
!xml ||
!xml.documentElement ||
xml.getElementsByTagName('parsererror').length
) {
console.error('Invalid XML: ' + data)
}
return xml
},
parseHTML: function(html) {
let fragment = doc.createDocumentFragment().cloneNode(false)
if (typeof html !== 'string') {
return fragment
}
if (!Helper.rhtml.test(html)) {
fragment.appendChild(document.createTextNode(html))
return fragment
}
html = html.replace(Helper.rxhtml, '<$1></$2>').trim()
let tag = (Helper.rtagName.exec(html) || ['', ''])[1].toLowerCase()
let wrap = Helper.tagHooks[tag] || Helper.tagHooks._default
let firstChild = null
//使用innerHTML生成的script节点不会触发请求与执行text属性
wrap.innerHTML = html
let script = wrap.getElementsByTagName('script')
if (script.length) {
for (let i = 0, el; (el = script[i++]); ) {
if (Helper.scriptTypes[el.type]) {
let tmp = doc.createElement('script').cloneNode(false)
el.attributes.forEach(function(attr) {
tmp.setAttribute(attr.name, attr.value)
})
tmp.text = el.text
el.parentNode.replaceChild(tmp, el)
}
}
}
while ((firstChild = wrap.firstChild)) {
fragment.appendChild(firstChild)
}
return fragment
},
parseForm: function(form) {
let data = {}
let hasAttach = false
for (let i = 0, field; (field = form.elements[i++]); ) {
switch (field.type) {
case 'select-one':
case 'select-multiple':
if (field.name.length && !field.disabled) {
for (let j = 0, opt; (opt = field.options[j++]); ) {
if (opt.selected) {
data[field.name] = opt.value || opt.text
}
}
}
break
case 'file':
if (field.name.length && !field.disabled) {
data[field.name] = field.files[0]
hasAttach = true
}
break
case undefined:
case 'submit':
case 'reset':
case 'button':
break //按钮啥的, 直接忽略
case 'radio':
case 'checkbox':
// 只处理选中的
if (!field.checked) break
default:
if (field.name.length && !field.disabled) {
data[field.name] = field.value
}
}
}
// 如果有附件, 改为FormData
if (hasAttach) {
return this.mkFormData(data)
} else {
return data
}
},
mkFormData(data) {
let form = new FormData()
for (let i in data) {
let el = data[i]
if (Array.isArray(el)) {
el.forEach(function(it) {
form.append(i + '[]', it)
})
} else {
form.append(i, data[i])
}
}
return form
},
param: function(obj) {
if (!obj || typeof obj === 'string' || typeof obj === 'number') {
return obj
}
let arr = []
let q = function(k, v) {
if (/native code/.test(v)) {
return
}
v = typeof v === 'function' ? v() : v
v = toS.call(v) !== '[object File]' ? encode(v) : v
arr.push(encode(k) + '=' + v)
}
if (typeof obj === 'object') {
serialize('', obj, q)
}
return arr.join('&')
}
}

15
src/lib/scroll/index.js Normal file
View File

@ -0,0 +1,15 @@
/**
*
* @authors yutent (yutent@doui.cc)
* @date 2019-09-07 23:35:03
* @version v2.0.1
*
*/
'use strict'
import{bind,ebind,unbind}from"../utils.js";const IS_FF=!!window.sidebar;export default class Scroll extends HTMLElement{static get observedAttributes(){return[]}constructor(){super(),Object.defineProperty(this,"root",{value:this.attachShadow({mode:"open"}),writable:!0,enumerable:!1,configurable:!0}),Object.defineProperty(this,"props",{value:{},writable:!0,enumerable:!1,configurable:!0}),this.root.innerHTML='<style>*{box-sizing:border-box;margin:0;padding:0}::before,::after{box-sizing:border-box}:host{overflow:hidden;position:relative;display:flex;width:100%}:host .container{overflow:hidden;position:relative;width:100%;height:100%}.is-horizontal,.is-vertical{visibility:hidden;position:absolute;z-index:10240;opacity:0;transition:opacity 0.3s linear, visibility 0.3s linear}.is-horizontal .thumb,.is-vertical .thumb{display:block;border-radius:3px;background:rgba(44,47,53,0.25);cursor:default}.is-horizontal .thumb:hover,.is-vertical .thumb:hover{background:rgba(44,47,53,0.5)}.is-horizontal{left:0;bottom:1px;width:100%;height:6px}.is-horizontal .thumb{width:0;height:6px}.is-vertical{top:0;right:1px;width:6px;height:100%}.is-vertical .thumb{width:6px;height:0}:host(:hover) .is-horizontal,:host(:hover) .is-vertical{visibility:visible;opacity:1}\n</style> <div class="container"><slot></slot></div> <div class="is-horizontal"><span class="thumb"></span></div> <div class="is-vertical"><span class="thumb"></span></div> ',this.__BOX__=this.root.children[1],this.__X__=this.root.children[2].children[0],this.__Y__=this.root.children[3].children[0]}get scrollTop(){return this.__BOX__.scrollTop}set scrollTop(t){if((t=+t)==t){var{sh:s,oh:i,yh:e}=this.props;this.__BOX__.scrollTop=t;var o=this.__BOX__.scrollTop/(s-i)*(i-e);this.props.thumbY=o,this.__Y__.style.transform=`translateY(${o}px)`}}get scrollLeft(){return this.__BOX__.scrollLeft}set scrollLeft(t){if(n=+n,n==n){var{sw:s,ow:i,xw:e}=this.props;this.__BOX__.scrollLeft=n;var o=this.__BOX__.scrollLeft/(s-i)*(i-e);this.props.thumbX=o,this.__X__.style.transform=`translateX(${o}px)`}}get scrollHeight(){return this.__BOX__.scrollHeight}_fetchScrollX(t){var{sw:s,ow:i,xw:e}=this.props;return t<0?t=0:t>i-e&&(t=i-e),this.__BOX__.scrollLeft=t/(i-e)*(s-i),this.__X__.style.transform=`translateX(${t}px)`,t}_fetchScrollY(t){var{sh:s,oh:i,yh:e}=this.props;return t<0?t=0:t>i-e&&(t=i-e),this.__BOX__.scrollTop=t/(i-e)*(s-i),this.__Y__.style.transform=`translateY(${t}px)`,t}connectedCallback(){this._initFn=bind(this.__BOX__,"mouseenter",t=>{var s=this.__BOX__.offsetWidth,i=this.__BOX__.scrollWidth,e=this.__BOX__.offsetHeight,o=this.__BOX__.scrollHeight,r=e*e/o>>0,h=s*s/i>>0;r<50&&(r=50),h<50&&(h=50),h===s&&(h=0),r===e&&(r=0),this.props.oh=e,this.props.sh=o,this.props.ow=s,this.props.sw=i,this.props.yh=r,this.props.xw=h,this.__X__.style.width=h+"px",this.__Y__.style.height=r+"px"}),this._wheelFn=ebind(this.__BOX__,"wheel",t=>{t.preventDefault();var{sh:s,oh:i,yh:e,sw:o,ow:r,xw:h}=this.props;if(h||e){var l,_;if(IS_FF)l=t.deltaMode?10*t.deltaX:t.deltaX,_=t.deltaMode?10*t.deltaY:t.deltaY;else{var n=Math.abs(t.wheelDelta);n<120?(l=t.deltaX,_=t.deltaY):(l=t.deltaX/(n/120),_=t.deltaY/(n/120))}if(this.__BOX__.scrollTop+=_,this.__BOX__.scrollLeft+=l,h){var a=this.__BOX__.scrollLeft/(o-r)*(r-h);this.props.thumbX=a,this.__X__.style.transform=`translateX(${a}px)`}if(e){var p=this.__BOX__.scrollTop/(s-i)*(i-e);this.props.thumbY=p,this.__Y__.style.transform=`translateY(${p}px)`}}});var t,s,i,e,o=o=>{var{thumbY:r,thumbX:h}=this.props;null!==t&&(i=this._fetchScrollX(h+o.pageX-t)),null!==s&&(e=this._fetchScrollY(r+o.pageY-s))},r=h=>{t=null,s=null,this.props.thumbX=i,this.props.thumbY=e,unbind(document,"mousemove",o),unbind(document,"mouseup",r)};bind(this.__Y__,"mousedown",t=>{s=t.pageY,this.props.thumbY||(this.props.thumbY=0),bind(document,"mousemove",o),bind(document,"mouseup",r)}),bind(this.__X__,"mousedown",s=>{t=s.pageX,this.props.thumbX||(this.props.thumbX=0),bind(document,"mousemove",o),bind(document,"mouseup",r)})}disconnectedCallback(){unbind(this.__BOX__,"mouseenter",this._initFn),unbind(this.__BOX__,"wheel",this._wheelFn)}};
if(!customElements.get('wc-scroll')){
customElements.define('wc-scroll', Scroll)
}

1
src/lib/utils.js Normal file
View File

@ -0,0 +1 @@
function noop(){}export const nextTick=function(){let t=[];let n=document.createTextNode("\x3c!-- --\x3e");new MutationObserver(function(){let n=t.length;for(let e=0;e<n;e++)t[e]();t=t.slice(n)}).observe(n,{characterData:!0});let e=!1;return function(o){t.push(o),e=!e,n.data=e}}();export const each=function(t,n){if(t)if(Array.isArray(t))for(let e,o=0;(e=t[o++])&&!1!==n(e,o-1););else for(let e in t)if(t.hasOwnProperty(e)&&!1===n(t[e],e))break};export const bind=function(t,n,e=noop,o=!1){let r=n.split(",");return each(r,function(n){n=n.trim(),t.addEventListener(n,e,o)}),e};export const ebind=function(t,n,e,o){return bind(t,n,function(t){t.stopPropagation(),e&&e(t)},o)};export const unbind=function(t,n,e=noop,o=!1){let r=n.split(",");each(r,function(n){n=n.trim(),t.removeEventListener(n,e,o)})};export const clickOutside=function(t,n=noop){return bind(document,"mousedown",e=>{if(e)if(e.path){for(var o=e.path.concat();o.length>3;)if(o.shift()===t)return}else{var r=e.explicitOriginalTarget||e.target;if(t===r||t.contains(r)||t.root&&t.root.contains(r))return}n(e)})};

View File

@ -26,7 +26,7 @@ const MIME_TYPES = {
require('./tools/init') require('./tools/init')
const createTray = require('./tools/tray') const createTray = require('./tools/tray')
const createMenu = require('./tools/menu') const createMenu = require('./tools/menu')
const Shortcut = require('./tools/shortcut')
const { createMainWindow, createErrorWindow } = require('./tools/windows') const { createMainWindow, createErrorWindow } = require('./tools/windows')
const ROOT = __dirname const ROOT = __dirname
@ -35,7 +35,9 @@ const ROOT = __dirname
app.commandLine.appendSwitch('--lang', 'zh-CN') app.commandLine.appendSwitch('--lang', 'zh-CN')
app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required') app.commandLine.appendSwitch('--autoplay-policy', 'no-user-gesture-required')
protocol.registerStandardSchemes(['app'], { secure: true }) protocol.registerSchemesAsPrivileged([
{ scheme: 'app', privileges: { secure: true, standard: true } }
])
/* ----------------------------------------------------- */ /* ----------------------------------------------------- */
@ -69,7 +71,6 @@ app.once('ready', () => {
win.webContents.send('dock-click') win.webContents.send('dock-click')
} }
}) })
Shortcut.__init__(win)
} else { } else {
createErrorWindow() createErrorWindow()
} }

View File

@ -6,10 +6,10 @@
'use strict' 'use strict'
const { app, ipcMain } = require('electron') const { app, ipcMain, globalShortcut: GS } = require('electron')
const path = require('path') const path = require('path')
// const http = require('http')
const fs = require('iofs') const fs = require('iofs')
const Shortcut = require('./shortcut')
/* ********** 修复环境变量 start *********** */ /* ********** 修复环境变量 start *********** */
let PATH_SET = new Set() let PATH_SET = new Set()
@ -43,80 +43,73 @@ if (!fs.exists(APP_ROOT)) {
} }
const SUPPORTED_EXTS = ['.mp3', '.webm', '.ogg', '.flac', '.m4a', '.aac'] const SUPPORTED_EXTS = ['.mp3', '.webm', '.ogg', '.flac', '.m4a', '.aac']
const DB = {
read(file) {
let cache = (fs.cat(file) || '[]').toString('utf-8')
try {
return JSON.parse(cache)
} catch (err) {
return cache
}
},
save(file, data) {
fs.echo(JSON.stringify(data), file)
}
}
/* ----------------------------------------------------------------- */ /* ----------------------------------------------------------------- */
/* --------------------- 事件开始 ------------------------- */ /* --------------------- 事件开始 ------------------------- */
/* ---------------------------------------------------------------- */ /* ---------------------------------------------------------------- */
ipcMain.on('sonist', (ev, conn) => {
switch (conn.type) {
// 获取应用配置 // 获取应用配置
ipcMain.on('get-init', (ev, val) => { case 'get-init':
let cache = fs.cat(INIT_FILE).toString('utf-8') ev.returnValue = DB.read(INIT_FILE)
cache = JSON.parse(cache) break
ev.returnValue = cache
})
// 设置应用配置 // 设置应用配置
ipcMain.on('set-init', (ev, val) => { case 'set-init':
fs.echo(JSON.stringify(val), INIT_FILE) DB.save(INIT_FILE, conn.data)
}) break
// 获取音乐数据库 // 获取音乐数据库
ipcMain.on('get-music', (ev, val) => { case 'get-music':
let cache = fs.cat(DB_FILE).toString('utf-8') ev.returnValue = DB.read(DB_FILE)
cache = JSON.parse(cache) break
ev.returnValue = cache
})
// 更新音乐数据库 // 更新音乐数据库
ipcMain.on('set-music', (ev, val) => { case 'set-music':
fs.echo(JSON.stringify(val), DB_FILE) DB.save(DB_FILE, conn.data)
}) break
// 获取临时音乐数据库 // 获取临时音乐数据库
ipcMain.on('get-temp', (ev, val) => { case 'get-temp':
let cache = (fs.cat(TEMP_DB) || '[]').toString('utf-8') ev.returnValue = DB.read(TEMP_DB)
cache = JSON.parse(cache) break
ev.returnValue = cache
})
// 更新临时音乐数据库 // 更新临时音乐数据库
ipcMain.on('set-temp', (ev, val) => { case 'set-temp':
fs.echo(JSON.stringify(val), TEMP_DB) DB.save(TEMP_DB, conn.data)
}) break
// 读取歌词文件
/** case 'read-lrc':
* 保存歌词文件 let lrc = path.join(LRC_DIR, `${conn.id}.lrc`)
*/ if (fs.exists(lrc)) {
ipcMain.on('save-lrc', (ev, obj) => { ev.returnValue = DB.read(lrc)
fs.echo(obj.lrc, path.join(LRC_DIR, `${obj.id}.lrc`))
})
/**
* 读取歌词文件
*/
ipcMain.on('read-lrc', (ev, id) => {
let file = path.join(LRC_DIR, `${id}.lrc`)
if (fs.exists(file)) {
ev.returnValue = fs.cat(file).toString('utf8')
} else { } else {
ev.returnValue = null ev.returnValue = null
} }
}) break
// 保存歌词文件
case 'save-lrc':
fs.echo(conn.data, path.join(LRC_DIR, `${conn.id}.lrc`))
break
// 保存音乐文件
case 'save-cache':
let file = path.join(CACHE_DIR, conn.file)
fs.echo(conn.data, file)
ev.returnValue = `file://${file}`
break
/** // 扫描目录
* 保存音乐文件 case 'scan-dir':
*/ if (fs.isdir(conn.path)) {
ipcMain.on('save-cache', (ev, obj) => { let list = fs.ls(conn.path, true).filter(_ => {
let savefile = path.join(CACHE_DIR, obj.file)
fs.echo(obj.buff, savefile)
ev.returnValue = `file://${savefile}`
})
/**
* 扫描目录
*/
ipcMain.on('scan-dir', (ev, dir) => {
if (fs.isdir(dir)) {
let list = fs.ls(dir, true).filter(_ => {
if (fs.isdir(_)) { if (fs.isdir(_)) {
return false return false
} else { } else {
@ -131,4 +124,16 @@ ipcMain.on('scan-dir', (ev, dir) => {
} else { } else {
ev.returnValue = null ev.returnValue = null
} }
break
// 启用全局快捷键
case 'enable-gs':
Shortcut.__init__()
break
// 禁用全局快捷键
case 'disable-gs':
GS.unregisterAll()
break
}
}) })

View File

@ -6,52 +6,52 @@
'use strict' 'use strict'
const { globalShortcut: GS } = require('electron') const { app, globalShortcut: GS } = require('electron')
module.exports = { module.exports = {
__init__(win) { __init__() {
// 播放控制... // 播放控制...
GS.register('MediaNextTrack', _ => { GS.register('MediaNextTrack', _ => {
win.emit('gs-ctrl', 'next') app.__MAIN__.emit('gs-ctrl', 'next')
}) })
GS.register('MediaPreviousTrack', _ => { GS.register('MediaPreviousTrack', _ => {
win.emit('gs-ctrl', 'prev') app.__MAIN__.emit('gs-ctrl', 'prev')
}) })
GS.register('MediaStop', _ => { GS.register('MediaStop', _ => {
win.emit('gs-ctrl', 'stop') app.__MAIN__.emit('gs-ctrl', 'stop')
}) })
GS.register('MediaPlayPause', _ => { GS.register('MediaPlayPause', _ => {
win.emit('gs-ctrl', 'play') app.__MAIN__.emit('gs-ctrl', 'play')
}) })
// others // others
GS.register('Super+Alt+Space', _ => { GS.register('Super+Alt+Space', _ => {
win.emit('gs-ctrl', 'play') app.__MAIN__.emit('gs-ctrl', 'play')
}) })
GS.register('Super+Alt+Left', _ => { GS.register('Super+Alt+Left', _ => {
win.emit('gs-ctrl', 'prev') app.__MAIN__.emit('gs-ctrl', 'prev')
}) })
GS.register('Super+Alt+Right', _ => { GS.register('Super+Alt+Right', _ => {
win.emit('gs-ctrl', 'next') app.__MAIN__.emit('gs-ctrl', 'next')
}) })
GS.register('Super+Alt+Up', _ => { GS.register('Super+Alt+Up', _ => {
win.emit('gs-ctrl', 'vu') app.__MAIN__.emit('gs-ctrl', 'vu')
}) })
GS.register('Super+Alt+Down', _ => { GS.register('Super+Alt+Down', _ => {
win.emit('gs-ctrl', 'vd') app.__MAIN__.emit('gs-ctrl', 'vd')
}) })
GS.register('Super+Alt+R', _ => { GS.register('Super+Alt+R', _ => {
win.emit('gs-ctrl', 'lrc') app.__MAIN__.emit('gs-ctrl', 'lrc')
}) })
GS.register('Super+Alt+Shift+M', _ => { GS.register('Super+Alt+Shift+M', _ => {
win.emit('gs-ctrl', 'mini') app.__MAIN__.emit('gs-ctrl', 'mini')
}) })
} }
} }

View File

@ -22,7 +22,8 @@ exports.createMainWindow = function(icon) {
icon, icon,
webPreferences: { webPreferences: {
webSecurity: false, webSecurity: false,
experimentalFeatures: true experimentalFeatures: true,
nodeIntegration: true
}, },
show: false show: false
}) })
@ -33,7 +34,7 @@ exports.createMainWindow = function(icon) {
win.on('ready-to-show', _ => { win.on('ready-to-show', _ => {
win.show() win.show()
// win.openDevTools() win.openDevTools()
}) })
return win return win
@ -74,12 +75,15 @@ exports.createDesktopLrcWindow = function(screen) {
resizable: false, resizable: false,
alwaysOnTop: true, alwaysOnTop: true,
skipTaskbar: true, skipTaskbar: true,
x: (screen.size.width - 1024) / 2, x: (screen.width - 1024) / 2,
y: screen.size.height - 100, y: screen.height - 100,
transparent: true, transparent: true,
hasShadow: false, hasShadow: false,
thickFrame: false, thickFrame: false,
show: false show: false,
webPreferences: {
nodeIntegration: true
}
}) })
win.loadURL('app://local/desktop-lrc.html') win.loadURL('app://local/desktop-lrc.html')
@ -98,10 +102,13 @@ exports.createMiniWindow = function(screen) {
resizable: false, resizable: false,
alwaysOnTop: true, alwaysOnTop: true,
skipTaskbar: true, skipTaskbar: true,
x: screen.size.width - 320, x: screen.width - 320,
y: 0, y: 0,
thickFrame: false, thickFrame: false,
show: false show: false,
webPreferences: {
nodeIntegration: true
}
}) })
win.loadURL('app://local/mini-win.html') win.loadURL('app://local/mini-win.html')

View File

@ -14,17 +14,17 @@
<th>时长</th> <th>时长</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbodyclass="ac">
<tr <tr
:class="{active: it.id === curr}" :class="{active: it.id === curr}"
:for="it in list" :for="it in list"
:on-contextmenu="handleMenu(it, $index, $event)" :on-contextmenu="handleMenu(it, $index, $event)"
:dblclick="play(it, $index)"> :dblclick="play(it, $index)">
<td class="ac idx" :text="$index + 1 + '.'"></td> <td class="idx" :text="$index + 1 + '.'"></td>
<td :text="it.title"></td> <td :text="it.title | truncate(10)" :attr-title="it.title"></td>
<td class="ac" :text="it.artist"></td> <td :text="it.artist | truncate(10)" :attr-title="it.artist"></td>
<td class="ac" :text="it.album"></td> <td :text="it.album | truncate(10)" :attr-title="it.album"></td>
<td class="ac" :text="it.duration | time"></td> <td :text="it.duration | time"></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>

View File

@ -91,6 +91,14 @@
</section> </section>
<section class="block">
<span class="label">启用全局快捷键</span>
<div class="field">
<anot-switch :value="setting.allowGS"></anot-switch><span>(即使app在后台也能控制)</span>
</div>
</section>
<section class="block"> <section class="block">
<span class="label">关于Sonist</span> <span class="label">关于Sonist</span>
<div class="desc"> <div class="desc">

View File

@ -26,16 +26,16 @@
<th>时长</th> <th>时长</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbodyclass="ac">
<tr <tr
:for="it in list" :for="it in list"
:class="{active: it.kgHash === curr}" :class="{active: it.kgHash === curr}"
:dblclick="play(it, $index)"> :dblclick="play(it, $index)">
<td class="idx" :text="$index + 1 + '.'"></td> <td class="idx" :text="$index + 1 + '.'"></td>
<td :text="it.title"></td> <td :text="it.title | truncate(10)" :attr-title="it.title"></td>
<td class="ac" :text="it.artist"></td> <td :text="it.artist | truncate(10)" :attr-title="it.artist"></td>
<td class="ac" :text="it.album"></td> <td :text="it.album | truncate(10)" :attr-title="it.album"></td>
<td class="ac" :text="it.duration | time"></td> <td :text="it.duration | time"></td>
</tr> </tr>
</tbody> </tbody>
</table> </table>