2016年11月15日火曜日

指定日時に文章画像や実行ボタンなどを表示させるリマインダーアプリ

デスクトップPC(Windows)用リマインダ―アプリです。
一旦予定を立てたら、その予定に従って行動している最中は日時に対して注意を払わなくて済むように(次の行動に移る日時になったら画面上に必要なものが表示されるように)するためのものです。
よくあるリマインダーアプリと違うところは「表示させるものをJavaScriptなどで制御できる」という点です。


※動作画面や使用方法の解説については後日追記します。

ソース
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<HTA:Application
applicationName="time.hta"
SINGLEINSTANCE="yes"
>
<title>time</title>
<style type="text/css">
.w-max{width:100%;}
.max{width:100%;height:100%;}
.of-auto{overflow:auto;}
.b1{border:solid 1px #000;}
.no_mp{margin:0;padding:0px;}
.bc{border-collapse:collapse;}
.nowrap{white-space:nowrap;}
.btns button{margin:2px;width:100%;padding:2px;}
.ta-c{text-align:center;}
.hide{display:none;}
</style>
</head>
<body style="overflow:hidden;">
<table class="max bc">
<td><div id=div class="max of-auto b1" style="padding:20px 10px 20px 10px;"></div></td>
<td style="width:150px;">
<table class=bc>
<tr>
<td class=btns>
<button id=btn追加>通知追加</button>
<button id=btn間隔>間隔</button>
<button id=btn祝祭日>祝祭日</button>
<span><button id=btn警告>警告</button><span></span></span>
<button id=btnStartUp>StartUpフォルダ</button>
<button id=btn基準フォルダ>基準フォルダ</button>
</td>
</tr>
<tr><td id=tdカレンダー class=ta-c></td></tr>
</table>
</td>
</table>
</body>
<script>
ウィンドウ位置={
初期化:function(){
// resizeToとmoveToを実行した後の、設定値と実測値のズレを把握しておく。
var win=window, doc=document, cW='clientWidth', cH='clientHeight', L=400, T=400, W=400, H=400, obj, this_=this
var gets=function(){
return {
L : win.screenLeft || win.screenX || 0,
T : win.screenTop || win.screenY || 0,
W : win.innerWidth || doc.body[cW] || doc.documentElement[cW] || 0,
H : win.innerHeight || doc.body[cH] || doc.documentElement[cH] || 0
}
}
moveTo(L,T)
resizeTo(W,H)
obj = gets()
// 最小化状態ではウィンドウ位置パラメータが取得できない(onbeforeunloadイベント発生時に取得では遅い)ので、onresizeイベント発生時に逐一記録させる。(onmoveはHTAでは利用不可)
var fun = function(){
var obj_ = this_.obj = gets()
obj_.L += L - obj.L
obj_.T += T - obj.T
obj_.W += W - obj.W
obj_.H += H - obj.H
}
win.attachEvent('onresize', fun)
fun()
fs.FileExists(pathウィンドウサイズ) ? eval(Read(pathウィンドウサイズ)) : resizeTo(800,600)
},
保存:function(){
var obj=this.obj
Write(pathウィンドウサイズ, 'moveTo('+obj.L+'<screen.width ? '+obj.L+' : 0,'+obj.T+'<screen.height ? '+obj.T+' : 0);resizeTo('+obj.W+','+obj.H+');')
}
}
onload=function(){
基準フォルダ = fs.GetParentFolderName(location.href.replace(/^file:/,'').replace(/^\/+([A-Z]:)/,'$1')).replace(/%20/g,' ')
path情報 =基準フォルダ+'/情報'
path警告 =path情報+'/警告.js'
path祝祭日=path情報+'/祝祭日.txt'
pathウィンドウサイズ = path情報+'/ウィンドウサイズ.js'
setTimeout(function(){ウィンドウ位置.初期化()},1)
初期化()
}
onbeforeunload = function(){
ウィンドウ位置.保存()
}
初期化=function(){
初回起動時の初期化()
objElems.初期化()
カレンダー.表示()
setTimeout(function(){
// カレンダーを表示した状態で一旦画面表示を更新して、その状態でdivのoffsetWidthに合わせて
// tbl通知の横幅を調整する。そのためにsetTimeoutを使っている。
obj通知 .初期化(function(){
// obj通知.初期化完了後に実行される。
到達確認.初期化()
obj警告.初期化()
})
},1)
}
初回起動時の初期化=function(){
// 動作に必要なフォルダやファイルなどを作成する。
// 情報フォルダがあるなら初回起動時の初期化は済みと判定する。
if(fs.FolderExists(path情報)){return}
makeFile(path情報+'/arr通知.js','[]')
}
btn間隔.onclick=function(){
var str=prompt('各通知の通知日時に到達したかを確認する周期を入力してください。 単位:秒\n0を入力すると通知を停止します。', 到達確認.周期), num=str-0
if(str===null){return}
if(str.match(/(\d+)min/)){num = RegExp.$1*60}
if(str.match(/(\d+)hr/)){num = RegExp.$1*60*60}
if(!isFinite(num)){return alert('半角数字以外は設定できません\n\n'+str)}
Write(path情報+'/間隔', num)
到達確認.周期 = num
到達確認.リセット()
}
btnStartUp.onclick=function(){ shell.run('explorer '+shell.SpecialFolders("Startup").replace(/^([^ ]+ [^ ]+)$/g,'"$1"')) }
btn基準フォルダ.onclick=function(){ shell.run('explorer '+基準フォルダ.replace(/^([^ ]+ [^ ]+)$/g,'"$1"').replace(/\//g,'\\')) }
btn祝祭日.onclick=function(){
if(!fs.FileExists(path祝祭日)){ Write(path祝祭日, '2016/11/23,#fcc,勤労感謝の日\r\n2016/11/30,#cff,色んな意味でオフ\r\n') }
shell.run('notepad '+path祝祭日.replace(/^([^ ]+ [^ ]+)$/g,'"$1"'), 1, true)
カレンダー.load祝祭日()
カレンダー.表示()
}
obj警告={
初期化:function(){
if(!fs.FileExists(path警告)){
var str=this.func+'', 末尾=str.match(/(\n[ \t]+)\}$/)[1], reg=new RegExp(末尾,'g')
Write(path警告, str.replace(reg, '\n'))
}else{
this.func = readObj(path警告)
}
var this_=this
btn警告.onclick=function(){
shell.run('notepad '+path警告.replace(/^([^ ]+ [^ ]+)$/,'"$1"'), 1, true)
this_.func = readObj(path警告)
this_.判定()
}
this.判定()
},
func:function(dat){
// 通知日時として指定した日時が土日など、このプログラムを見る機会が無い日にちになっているような場合に気付けるようにするためのメソッド。
// 各通知に対してこのメソッドが実行される。
var 曜日=dat.getDay()
if(曜日==0 || 曜日==6){ return (曜日==0?'日':'土')+'曜日' }
if(com(function(){/*
2016/11/09
2016/11/10
*/}).indexOf(get年月日(dat))>0){ return 'PC使わない日' }
},
判定:function(){
var span=btn警告[NE], func=this.func, lnk=str2elem('<a href="#" style="margin:2px;font-size:20px;"><b>※</b></a>'), newL
span[iT] = ''
for0L(div[CN], function(i, tbl通知){
var inp=tbl通知.onfocus().div通知日時.childs.input通知日時, dat=str2dat(inp.value), ret
if(!dat || !(ret=func(dat))){ return tbl通知.style.backgroundColor = '' }
span[iB](newL=lnk.cloneNode(true))
newL.title = ret
newL.onclick=function(){ inp.select() }
tbl通知.style.backgroundColor = '#f99'
})
lnk.removeNode(true)
}
}
到達確認={
初期化:function(){ var p=path情報+'/間隔'; this.周期 = fs.FileExists(p) ? Read(p) : 5; this.リセット() },
周期:10,
リセット時:0,
リセット:function(t, sw到達直後){
// setTimeoutによる起動が確定しているfunctionが起動時に処理中断するようにする。
this.リセット時 = t = t || getTime()
// 周期が0以下でなければ、新しいループを開始する。
if(this.周期 <= 0){return btn間隔[iH]='間隔(<span style="font-size:20px;color:red;"><b>停止中</b></span>)'}
// this.周期が異常に短いとappActivate地獄(ログオフしないと他の何もactiveにできない状態)になってしまう。安全のために3秒以下の値の場合は3秒に変更する。
var 周期=this.周期, this_=this
if(周期 < 3){周期 = this.周期 = 3}
sw到達直後 ? setTimeout(function(){this_.ループ(t)}, 周期 * 1000) : this.ループ(t)
this.btn表示更新ループ(t)
},
ループ:function(リセット時){
// setTimeout実行時の「リセット時」と現在の「リセット時」の値が一致しなければ別のループが開始しているので、一致しない方は終了する。
if(リセット時!=this.リセット時){return}
var this_=this
// 各通知の通知日時を読み込んで、通知日時に到達しているものがあるか確認する&到達までの残り時間の表示を更新する。
var sw到達, time今=getTime()
for0L(div[CN], function(i,tbl通知){
var obj=tbl通知.onfocus(),C=obj.div通知日時.childs, inp=C.input通知日時, str=inp.value, span=C.span残り時間, dat=str2dat(str)
if(dat){ obj通知.残り時間表示更新(dat, span) }else{return true}
if(!sw到達 && dat && getTime(dat)<=time今){ sw到達=true }
})
if(sw到達){
// ウィンドウがアクティブか否かに関わらず、編集中か否かも考慮しない。時間が来たら通知を表示する。
appActivate()
div.scrollTop = 0
// ここでリセット時の値を更新しないと調整()の時に値が古くて調整できない。
return setTimeout(function(){this_.リセット(0, true)}, 1)
}
// 先頭の通知日時までの時間が周期より短い場合はリセット時の値を調整して、通知日時にループが実行されるようにする。
var tbl=div[FI], 周期=this.周期
while(tbl){
var dat先頭=str2dat(tbl.onfocus().div通知日時.childs.input通知日時.value)
if(!dat先頭){ break }
var time先頭=getTime(dat先頭), 残り時間=time先頭-getTime(), msec周期=周期*1000
if(残り時間<=0 || msec周期<残り時間){break}
リセット時 = this.リセット時 = getTime(new Date(time先頭-msec周期))
// リセット時の値を変更したらbtn表示更新ループを再起動しないと、そちらのループが止まったままになってしまう。
this.btn表示更新ループ(リセット時)
break
}
var 今=(getTime()-リセット時) % (周期*1000)
setTimeout(function(){ this_.ループ(リセット時) }, (周期*1000)-今)
},
btn表示更新ループ:function(リセット時){
if(リセット時!=this.リセット時){return}
var this_=this, 周期=Math.ceil(this.周期), 経過時間=Math.round((getTime()-this.リセット時) / 1000 % 周期)
btn間隔[iT] = '間隔('+経過時間+'/'+周期+')'
setTimeout(function(){ this_.btn表示更新ループ(リセット時) }, 1000)
},
調整:function(dat){
// 通知日時を変更した際、通知日時が3分後で到達確認の周期が5分だったりすると
// 指定された日時に通知が表示されない場合が生じてしまう。
// そのようなことがないように、このメソッドで調整する。
var 周期=this.周期*1000
if(!周期){return}
var 次回=this.リセット時+周期, 通知=getTime(dat)
if(次回 < 通知){ return }
this.リセット(getTime(new Date(通知-周期)))
}
}
obj通知={
初期化:function(fun通知初期化後){
// divのスクロールバーが表示された状態でのdiv.clientWidthが知りたいので、
// スクロールバーが表示されるようなエレメントをiBする。
var d=str2elem('div'), this_=this
div[iB](setProp(d, {style:{width:div.offsetWidth+10, height:div.offsetHeight+10}}, true))
var getNum=function(str){return str.match(/(\d+)/)[1]-0}, s=div.style
var get幅=function(){return div.clientWidth-getNum(s.paddingLeft)-getNum(s.paddingRight)-2}
setTimeout(function(){
// clientWidthの正しい値を取得するためにsetTimeoutを使っている。
// tbl通知の横幅をdiv.clientWidthの幅に合わせて調整しておく。
objElems.elems.tbl通知.elem.style.width = get幅()
div[FI].removeNode(true)
btn追加.onclick = function(){ this_.追加().div通知日時.childs.input通知日時.select(); this_.保存() }
for0L(readObj(path情報+'/arr通知.js'), function(i,v){ this_.追加(v) })
fun通知初期化後()
setScrollTop(div, div[FI])
},1)
window.attachEvent('onresize', function(){
// windowのリサイズイベントが発生したらtbl通知の横幅を調整する。
// style="width:100%"で勝手に調整してくれればラクだが、それだと横方向スクロールバーが表示されたりされなかったりで見栄えが悪い。
for0L(div[CN], function(i,tbl){ tbl.style.width=1 })
setTimeout(function(){ for0L(div[CN], function(i,tbl){ tbl.style.width=get幅() }) },1)
})
},
追加:function(arr内容){
arr内容 = arr内容 || [{}]
var objNew={
tbl通知 :objElems.getClone('tbl通知' ),
div通知日時:objElems.getClone('div通知日時'),
div通知 :objElems.getClone('div通知' )
}
var this_=this, newTBL=setProp(objNew.tbl通知.elem, {onfocus:function(){ return objNew }, onclick:this_.click})
div[iB](newTBL)
objNew.tbl通知.childs.td通知日時 [iB](objNew.div通知日時.elem)
objNew.tbl通知.childs.td通知ボタン[iB](objNew.div通知 .elem)
var fun=objNew.div通知.childs.button内容追加.onclick=function(node){ this_.内容TBL追加(objNew.tbl通知.childs.td通知内容, node || {}, objNew) }
for0L(arr内容, function(i,node){ fun(node) })
objNew.div通知.childs.button通知削除.onclick=function(){ this_.削除(objNew.tbl通知.elem) }
var inp通知日時=objNew.div通知日時.childs.input通知日時
setProp(inp通知日時, {
onchange:function(){
this_.日時変更(objNew.tbl通知.elem, this)
this_.保存()
obj警告.判定()
// 先頭のtbl通知だけ到達確認までの期間調整が必要な可能性がある。(下に行くほど到達までの期間が長いので、2番目以降のtblで調整するのは不適切。
var dat = str2dat(this.value)
;(objNew.tbl通知.elem[PR] && !dat) ? 0 : 到達確認.調整(dat)
},
value:arr内容.日時 || '通知日時',
onkeydown:function(){
var this_=this
switch(event.keyCode){
case 13:
this.onchange()
setScrollTop(div,objNew.tbl通知.elem)
setTimeout(function(){this_.select()},1)
return
case 27:return document.body.click() // ESC
}
},
onkeyup:function(){
var v=this.value, dat=str2dat(v, true)
if(v){ objNew.div通知日時.childs.span解釈後[iT] = dat ? get日時(dat) : v }
},
ondrag:function(){ this.onchange() },
onfocus:function(){ activeMenu.onclick(objNew.div通知日時.childs.span解釈後); this.onkeyup(); this.select() },
onclick:cancelBubble
})
this.日時変更(objNew.tbl通知.elem, inp通知日時)
setScrollTop(div,objNew.tbl通知.elem)
return objNew
},
click:function(){ activeMenu.onclick( this.onfocus().tbl通知.childs.td通知ボタン ) },
保存:function(){
var arr通知=[]
for0L(div[CN], function(i, tbl通知){
arr通知[i] = []
var obj=tbl通知.onfocus(), arr内容TBL=obj.tbl通知.childs.td通知内容[CN]
for0L(arr内容TBL, function(j, tbl内容){ arr通知[i][j] = tbl内容.onfocus() })
arr通知[i].日時 = obj.div通知日時.childs.input通知日時.value
})
Write(path情報+'/arr通知.js', obj2txt(arr通知))
},
内容TBL追加:function(td通知内容, node, objNew){
var new内容TBL=objElems.getClone('tbl通知内容'), E=new内容TBL.elem, C=new内容TBL.childs, this_=this
td通知内容[iB](E)
E.onclick=function(){ activeMenu.onclick(C.button編集[PA]) }
obj内容.btn編集_初期化(C.button編集, C.td通知内容, node, objNew)
node.種類 && obj内容.表示(node, C.td通知内容, objNew)
E.onfocus=function(){ return node }
C.tdDot[iT] = '・'
C.button△.onclick=function(){ if(E[PR]){ E[PA][iB](E[PR].removeNode(true), E[NE]); this_.保存() } }
C.button▽.onclick=function(){ E[PA][iB](E.removeNode(true), E[NE] ? E[NE][NE] : null); this_.保存() }
C.button削除.onclick=function(){ obj内容.btn削除_click(E) }
},
日時変更:function(tbl通知, inp日時){
var str=inp日時.value, dat=str2dat(str, true), obj=tbl通知.onfocus(), span=obj.div通知日時.childs.span残り時間
if(dat){
inp日時.value = get日時(dat)
inp日時.style.backgroundColor = ''
this.残り時間表示更新(dat, span)
}else{
inp日時.style.backgroundColor = '#ccc'
span[iT] = ''
}
div.insertBefore(tbl通知.removeNode(true))
for0L(div[CN], function(i, tbl通知_){
var str_=tbl通知_.onfocus().div通知日時.childs.input通知日時.value, dat_=str2dat(str_), sw
if(!dat && dat_){return}
if( dat){ if(!dat_ || getTime(dat) < getTime(dat_)){sw=true} }
if(!dat){ if(!dat_ && str < str_ ){sw=true} }
if(sw){ return div[iB](tbl通知.removeNode(true), tbl通知_) }
})
},
残り時間表示更新:function(dat, span){
var 長さ=getTime(dat)-getTime(), 表示, sw過去=長さ<0?true:false, sec=60
forIn({分:60, 時間:24, 日:31, ヵ月:12, 年:1}, function(単位, num){
表示 = Math.round(長さ / sec / 100) / 10 // 長さの中身はミリ秒単位。表示は少数第1位までになるようにround前と後に分けて3ケタ下げる
if(単位=='年' || (!sw過去 && 表示 < num) || (sw過去 && -(num) < 表示)){return 表示+=単位+'後'}
sec *= num
})
span[iT] = '約 '+表示+' '
span.style.backgroundColor = sw過去 ? '#fcc' : ''
},
削除:function(tbl){
var s=tbl.style, bg='backgroundColor', 元=s[bg], arr内容=tbl.onfocus().tbl通知.childs.td通知内容[CN], sw中断
s[bg] = '#f33'
if(confirm('削除しますか?')){
for0L(arr内容, function(i, tbl内容){ return sw中断 = obj内容.削除(tbl内容) })
if(!sw中断){
tbl.removeNode(true)
obj警告.判定()
return this.保存()
}
}
s[bg] = 元
}
}
objElems={
初期化:function(){
var elems=this.elems={}, elem
forIn(this.html, function(name, v){ elems[name] = {elem:elem=str2elem(v), childs:objElems.getChilds(elem)} })
},
getChilds:function(elem){
var obj={}
for0L('td/button/input/span/select'.split('/'),function(i,tagName){ for0L(getsETN(elem,tagName), function(i, elem){
if(elem[FI] && elem[FI].tagName && tagName!='select'){return}
switch(tagName){
case 'td':case 'button':case 'span': obj[tagName+elem[iT]] = elem; if(tagName=='td'){elem[iT]=''}; return
case 'input': obj[tagName+elem.value] = elem;return
case 'select': obj[tagName+selOps(elem).text] = elem; elem.options[0]=null; return
}
}) })
return obj
},
html:{
tbl通知 :'<table class="bc w-max b1" style="margin:20px 0px 20px 0px;"><td><table class="bc w-max"><tr><td>通知日時</td></tr><tr><td>通知内容</td></tr></table></td><td width=1 class=hide>通知ボタン</td></table>',
div通知日時:'<table class="w-max bc"><td width=1><input style="font-size:18px;width:180px;" class=ta-c value="通知日時"></td><td width=1><span class=nowrap style="display:none;background-color:#ccc;">解釈後</span></td><td><span class=nowrap>残り時間</span></td></table>',
tbl通知内容:'<table style="background-color:#ccf;border:solid 2px #fff;" class="bc w-max"><td width=1>Dot</td><td>通知内容</td><td width=1 class=hide><button>編集</button><button>削除</button><button>△</button><button>▽</button></td></table>',
div通知 :'<div><button>内容追加</button><button>通知削除</button></div>'
},
getClone:function(name){
var obj0=this.elems[name], elemClone=obj0.elem.cloneNode(true), objC={}, objRet={elem:elemClone, childs:objC}
forIn(obj0.childs, function(name, elem){
var arr=[], elemC=elemClone
while(elem[PR] || (elem[PA] && elem[PA].tagName)){
if(elem[PR]){
arr.unshift(NE); elem=elem[PR]
}else{
arr.unshift(FI); elem=elem[PA]
}
}
for0L(arr, function(i, str){ elemC=elemC[str] })
objC[name] = elemC
})
return objRet
}
}
obj内容={
編集:function(elemPA, node, btn, objNew){
var 種類=node.種類=node.種類 || forIn(this.種類, function(name,v){return 種類=name}), objElems={}, objN2H={ta:'<textarea wrap=off class=w-max></textarea>', inp:'<input class=w-max>', chk:'<input type="checkbox">'}, tbl=str2elem('<table class="bc w-max"><td><span class=nowrap></span></td></table>'), tbo=tbl[FI], trModel=tbo[FI].removeNode(true), newTR
elemPA[iH] = ''
forIn(this.種類[種類].編集.項目, function(name, v){
if(objN2H[v]){return void (objElems[name] = str2elem(objN2H[v]))}
if(v.match(/sel\(([^()]*)\)([^)]?)([^()]*)/)){
var 選択肢=RegExp.$1, 区切り文字=RegExp.$2, 初期値=RegExp.$3, sel=objElems[name]=str2elem('sel')
for0L(選択肢.split(区切り文字), function(i,v){ sel.options[i]=new Option(v); if(v==初期値){sel[sI]=i} })
return
}
objElems[name] = str2elem(v)
})
trModel[iB](trModel[FI].cloneNode())
trModel[FI].width = 1
elemPA[iB](tbl)
// 内容の種類を選択するセレクトボックスを先頭行にする。
var sel=str2elem('sel'), ops=sel.options, i=0, this_=this
tbo[iB]( newTR=trModel.cloneNode(true) )
newTR[CN][0][FI][iT] = '内容の種類'
newTR[CN][1][iB](sel)
forIn(this.種類, function(name,obj){ ops[i++]=new Option(name); if(name==種類){sel[sI]=i-1} })
sel.onchange=function(){
if(node.種類 && this_.種類[node.種類].削除 && this_.種類[node.種類].削除(node)){
return for0L(this.options, function(i,op){ if(op.text==node.種類){ sel[sI] = i; return true } })
}
node.種類=selOps(this).text
this_.編集(elemPA, node, btn, objNew)
}
// 種類に応じた編集項目を表示する。
forIn(objElems, function(name, elem){
tbo[iB]( newTR=trModel.cloneNode(true) )
newTR[CN][0][FI][iT] = name
newTR[CN][1][iB](elem)
})
this.種類[種類].編集.初期化(objElems, node)
btn[iT] = '表示'
btn.onclick=function(){ obj内容.保存(elemPA, node, objElems, btn, objNew) }
setScrollTop(div, objNew.tbl通知.elem)
},
保存:function(elemPA, node, objElems, btn, objNew){
this.種類[node.種類].保存(objElems, node)
obj通知.保存()
this.表示(node, elemPA, objNew)
this.btn編集_初期化(btn, elemPA, node, objNew)
},
削除:function(tbl){
// 上位(内容ではなく通知自体)を削除する場合などに呼ばれるメソッド(内容一件ごとの削除確認メッセージは出さない。)
// 添付ファイルを作成するタイプの内容の場合はファイルの削除なども必要なので
// 削除対象の内容に対して、そういった処理が必要なら実行させる。(内容の種類によっては「添付ファイルも削除されますが続行しますか?」のようなメッセージを出す場合もある。
var node=tbl.onfocus(), fun削除
if(!node.種類 || !(fun削除=this.種類[node.種類].削除) || !fun削除(node)){ tbl.removeNode(true); return false }
return true
},
btn削除_click:function(tbl){
// 削除ボタンを押した時に実行するメソッド(実行前の確認メッセージを出したりする)
if(tbl[PA][CN].length==1){return alert('内容を0個にすることはできません')}
tbl.style.backgroundColor = '#f33'
if(confirm('削除しますか?') && !this.削除(tbl)){ return obj通知.保存() }
tbl.style.backgroundColor = '#ccf'
},
表示:function(node, elemPA, objNew){ this.種類[node.種類].表示(node, elemPA, objNew) },
btn編集_初期化:function(btn, elemPA, node, objNew){ setProp(btn, {onclick:function(){ obj内容.編集(elemPA, node, this, objNew) }, innerText:'編集'})},
種類:{
txt:{
編集:{
項目:{文字列:'ta', サイズ:'sel(,0.5em,0.8em,1.0em,1.2em,1.8em,2.0em),', RGB:'inp', 下線:'chk', 太字:'chk', 斜体:'chk', 改行禁止:'chk'},
初期化:function(elems, node){
var prop=node.props
elems.文字列.style.height='200px'
if(!prop || !prop.style){return}
setTimeout(function(){
// HTMLエレメント作成後、続けて中身を追加すると内容が長文などの理由でレイアウトが崩れる場合があるので、setTimeoutで一旦区切っている。
elems.文字列.value = prop[iT]
for0L(elems.サイズ.options, function(i,op){ if(prop.style.fontSize==op.text){ elems.サイズ[sI] = i; return true } })
elems.RGB .value = prop.style.color
elems.下線 .checked = prop.style.textDecoration == 'underline' ? true : false
elems.太字 .checked = prop.style.fontWeight == 'bold' ? true : false
elems.斜体 .checked = prop.style.fontStyle == 'italic' ? true : false
elems.改行禁止.checked = prop.style.whiteSpace == 'nowrap' ? true : false
},1)
}
},
保存:function(elems, node){
node.props={
innerText : elems.文字列.value,
style:{
fontSize :selOps(elems.サイズ).text,
color :elems.RGB.value,
textDecoration:elems.下線 .checked ? 'underline' : 'none',
fontWeight :elems.太字 .checked ? 'bold' : 'normal',
fontStyle :elems.斜体 .checked ? 'italic' : 'normal',
whiteSpace :elems.改行禁止.checked ? 'nowrap' : 'normal'
}
}
},
表示:function(node, elemPA){
elemPA[iH] = '<span></span>'
setProp(elemPA[FI], node.props, true)
}
},
lnk:{
編集:{
項目:{文字列:'ta', リンク先:'inp', サイズ:'sel(,0.5em,0.8em,1.0em,1.2em,1.8em,2.0em),', RGB:'inp', 下線:'chk', 太字:'chk', 斜体:'chk', 改行禁止:'chk'},
初期化:function(elems, node){
var prop=node.props
if(!prop || !prop.style){return}
setTimeout(function(){
elems.文字列 .value = prop[iT]
elems.リンク先.value = prop.href
for0L(elems.サイズ.options, function(i,op){ if(prop.style.fontSize==op.text){ elems.サイズ[sI] = i; return true } })
elems.RGB .value = prop.style.color
elems.下線 .checked = prop.style.textDecoration == 'underline' ? true : false
elems.太字 .checked = prop.style.fontWeight == 'bold' ? true : false
elems.斜体 .checked = prop.style.fontStyle == 'italic' ? true : false
elems.改行禁止.checked = prop.style.whiteSpace == 'nowrap' ? true : false
},1)
}
},
保存:function(elems, node){
node.props={
innerText : elems.文字列 .value,
href : elems.リンク先.value,
style:{
fontSize :selOps(elems.サイズ).text,
color :elems.RGB.value,
textDecoration:elems.下線 .checked ? 'underline' : 'none',
fontWeight :elems.太字 .checked ? 'bold' : 'normal',
fontStyle :elems.斜体 .checked ? 'italic' : 'normal',
whiteSpace :elems.改行禁止.checked ? 'nowrap' : 'normal'
}
}
},
表示:function(node, elemPA){
elemPA[iH] = '<a></a>'
setProp(elemPA[FI], node.props, true)
}
},
runBtn:{
編集:{
項目:{文字列:'inp', path:'inp', パラメータ:'inp', 作業フォルダ:'inp', icon:'inp'},
初期化:function(elems, node){
var prop=node.props
if(!prop){return}
setTimeout(function(){
elems.文字列 .value = prop[iT] || ''
elems.path .value = prop.path || ''
elems.パラメータ .value = prop.パラメータ || ''
elems.作業フォルダ.value = prop.作業フォルダ || ''
elems.icon .value = prop.icon || ''
},1)
}
},
保存:function(elems, node){
node.props={
innerText : elems.文字列 .value,
path : elems.path .value,
パラメータ : elems.パラメータ .value,
作業フォルダ: elems.作業フォルダ.value,
icon : elems.icon .value
}
},
表示:function(node, elemPA){
elemPA[iH] = '<button></button>'
var btn=elemPA[FI], P=node.props, icon=P.icon
if(icon){
btn[iH] = '<img>'
var img=btn[FI]
if(icon.indexOf('data:image')==0 || fs.FileExists(icon) || icon.indexOf('http')==0){
img.src = icon
}else{
img.src = path情報+'/画像/'+icon
}
img.alt = P.innerText
}else{
btn[iT] = P.innerText
}
btn.onclick = function(){ shellRun(P.path, P.パラメータ, P.作業フォルダ); cancelBubble() }
}
},
func:{
編集:{
項目:{func:'ta', notepad:'btn'},
初期化:function(elems, node){
var prop=node.props, func=prop ? prop.func : 0
func = func || インデント調整(function(elemPA, objNew){
elemPA[iH] = '<button>次の9:00にする</button>'
elemPA[FI].onclick=function(){
var inp=objNew.div通知日時.childs.input通知日時,dat=str2dat('9:00',true)
// 現在時刻の次の9:00が土日のどちらかなら、月曜の日時になるよう修正する。
while(dat.getDay()==0 || dat.getDay()==6){ dat=new Date(dat.getTime()+1000*60*60*24) }
inp.value = get日時(dat)
inp.onchange()
}
}+'')
elems.func.style.height='100px'
elems.func.wrap='off'
elems.notepad.onclick=function(){
var path=gsf2+'/notepad_'+getTime()+'.txt'
Write(path, elems.func.value)
shell.run('notepad '+path, 1, true)
elems.func.value = Read(path)
fs.DeleteFile(path)
}
elems.notepad[iT] = '起動'
setTimeout(function(){ elems.func.value = func },1)
}
},
保存:function(elems, node){ node.props={func:elems.func.value} },
表示:function(node, elemPA, objNew){
elemPA[iT] = ''
var P=node.props, func
if(!P.func){return}
eval('func = '+P.func)
try{ func(elemPA, objNew) }catch(e){ elemPA[iT] = 'error' }
}
},
contentEditable:{
編集:{
項目:{編集領域:'<div class=b1 style="height:100px;" contentEditable=true></div>', btns:'div'},
初期化:function(elems, node){
var prop=node.props || {}
elems.btns[iH] = '<button>メモ帳で編集</button><button>ペイント</button>'
elems.btns[CN][0].onclick=function(){
var path=gsf2+'/notepad_'+getTime()+'.txt'
Write(path, elems.編集領域[iH])
shell.run('notepad '+path, 1, true)
elems.編集領域[iH] = Read(path)
fs.DeleteFile(path)
}
elems.btns[CN][1].onclick=function(){shell.run('mspaint')}
setTimeout(function(){ elems.編集領域[iH] = prop[iH] || '' },1)
}
},
保存:function(elems, node){ node.props={innerHTML:elems.編集領域[iH]} },
表示:function(node, elemPA, objNew){ elemPA[iH] = node.props[iH] || '' }
},
notesLink:{
編集:{
項目:{貼り付け場所:'<div class=b1 style="height:100px;" contentEditable=true></div>', 文字列:'inp'},
初期化:function(elems, node){
var prop=node.props || {}, lnk=''
if(prop.文字列){ lnk=('<a href="'+prop.href+'">'+prop.文字列+'</a>') }
setTimeout(function(){
elems.貼り付け場所[iH] = lnk
elems.文字列.value = prop.文字列 || ''
},1)
}
},
保存:function(elems, node){
var lnk=getsETN(elems.貼り付け場所, 'a'), str=elems.文字列.value, 文字列=lnk ? (str ? str : lnk[0][iT]) : ''
node.props={href:lnk ? lnk[0].href : '', 文字列:文字列}
},
表示:function(node, elemPA, objNew){
if(!node.props || !node.props.文字列){ return elemPA[iT]='' }
elemPA[iH] = '<button>notes</button> <span>'+node.props.文字列+'</span>'
elemPA[FI].onclick=function(){
cancelBubble()
var path=gsf2+'/notesLink_'+getTime()+'.lnk', lnk=shell.CreateShortcut(path)
lnk.targetPath = node.props.href
lnk.save()
shell.run(path)
fs.DeleteFile(path)
}
}
},
next:{
編集:{
項目:{選択肢:'<input class=w-max>', 警告判定日時をスキップ:'chk'},
初期化:function(elems, node){
var prop=node.props || {選択肢:'10:00,16:00', 警告判定日時をスキップ:true}
elems.警告判定日時をスキップ.checked = prop.選択肢
setTimeout(function(){ elems.選択肢.value = prop.選択肢 },1)
}
},
保存:function(elems, node){ node.props={選択肢:elems.選択肢.value, 警告判定日時をスキップ:elems.警告判定日時をスキップ.checked} },
表示:function(node, elemPA, objNew){
if(!node.props || !node.props.選択肢){ return elemPA[iT]='' }
elemPA[iH] = '<span>next→</span><button></button>'
var btn=elemPA[FI][NE].removeNode(true), newBTN
var fun=function(){
cancelBubble()
var dat=str2dat(this[iT], true), inp=objNew.div通知日時.childs.input通知日時
while(obj警告.func(dat)){
if(!node.props.警告判定日時をスキップ){break}
dat = new Date(getTime(dat)+1000*60*60*24)
}
inp.value = get日時(dat)
inp.onchange()
}
for0L(node.props.選択肢.split(','), function(i,str){
elemPA[iB](newBTN=btn.cloneNode(true))
newBTN[iT] = str
newBTN.onclick = fun
})
}
},
file:{
編集:{
項目:{ファイル:'<iframe class=w-max style="height:100px;">'},
初期化:function(elems, node){
var prop=node.props || (node.props={path添付:''})
if(!prop.path添付 || !fs.FolderExists(prop.path添付)){ fs.CreateFolder(prop.path添付 = path情報+'/添付ファイル_'+getTime()) }
elems.ファイル.src = prop.path添付
}
},
保存:function(elems, node){ node.props={path添付:elems.ファイル.src} },
削除:function(node){
if(node.props && node.props.path添付 && !confirm('添付されているファイルも削除されますが継続しますか?')){return true}
fs.DeleteFolder(node.props.path添付)
},
表示:function(node, elemPA, objNew){
if(!node.props){ return elemPA[iT]='ファイル無し' }
elemPA[iH] = '<iframe class=w-max style="height:100px;" src="'+node.props.path添付+'"></iframe>'
}
}
}
}
カレンダー={
load祝祭日:function(){
var obj祝祭日=this.obj祝祭日={}
if(!fs.FileExists(path祝祭日)){return obj祝祭日}
for0L(RAS(path祝祭日), function(i,line){
if(!line.length){return}
var arr=line.split(',')
obj祝祭日[ get年月日(str2dat(arr[0])) ] = {color:arr[1], title:arr[2]}
})
return obj祝祭日
},
get祝祭日:function(){ return this.obj祝祭日 || this.load祝祭日() },
表示:function(dat){
// 2ヵ月分のカレンダーを表示する。
dat = dat || (new Date())
var tbl=str2elem('<table border=1 class=bc><td class=ta-c></td></table>'), tbo=tbl[FI], tr=tbo[FI].removeNode(true), td=tr[FI].cloneNode(true), newTR, newTD, tds
tdカレンダー[iT] = ''
tdカレンダー[iB](tbl)
tbo[iB](newTR=tr.cloneNode(true))
newTD=newTR[FI]
newTD.colSpan=7
newTD[iH] = '<table class="bc w-max"><td width=1><button>←</button></td><td><input class="w-max ta-c" value="'+get年月日(dat).slice(0,7)+'"></td><td width=1><button>→</button></td></table>'
var btns=getsETN(newTD,'button'), inp=getsETN(newTD,'input')[0]
btns[0].onclick = function(){ var arr=inp.value.split('/'); arr[1]--; inp.value=arr.join('/'); inp.onchange() }
inp.onchange=function(){
if(!this.value){return this.value=get年月日(dat).slice(0,7)}
カレンダー.表示(str2dat(this.value))
}
btns[1].onclick = function(){ var arr=inp.value.split('/'); arr[1]++; inp.value=arr.join('/'); inp.onchange() }
for(var i=0;i<6;i++){ tr[iB](td.cloneNode(true)) }
tr[CN][0].style.backgroundColor = '#fcc'
tr[CN][6].style.backgroundColor = '#ccf'
tbo[iB](newTR=tr.cloneNode(true))
for0L('日月火水木金土'.split(''), function(i,曜日){newTR[CN][i][iT] = 曜日})
var obj祝祭日=this.get祝祭日()
var fun=function(dat){
var arr=get年月日(dat).split('/'), 月=arr[1]-1, 日, 曜日, now年月日=get年月日(), dat_=new Date(), 年月日_
dat_.setFullYear(arr[0], 月, 1)
while(dat_.getMonth()==月){
日 = dat_.getDate()
曜日 = dat_.getDay()
if(日==1 || 曜日==0){ tbo[iB](newTR=tr.cloneNode(true)) }
newTR[CN][曜日][iT] = 日
年月日_ = get年月日(dat_)
if(obj祝祭日[年月日_]){ newTR[CN][曜日].style.backgroundColor = obj祝祭日[年月日_].color; newTR[CN][曜日].title = obj祝祭日[年月日_].title }
if(年月日_==now年月日){ newTR[CN][曜日].style.backgroundColor = '#cfc' }
dat_.setDate(日+1)
}
return dat_
}
var dat_ = fun(dat)
tbo[iB](newTR=tbo[FI].cloneNode(true))
newTR[FI][iT] = get年月日(dat_).slice(0,7)
fun(dat_)
}
}
activeMenu={
// アクティブ状態(最後にクリックされたエレメント)に対応するメニューだけを可視状態にして、他は不可視にするためのオブジェクト。
onclick:function(elem, onblur){
cancelBubble()
this.elem && !isElem同一(elem, this.elem) && this.hide()
;(this.elem=elem).style.display = 'block'
this.onblur = onblur
},
hide:function(){ this.elem && (this.elem.style.display='none') && this.onblur && this.onblur() }
}
document.body.onclick=function(){ activeMenu.hide() }
インデント調整=function(str){
var 末尾=str.match(/(\n[ \t]+)[^ \t]*$/)[1], reg=new RegExp(末尾,'g')
return str.replace(reg, '\n')
}
;(function(){
// 指定したエレメントが画面上に表示されるように、scrollTopの値を調整する関数。
var getSum=function(elem, param){ var sum=0; while(elem){ sum+=elem[param]; elem=elem.offsetParent }; return sum }
var getNum=function(str){ return str.match(/(\d+)/)[1]-0 }
var main=function(elemPA, elem){
// elemPAはスクロール可能なエレメント。elemはその中の表示したいエレメント。
var elemOT=getSum(elem,'offsetTop'), elemST=getSum(elem,'scrollTop'), FCT=getSum(elemPA[FI],'offsetTop'), elemOH=elem.offsetHeight, CH=elemPA.clientHeight
if((FCT+CH)<(elemOT+elemOH)){
// 上端は表示領域内部だけど、下端がはみ出ている場合(FCT+CH)<(elemOT+elemOH)
elemPA.scrollTop = elemOT-FCT-CH+elemOH+getNum(elemPA.style.paddingTop)+getNum(elemPA.style.paddingBottom)
}
if((elemOT-elemST)<FCT){
// 表示領域より↑にいる場合
elemPA.scrollTop = elemOT-FCT
}
}
// エレメント作成直後などの場合、clientHeightなどの正確な値が取得できないので、一旦描画処理を挟むようにsetTimeout経由で起動する。
setScrollTop = function(elemPA, elem){ setTimeout(function(){ main(elemPA, elem) }, 1) }
})();
isElem同一=function(elemA, elemB){
while(1){
if(elemA.tagName!=elemB.tagName){return false}
if(elemA[PR]){
if(!elemB[PR]){return false}
elemA = elemA[PR]
elemB = elemB[PR]
continue
}else if(elemB[PR]){ return false }
if(elemA[PA]){
if(!elemB[PA]){return false}
elemA = elemA[PA]
elemB = elemB[PA]
continue
}else if(elemB[PA]){ return false }
return true
}
}
str2elem=function(str){
var m=arguments.callee, div=m.div || (m.div=document.createElement('div')), obj=m.obj || {div:'<div></div>', inp:'<input>', chk:'<input type="checkbox">', sel:'<select></select>', tbl:'<table><td></td></table>', btn:'<button></button>'}
div[iH] = obj[str] || str
return div[FI].removeNode(true)
}
com=function(f){return (f+'').match(/\/\*([^\v]*)\*\//)[1]}
getClone=function(id, dist){ var elem=window[id].cloneNode(true); elem.id=''; dist[iB](dist); return elem }
readObj=function(path){
var obj,str
try{str = Read(path)}catch(e){str = ReadU(path)}
try{eval('obj='+str)}catch(e){alert('readObjでエラー発生。\n'+path+'\n\nstr(先頭3行)['+(str+'').split('\n').slice(0,3).join('\n')+']\n\ncaller:'+arguments.callee.caller)}
return obj
}
makeFile=function(path,str){
// ファイル出力時に、途中階層のフォルダがなければ作成する関数。
var arr=path.replace(/\\/g,'/').split('/'), L=arr.length, i=1, path_
for(;i<L;i++){ !fs.FolderExists(path_=arr.slice(0,i).join('/')) && fs.CreateFolder(path_) }
Write(path, str)
}
shellRun=function(path, パラメータ, 作業フォルダ){
// HTAから直接shell.runすると、以下のようになる。それを回避するための関数。
// 呼出し先が起動する前にhta画面を最小化 → 呼出し先起動 → HTA画面の最小化解除
var swLnk
if(パラメータ || 作業フォルダ){
swLnk = true
var pathLnk=gsf2+getTime()+'.lnk', lnk=shell.CreateShortcut(pathLnk)
lnk.targetPath = path
lnk.Arguments = パラメータ
lnk.WorkingDirectory = 作業フォルダ
lnk.save()
path = pathLnk
}
var p=gsf2+'/shellRun'+getTime()+'.wsf', p0=p+'.txt'
var fun=function(){ (new ActiveXObject('WScript.Shell')).run((new ActiveXObject('Scripting.FileSystemObject')).OpenTextFile(WScript.Arguments(0)).ReadAll()) }
Write(p , ['<job>','<'+'script>','('+fun+')()','<'+'/script>','</job>'].join('\r\n'))
Write(p0, path)
shell.run(p+' '+p0, 0, true)
fs.DeleteFile(p)
fs.DeleteFile(p0)
// ショートカットファイルを作成した場合は、そのファイルも削除する。
swLnk && fs.DeleteFile(path)
}
// 以下、汎用関数など。
AXO = function(name){return new ActiveXObject(name)}
fs = AXO('Scripting.FileSystemObject')
shell = AXO('WScript.Shell')
gsf2 = fs.GetSpecialFolder(2)
Read = function(p){var o=fs.OpenTextFile(p), s=o.AtEndOfStream?'':o.ReadAll(); o.Close(); return s}
ReadU = function(p){var o=fs.OpenTextFile(p,1,false,true), s=o.AtEndOfStream?'':o.ReadAll(); o.Close(); return s}
Write = function(path,v){
var flagErr
try{ with(fs.CreateTextFile(path)){Write(v);return Close()} }catch(e){ flagErr=true }
if(flagErr){
// UTF-8でなければ保存できない文字列を含んでいる場合、エラーになる。試しにUTF-8で保存してみる。
try{ with(fs.CreateTextFile(path,true,true)){Write(v);return Close()} }catch(e){
// 書き込み禁止などによるエラーと推測される。
alert('下記Pathファイルを保存できませんでした。\n'+path+'\n\nv(先頭3行)['+(v+'').split('\n').slice(0,3).join('\n')+']\n\ncaller:'+arguments.callee.caller)
}
}
}
RAS = function(p){return Read(p).split('\r\n')}
for0L = function(arr,fun){for(var i=0,L=arr.length,res;i<L;i++){if(res=fun(i,arr[i])){return res}}}
forIn = function(obj,fun){var name,res; for(name in obj){if(res=fun(name, obj[name])){return res}}}
fun = function(path){return path.indexOf(' ')<0 ? path : ('"'+path+'"')}
selOps= function(sel){return sel.options[sel[sI]]}
getDirList = function(path, swFile){
var pathTMP=gsf2+'/getDirList_'+getTime()+'.txt', arr
shell.Run('cmd /C dir /A'+(swFile?'-':'')+'D /B "'+path+'" > "'+pathTMP+'"' , 0 , true)
arr = fs.FileExists(pathTMP) ? RAS(pathTMP) : []
arr.pop()
try{fs.DeleteFile(pathTMP)}catch(e){}
return arr
}
getFileList=function(path){return getDirList(path, true)}
me = function(){ return arguments.callee.caller }
cancelBubble=function(){event.cancelBubble = true}
setProp=function(objTarget, objProp, swSub){
forIn(objProp, function(name, v){
if(swSub && forIn(objProp[name], function(n,v){return setProp(objTarget[name] || (objTarget[name]={}), objProp[name], true)}) ){ return }
objTarget[name] = objProp[name]
})
return objTarget
}
getsETN=function(elem,name){return elem.getElementsByTagName(name)}
iT='innerText', iH='innerHTML', PA='parentNode', FI='firstChild', CN='childNodes', NE='nextSibling', PR='previousSibling', sI='selectedIndex', iB='insertBefore'
for0L = function(arr,fun){for(var i=0,L=arr.length,res;i<L;i++){if(res=fun(i,arr[i])){return res}}}
forIn = function(obj,fun){var name,res; for(name in obj){if(res=fun(name, obj[name])){return res}}}
;(function(){
var rYn=/\n/g, rYr=/\r/g, rDQ=/[""]/g, rArr=/Array/, rDat=/Date/, YnYt='\n\t', date_='date', num='number', boo='boolean', und='undefined', str='string', obj='object', nul='null', fun='function', DQ='"', YY='\\\\', YYn='\\n', YYr='\\r', YYdq='\\"', headF='(function(){', varObj='\r\nvar obj = ', footF='\r\n\treturn obj\r\n})()', arr='[]', newDate='new Date("', datEnd='")'
var str0='\r\nobj[', str1='] = ', len0='', str2='{', str3='\r\n}', comma=',', YrYn='\r\n', str4=' : '
var forIn0=function(o){var s=len0; forIn(o, function(n,v){s+= str0 + me(n) + str1 + me(v)}); return s}
var forIn1=function(o){var s=len0; forIn(o, function(n,v){s+= (s?comma:len0) + YrYn + me(n) + str4 + me(v)}); return s}
var funcインデント調整=function(fun){ var str=fun.toString(), 最終行=str.match(/([\t ]*)\}$/)[1], reg=new RegExp('\\n'+最終行,'g'); return str.replace(reg,'\n') }
var me = obj2txt = function(o){
if(!o && o!=0 && o!=null && typeof(o)!=date_){ return }
switch(typeof(o)){
case num:case boo:case und:return o
case str:return DQ + o.replace(/\\/g,YY).replace(rYn,YYn).replace(rYr,YYr).replace(rDQ,YYdq) + DQ
case obj:
if(o==null){ return nul }
if(o.constructor.toString().match(rArr)){
return headF + (varObj + arr + forIn0(o)).replace(rYn,YnYt) + footF
}else if(o.constructor.toString().match(rDat)){
return headF + (varObj + newDate + o + datEnd + forIn0(o)).replace(rYn,YnYt) + footF
}else{
return str2 + forIn1(o).replace(rYn,YnYt) + str3
}
case fun:
return (headF + (varObj + funcインデント調整(o) + forIn0(o)).replace(rYn,YnYt) + footF)
case date_:
return headF + varObj + newDate + o + datEnd + forIn0(o) + footF
}
}
})();
;(function(){
var D =function(d){return d || new Date()}
var T =function(t){return new Date(t)}
var 二桁 =function(v){var s='0'+v,L=s.length; return s.slice(L-2,L)}
get年月日=function(d){ d=D(d); return [ d.getFullYear(), 二桁(d.getMonth()+1), 二桁(d.getDate() )].join('/') }
get時分秒=function(d){ d=D(d); return [二桁(d.getHours()) , 二桁(d.getMinutes()), 二桁(d.getSeconds())].join(':') }
get日時 =function(d){ d=D(d); return this.get年月日(d)+' '+this.get時分秒(d)}
getTime =function(d){ return (d || (new Date())).getTime()}
str2dat=function(str, swNext){
var isOld=function(datA){return getTime(datA) <= getTime() ? true : false}
var J=function(arr,arrI,chr){var a=arrI?[]:arr; for0L(arrI,function(i,v){a[i] = arr[v]}); return a.join(chr)}
var getNext=function(dat, i){
var arr=get日時(dat).replace(/[ \/:]/g,',').split(','); arr[i]++
return T(J(arr,[0,1,2],'/')+' '+J(arr,[3,4,5],':'))
}
var RJ=function(arr,chr){var str=''; for0L(arr,function(i,v){arr[i]=RegExp['$'+v]}); return arr.join(chr)}
var funNext=function(swNext, dat, i){ return swNext ? (isOld(dat) ? getNext(dat, i) : dat) : dat }
str2dat=function(str, swNext){
var d=D(), newD, R=RegExp, Y=d.getFullYear(), m=function(reg){var reg=reg.replace(/(\{[,\d]+\})/g,'\\d$1'); return str.match(new RegExp(reg))}
if(m('({4}/{1,2}/{1,2} {1,2}:{1,2})' )){ return T(R.$1) }
if(m('({4}/{1,2}/{1,2})' )){ return T(R.$1) }
if(m('({4}/{1,2})' )){ return T(R.$1+'/1') }
if(m( '({1,2}/{1,2} {1,2}:{1,2})' )){ return funNext(swNext, T(Y+'/'+R.$1), 0) }
if(m( '({1,2}/{1,2} )({1,2})({1,2})' )){ return funNext(swNext, T(Y+'/'+R.$1+R.$2+':'+R.$3), 0) }
if(m( '({1,2}/{1,2})' )){ return funNext(swNext, T(Y+'/'+R.$1 +' '+get時分秒() ), 0) }
if(m('({4})/?({2})/?({2}) ?({2}):?({2})' )){ return T(RJ([1,2,3],'/')+' '+RJ([4,5],':')) }
if(str.indexOf(Y)==0 && m('({4})/?({2})/?({2})')){ return T(RJ([1,2,3],'/')) }
if(m( '({1,2})/?({2}) *({2}):?({2})' )){ return funNext(swNext, T([Y,R.$1,R.$2].join('/')+' '+RJ([3,4],':')), 0) }
if(m('({1,2}):({1,2})') || m('({2})({2})')){ return funNext(swNext, T(get年月日() +' '+R.$1+':'+R.$2), 2) }
}
return str2dat(str, swNext)
}
})();
appActivate=function(){
var doc=document
// 既にアクティブならappActivateの必要がない。
if(doc.hasFocus()){return}
// appActivateが成功するとwindow.onfocusが発生するが、それだけではhasFocs=trueにならないので
// hasFocusメソッドでアクティブか否かを判別できるようにwindow.onfocusイベントが起きたらdocument.body.focusを実行するようにしておく。
onfocus = function(){ doc.body.focus() }
shell.run(location.href, 1, true)
setTimeout(function(){
if(doc.hasFocus()){return}
// appActivateを実行した時に、対象のウィンドウが他のウィンドウに隠れているだけなら最前面に表示されるが
// 最小化している場合はタスクバー上のボタンがアクティブになるだけで、ウィンドウは表示されない。
// user32.dllのSetForegroundWindowメソッドを使えば最小化状態から復帰させることもできそうだが、
// HTAからDLLを利用するのは容易ではない。(ExcelのDLL呼出しを使う or PowerShellのスクリプト実行許可を管理者権限で設定しておく or 自作DLLを作ってrundll32から呼べるようにする)とか。
// 自身を再起動しても良いなら、下記2行で最小化状態からでも最前面に表示できる。ただし編集中のデータは失われる。。
shell.run(location.href)
window.close()
},1)
}
</script>
</html>
view raw time.hta hosted with ❤ by GitHub


このプログラムを実行している状態で、通知日時に達すると、プログラムが最小化されていたり他のプログラムの後ろに隠れていたとしてもAppActivateメソッドによって最前面に表示されます。

0 件のコメント:

コメントを投稿