2016年6月23日木曜日

ネットワークドライブ上で動作する簡易的な伝言板

テキストファイルを使った簡易的な伝言板を作成しました。
起動中は定期的にファイルが読み込まれて、常に最新の情報が表示されます。
伝言板の画面上でテキストを編集すると、即座にファイルに反映されます。(ファイルを選ぶ、開く、保存などの手間を省略しています)
プログラムも出力ファイルもすべてがメモ帳で表示・編集可能なので自由に改造できます。




[利用イメージ]
何人かで予定を知らせあったり共用備品についてコメントを書いたりして使います。
例えば以下の場合、山田さんがテキストを編集した時に佐藤さんがプログラムを起動済みだと最長1分間程度は、佐藤さんのPCの画面上では山田さんの欄が編集前の古い情報になっていますが1分経つと自動的に最新の情報が読み込まれて、佐藤さんのPC側は何も操作しなくても山田さんの編集内容が反映されます。



ソースは以下
<html>
<head>
<title>伝言板</title>
<style type="text/css">
.max{width:100%;height:100%;}
.w-max{width:100%;}
.bc{border-collapse:collapse;}
.no_mp{margin:0px;padding:0px;}
.ta-c{text-align:center;}
.of-auto{overflow:auto;}
.of-hidden{overflow:hidden;}
.nowrap{white-space:nowrap;}
.pad5{padding:5px;}
</style>
</head>
<body>
<table class="bc max">
<tr style="height:10px;">
<td><div id=divプログレスバー style="background-color:#00f; width:1px;"></div></td>
</tr>
<tr style="height:1px;">
<td>
<button>追加</button>
<button>自動更新を停止</button>
<button>縦幅の最適化</button>
</td>
</tr>
<tr>
<td>
<div id=divメイン class="max of-auto pad5" style="border:solid 1px #000;">
<table class="bc w-max" style="border:solid 1px #000; display:none; margin:5px 0px 5px 0px;">
<tr><td><input class=w-max></td></tr>
<tr><td><textarea class=w-max></textarea></td></tr>
</table>
</div>
</td>
</tr>
</table>
</body>
<script>
fs = new ActiveXObject('Scripting.FileSystemObject')
shell = new ActiveXObject('WScript.Shell')
Read = function(p){var r=fs.OpenTextFile(p),v=r.AtEndOfStream?'':r.ReadAll();r.Close();return v}
RAS = function(p){return Read(p).split('\r\n')}
Write = function(path,v){with(fs.CreateTextFile(path)){Write(v);Close()}}
gsf2 = fs.GetSpecialFolder(2)
me = function(){return arguments.callee.caller}
gets = function(parentElem, name){return parentElem.getElementsByTagName(name)}
// dirコマンドによるファイル(フォルダ)リスト取得
;(function(){
var funCore=function(path, swDir){
var pathTMP=gsf2+'/'+fs.getTempName()+'.txt'
shell.run('cmd /C dir /A'+(swDir?'':'-')+'D /B "'+path+'" > "'+pathTMP+'"', 0, true)
var arr=RAS(pathTMP)
arr.pop()
fs.deleteFile(pathTMP)
return arr
}
getFileList=function(path){ return funCore(path) }
getDirList =function(path){ return funCore(path,true) }
})();
// ネットワークドライブ上で起動した場合、dirコマンドによるファイル(orフォルダ)の一覧を取得したい場合
// フルパスを指定しないといけない。その時などに必要になるフルパスを用意しておく。
基準フォルダ = fs.GetFolder('.').path
// 伝言板に関する各種情報を入れておくフォルダのPathを決めておく。
path情報 = 基準フォルダ + '/情報'
path伝言 = path情報 + '/伝言'
// 多用するプロパティ名を短縮表記できるようにしておく。
iT='innerText', FI='firstChild', CN='childNodes', PA='parentNode'
onload=function(){
書き込み権限の確認()
if(!書き込み権限の確認.sw権限有り){
alert('書き込み権限がありません。読み込みは可能ですが、書き込みはできません。')
gets(divメイン,'input' )[0].disabled = true
gets(divメイン,'textarea')[0].disabled = true
}
// 各ボタンのonclickに関数を紐づける。
btn初期化()
var arr=getFileList(path伝言)
for(var i=0,L=arr.length;i<L;i++){ tbl追加(path伝言+'/'+arr[i]) }
自動更新()
}
書き込み権限の確認 = function(){
try{
if(!fs.FolderExists(path情報)){ fs.CreateFolder(path情報) }
if(!fs.FolderExists(path伝言)){ fs.CreateFolder(path伝言) }
}
catch(e){
alert('動作に必要なフォルダを作成できません。')
window.close()
}
try{
var pathTMP=path情報+'/書き込みテスト'
Write(pathTMP, 123)
fs.DeleteFile(pathTMP)
}
catch(e){ return }
me().sw権限有り = true
}
btn初期化=function(){
var btns=document.getElementsByTagName('button'), obj={}
for(var i=0,L=btns.length;i<L;i++){
obj[btns[i][iT]] = btns[i]
}
obj.追加 .onclick = btn追加
obj.自動更新を停止.onclick = btn自動更新を停止
obj.縦幅の最適化 .onclick = btn縦幅の最適化
if(!書き込み権限の確認.sw権限有り){ obj.追加.disabled = true }
}
btn追加=function(){
var name=prompt('追加する名前を入力してください','new Name','aaa')
if(!name){return}
if(名前判定(name)==名前判定.ng){return alert('['+name+']はファイルシステムに許可されない文字を含んでいます。')}
var path = path伝言 + '/' + name
if(fs.FileExists(path)){return alert('['+name+']の名前のデータが既にあるため、この名前に変更できません。')}
Write(path, '')
tbl追加(path)
}
tbl追加=function(path){
var tbl=divメイン[FI].cloneNode(true)
tbl.style.display = 'block'
divメイン.insertBefore(tbl)
tbl更新(tbl, path)
}
tbl更新=function(tbl, path){
var inp=gets(tbl,'input')[0], ta=gets(tbl,'textarea')[0]
if(path){
// [tbl追加]からのコールでPathを引数で受け取っている場合
// エレメントごとに関数を作成して、その関数にプロパティ値を持たせる。
var funInp=function(){ name変更(this) }, funTA=function(){ ta変更(this) }
funInp.obj = funTA.obj = {name:fs.GetBaseName(path)}
inp.onchange = funInp
ta .onchange = funTA
inp.onkeydown = ta.onkeydown = onkeydown編集エリア
inp.onblur = ta.onblur = onblur編集エリア
// わざわざsetTimeoutで時間差実行しているのは、
// エレメントが画面上に表示されてからvalueプロパティにnameの値を入れるため。
// nameの値が異常に長い場合、画面表示前にvalueに入っているとエレメントの表示サイズが長く描画されてしまう。
var funName=function(){var m=me(); m.inp.value = m.name}
funName.inp = inp
funName.name = funInp.obj.name
setTimeout(funName, 1)
}else{
// [表示更新]などからコールされた場合。inputの中身からpathを作成する
path = path伝言 + '/' + inp.value
}
ta.value = Read(path)
}
onkeydown編集エリア=function(){
自動更新.停止 = true
}
onblur編集エリア=function(){
if(!自動更新.停止){return}
自動更新(true)
}
btn自動更新を停止=function(){
var btn=this
自動更新.btn停止 = true
btn[iT] = '自動更新を開始'
btn.onclick = btn自動更新を開始
}
btn自動更新を開始=function(){
var btn=this
自動更新.btn停止 = false
btn[iT] = '自動更新を停止'
btn.onclick = btn自動更新を停止
自動更新(true)
}
自動更新=function(sw開始){
var m=me(), カウント=m.カウント, max値=m.max値
// 停止ボタンによる停止中ならどのような状況でも止める。
if(m.btn停止){return}
if(m.停止 && !sw開始){return}
m.停止 = false
if(!カウント){
// 未設定の場合はデフォルト値を入れる
カウント = 0
max値 = m.max値 = 60
}
カウント++
divプログレスバー.style.width = Math.round(カウント/max値*100)+'%'
if(max値<=カウント){
setTimeout(表示更新, 1000)
カウント = 0
}
m.カウント = カウント
setTimeout(m, 1000)
}
表示更新=function(){
var tbls=divメイン[CN]
for(var i=1,L=tbls.length;i<L;i++){
tbl更新(tbls[i])
}
}
名前判定 = function(name){
var ng=me().ng=true
if(name.match(/[\/\\?:*"<>|]/)){return ng}
}
ta変更=function(ta){
Write(path伝言+'/'+ta.onchange.obj.name, ta.value)
自動更新.カウント = 自動更新.max値
}
name変更=function(inp){
var 元=inp.onchange.obj.name, 新=inp.value
if(!新){return name削除(inp, 元)}
if(元==新){return}
if(名前判定(新)==名前判定.ng){return alert('['+新+']はファイルシステムに許可されない文字を含んでいます。')}
var path=path伝言+'/'+新
if(fs.FileExists(path)){return alert('['+新+']の名前のデータが既にあるため、この名前に変更できません。')}
fs.MoveFile(path伝言+'/'+元, path)
inp.onchange.obj.name = 新
}
name削除=function(inp, name){
if(!confirm('['+name+']を削除しますか?')){return inp.value = name}
fs.DeleteFile(path伝言+'/'+name)
// inpの四代上はTBL。それを削除する。
inp[PA][PA][PA][PA].removeNode(true)
// 名前削除のためにキー操作している場合はonkeydownイベントで自動更新が止まっている。
自動更新(true)
}
btn縦幅の最適化=function(){
var tbls=divメイン[CN], ta, str
for(var i=1,L=tbls.length;i<L;i++){
ta = gets(tbls[i], 'textarea')[0]
str = ta.value
ta.style.height = (str.replace(/\r/g,'').split('\n').length + 1) + 'em'
}
}
</script>
</html>
view raw 伝言板.hta hosted with ❤ by GitHub


起動すると以下の画面が表示されます。



「追加」ボタンを押すと入力プロンプトが表示されます。



名前を決めてエンターキーを押すか「OK」ボタンを押すと以下のようになります。



ファイルはこんな感じに出来上がっています。



一番上の青いゲージが右まで伸びきると、各ファイルの内容が画面上に読み込まれます。
テキストの編集中に画面更新処理がかかると編集内容が失われる恐れがあるので、名前やテキストの編集中は青いゲージは停止します。編集エリアからフォーカスが外れると、青いゲージが再び動き出します。


2つ「追加」して、テキストを編集して「縦幅の最適化」ボタンを押すとこんな感じになります。



ちなみに書き込み権限が無い人が開くと、プログラム起動時に以下の画面が表示されます。


何度でも「再実行」できますが、書き込み権限が無い限り永遠に上の画面が表示されます。
「キャンセル」か「続行」を選択すると、次に以下のメッセージが表示されます。


上記メッセージを閉じると、以下の画面が表示されます。
inputボックスやtextareaは無効化状態になっていて、編集や選択ができません。




0 件のコメント:

コメントを投稿