変奏現実

パソコンやMMORPGのことなどを思いつくまま・・・記載されている会社名・製品名・システム名などは、各社の商標、または登録商標です。

この画面は、簡易表示です

javascript

[javascript]createElementとかaddEventListenerはダルい

createElementはダルい。

そもそも、innerHTMLを使えば済む。

createElementはタグ名が付いてるダケの空っぽなHTMLエレメントを作るだけなので

var button = createElement('button');
button.className = 'fileButton fileDrag';
button.id = 'fileButton' + buttonId;
button.type = 'button';
button.innerHTML = caption;
parentObj.appendChild(button);

と・・・ダラダラと書かないといけないから

      var fileButton  = createElement('button', {
        className:  ['fileButton','fileDrag'],
        id:         'fileButton' + buttonId,
        type:       'button',
        innerHTML:  caption,
        parent:     parentObj
      });

の様に連想配列でエレメントの属性を一式で指定できればいいのに・・・

と思ったので書いてみた。

    //  HTMLエレメントを作成します
    const createElement = (tagName, options) => {
      var eventList = ['ended'];
      var elm  = document.createElement(tagName);
      Object.keys(options).forEach( (key) => {
        // eventの場合
        if( eventList.includes(key) ) {
          elm.addEventListener(key,options[key]);
        } else {
          // event以外の場合
          switch(key) {
            case 'className': // プロパティはclassNameですが、タグの属性はclassなので別途に処理します。
              if(Array.isArray(options[key])) {
                elm.classList.add(...options[key]); // 配列を展開しパラメータとして引き渡す
              } else {
                elm.classList.add(options[key]);
              }
              break;
            case 'innerHTML': // setAttributeではタグ内にinnerhtml="***"と属性を生成するので別途に処理します。
              elm.innerHTML = options[key];
              break;
            case 'parent':    // setAttributeではタグ内にparent="***"と属性を生成する様で、画面に生成したエレメントを割り付けないので別途に処理します。
              options[key].appendChild(elm);
              break;
            default:
              elm.setAttribute(key, options[key]);
              break;
          }
        }
      });
      return elm;
    };

イベントに対応するeventListの中身が少ないのは、creapteElementの後で毎回addEventListenerするのも面倒なので、スクリプトの最初でクラス指定でイベント登録を済ませたかったから・・・

とは云え、クラス数×イベント数がいっぱいあったら、その分のdocument.addEventListenerは呼び出したくないので、

    const onLoadWindow = () => {
      //  コントロールのイベント処理を登録します
      const classEvents = [
        /* onload */
        { className:  'fileButton',         events:   { 'click':  onClickFileButton}},
      ...
      ];
      registEventListener(classEvents);
    };
    //  documenイベント処理
    var documentEvents = {};
    const documentEventListener = (event) => {
      // 処理
      var target = event.target || event.srcElement;
      Array.from(target.classList).map((className) => documentEvents[event.type][className])
        .filter((func) => func !== undefined)
        .forEach((func) => func(event));
    };
    //  指定クラスを持つHTMLエレメントにイベントを登録します
    const registEventListener = (classEvents) => {
      // classEvents配列数分ループ
      classEvents.forEach((classEvent) => {
        // イベント数分ループ
        Object.keys(classEvent.events).forEach((eventName) => {
          // クラスのイベントごとの処理
          if( documentEvents[eventName] === undefined ) {
            // documentEvents[eventName][classEvent.className]に代入できるように初期化
            documentEvents[eventName] = {};
            document.addEventListener(eventName, documentEventListener);
          }
          // ※同じクラス・同じイベントに対応する処理は1つだけ
          documentEvents[eventName][classEvent.className] = classEvent.events[eventName];
        });
      });
    };
    //  画面初期表示時の処理
    window.onload = onLoadWindow;

イベントごとに1回document.addEventListenerを呼び出し、イベント発生時に呼出された時にイベントターゲットのクラス名からイベントで処理したい関数をリストアップして呼び出す様にしてみた。

const classEvents =[
  {
    className:  'fileButton',
    events:   {
      'click':  onClickFileButton}
    },
  },
  ...
]

この書き方は思いついたイベントを書き殴るには都合がいいけど、documentEventsでは扱いにくいので

var documentEvents = [
  {
    'click': [
      {
        'fileButton': onClickFileButton,
        ...
      }
    ],
  ...
  }
]

に置き換え、documentEvents[‘click’][‘fileButton’]からonClickFileButtonが容易に取得できるようにしている。

で・・・全部済ませたかったが、

videoタグのendedイベントはdocument.addEventListenerで拾えなかったので

自作のcreateElementで作成したHTMLエレメントにaddEventListenerでendedイベントを登録できるようにした。(ワケ

気分的には楽になるけど、

なぜかソースは長くなる。(大笑い



[javascript]matchとか

HTMLで同じようなタグが多いとのidに1とか2とか数値を付けるけど、

この数値の部分だけ欲しい時がある。

parseInt(id.match(/\d+$/))

とするとidの末尾の方の数字を数値で得られる。

matchは配列を返すので [0] を付けてparseIntに渡した方がいいけど、parseIntは引数を文字列に変換する際に配列なら先頭のみを対象とするみたいでこのままでも支障は無い。

ファイルの拡張子ダケ欲しい時

fileName.match(/([.])(.*$)/)[2]

[1]の様に思えるけど

matchはキャプチャグループを使うと

[0]:全体

[1]:1つ目のキャプチャグループ

[2]:2つ目のキャプチャグループ

の様に返すので ピリオドの後の拡張子部分ダケ欲しい場合は[2]になる。



[Javascript]漢字読み仮名辞書とローマ字変換

VBAではバリバリと日本語の変数を使うけど

JavaScriptでは日本語変数は使えないので、よく打ち間違えるからVisualStudioCodeのヘルプは大助かり

しかし、Excelで仕様書を書く時は何もヘルプ機能が無いのでAll JavaScriptで作ってみた。これならダウンロードすれば安心。(なハズ。

漢字読み仮名辞書とローマ字変換

  • ローマ字変換と漢字読み仮名変換はほぼ似てるのでクラス化
    • 対象テキストと辞書のエントリィ(ひらかなとか漢字とか)の両方の先頭部分を比較し一致箇所が一番長いものを拾い出し、辞書の読み(ローマ字とかひらがな)に差し替える。
    • 基本的に辞書はJSONのまま使用。
  • ローマ字変換と漢字ひら仮名変換の2オブジェクトを作成。
    • 辞書はそれぞれ別途用意。
    • 変数名で使うセパレータ(_)は、漢字読み仮名変換のみ指定。
    • 接頭語、接尾語指定は、漢字読み仮名変換のみ指定。
    • 例外的な記述の仕方はtralsrateAfterをオーバーロード。

使い方1:漢字の読みを登録しよう。

  1. 漢字欄に「新幹線」と入力
  2. ひらかな欄に「しんかんせん」と
  3. 【単語の読みを辞書に登録】ボタンを押す

使い方2:辞書に登録されたか確認しよう。

  1. 【辞書⇒クリップボード】ボタンを押す
  2. テキストエディタで貼り付け
  3. 多分、「新幹線\tしんかんせん\r\n」と出るハズ

使い方3:辞書を更新しよう

  1. テキストエディタを「新幹線\tしんかんせんwwww\r\n」に書き換える
  2. 全文をコピー
  3. 【クリップボード⇒辞書差替】ボタンを押す
  4. 漢字欄が「新幹線」なのを確認する
  5. 【漢字⇒ひらかな】ボタンを押す
  6. ひらがな欄に「しんかんせんwwww」と出るハズ

使い方4:ローマ字に変換しよう。

  1. ひらがな欄が「しんかんせんwwww」なのを確認する
  2. 【ひらがな⇒ローマ字】ボタンを押す
  3. ローマ字(スネークケース)欄に「shinkansenwww」と出るハズ
    • ※ローマ字変換処理は「知らない文字はそのまま出力する様になっている」

使い方5:辞書に登録した漢字を組み合わせて使ってみよう

  1. 漢字欄を「新幹線新幹線」と書き換える
  2. 【漢字⇒ひらかな】ボタンを押す
  3. ひらがな欄が「しんかんせん_しんかんせん」になる。
  4. 【ひらがな⇒ローマ字】ボタンを押す
  5. ローマ字(スネークケース)欄に「shinkansen_shinkansen」と出るハズ
    • ※登録した漢字と登録した漢字の間に_が入る。(仕様
  6. キャメルケース欄に「shinkansenShinkansen」と出るハズ
  7. コンスタントケース欄に「SHINKANSEN_SHINKANSEN」と出るハズ
  8. パスカルケース欄に「ShinkansenShinkansen」と出るハズ
  9. チェインケース欄に「shinkansen-shinkansen」と出るハズ

使い方6:「~1」とか「~2」とか、ありがちな変数名がどう変換されるかな?

  1. 漢字欄を「新幹線1」と書き換える
  2. 【漢字⇒ひらかな】ボタンを押す
  3. ひらがな欄が「しんかんせん1」になる。※辞書に未登録な文字の前後に[ _ ]は入らない。
  4. 【ひらがな⇒ローマ字】ボタンを押す
  5. ローマ字(大文字)欄に「SHINKANSEN1」、ローマ字(小文字)欄に「shinkansen1」と出るハズ
    • ※登録した漢字と登録した漢字の間に_が入る。(仕様

使い方7:辞書の登録

  1. 辞書ファイルを用意
  2. 全文をコピー
  3. 【クリップボード⇒辞書差替】ボタンをクリック

使い方8:複数の辞書を登録

  1. 1つ目の辞書は使い方7の通りに【クリップボード⇒辞書差替】ボタンで取込む
  2. 2つ目以降は【クリップボード⇒辞書追加】ボタンで取込む

使い方9:辞書を消そう。※ここ重要。

  1. F12でDevTools画面を開く
  2. 画面上の「アプリケーション」タグを選択
  3. 画面左の「ストレージ」の中の「LocalStrage」をクリックする
  4. 画面中央の「キー」の一覧に「kanjiYomi」を選択
  5. 右クリックして「削除」を選択

使い方は以上です。

ひらがな欄にカタカナを入力してもローマ字に変換できる。(ハズ

変数名中の「ロック」や「ファイル」等の英単語にするものは変則的な使い方ですが・・・

  • 漢字:「ロック」
  • ひらかな:「lock」

の様にして【単語の読みを辞書に登録】して【ひらかな⇒ローマ字】すると良いでしょう。

ローマ字にしたくない変数名中の「の」は

  • 漢字:「の」
  • ひらかな:「」

とすると出来るけど、「のはら」が「はら」になったり予想外のことが起きるでしょう。

新XXXXは「shinXXXX」にしたい場合は

  • 漢字:「新」
  • ひらかな:「しん@」

と読みに@を付けると読み仮名変換時に[ _ ]が付かない様にしたから

ローマ字にするとshinxxxxになるはず。

xxx年はxxxNenにしたい場合

  • 漢字:「年」
  • ひらかな:「@ねん」

と読みの先頭に@を付けると読み仮名変換時に[ _ ]が付かない様にしたから

◇◆◇◆◇◆◇◆

【単語の読みを辞書に登録】や【クリップボード⇒辞書差替】を登録するとLocalStrageに取り合えず保存するので

適当に【辞書⇒クリップボード】して辞書をバックアップすれば安全だろう。

LocalStrageはJSON式データで保存してるけど、

クリップボードは「漢字1\t読み仮名1\r\n」「漢字2\t読み仮名2\r\n」「漢字3\t読み仮名3\r\n」・・・形式なので、

多分、EXCEL⇔クリッップボード⇔画面で利用もできる。(知らんけど

なんでこんなのを作ったのかと云えば

Excelに日本語名とローマ字の対応表を作りXLOOKUPで引けばできることですが、

「xx処理yy関連zz対応www2」の様な日本語名がいっぱいあると登録するのに疲れてくるので

「xx処理」「yy部」「zz対応」「www」を一度登録すれば、

「aa処理yy関連zz関連www2」なら「aa処理」だけ登録すればいいものが欲しかった。(ダケ

それにしても・・・

3人居れば皆ローマ字はバラバラになるのは・・・(実に恐ろしい

試しに

IME用辞書のTEXT形式のものを見つけたので

置換前:^([^\t]+)\t([^\t]+)\t([^\t]+)$

置換後:\2\t\1

で正規表現ONで置換させて、うまく取り込める様だ。

しかし、LocalStrageの制約があるので、5MBを越える様な漢字の読みは登録できない。

ps.TEXTAREAタグに変えた

変数名の列を範囲指定して貼ってみると楽しいかもしれない。

  1. とりあえず、単漢読み仮名だけでも自動的にしたいなら
  2. 文化庁の「常用漢字一覧表」のPDFをダウンロード
  3. MS-WORDに強引に読ませる
  4. 全文をクリップボードに入れる
  5. テキストエディタに貼り付ける
  6. 雰囲気を見ながら
  7. 「^[\r\n]+」で、空行を削除 ※114行減る
  8. 「^\s(\S)\(\s+\)\s+」「\1\t」で、103行調整。
  9. 「^\s.*\r\n」で、タブや空白等で始まる行を削除 ※2249行減る
  10. 「^(\S+)\s+(\S+)\s+(.*)$」「\1\t\2」で、漢字と読みだけ残す ※1958行更新
  11. 「\(\S\)」で、(惡)などの旧字を削除 ※251行更新
  12. 「^漢字\t音訓\r\n」で、表タイトルを削除 ※115行削除
  13. 「^←→[^\r]*\r\n」で、削除 ※8行削除
  14. 微調整が必要なのが以下の14行。
    • 梅雨(つゆ),部屋(へや),今年(ことし), 昨日(きのう), お母(かあ)さん
    • 勺 シャク尺 シャク, 升 ショウ少 ショウ, 昭 ショウ宵 ショウ, 畝 せ瀬 せ, 浦 ホ補 ホ, 匁 もんめ夜 ヤ
    • 「人数」は, 陳 チン 陳列
    • 弁(/辨/瓣/辯/) ベン
  15. 全文コピって【クリップボード⇒辞書】ボタンを押す

と、してみたら良いかもしれない。

常用漢字⇒ジョウヨウカン_ジ⇒jou_you_kan_ji な具合

多分うまくできたと思うので、単漢字変換表サンプルのリンクをサンプルの画面に貼っておく。



[JavaScript] new ActiveXObject (progId)っぽく

IEのJavaScriptで

var cn = new ActiveXObject("ADODB.Connect");

みたいなことが出来たので、

Chromeでもできないのか考えてみた。

ActiveXObjectクラスのnew演算子でパラメータをplogIdとするOCXっぽいオブジェクトが戻って

くれれば嬉しいので、ストレートに

returnを使ってみた。

class ActiveXObject {
    constructor(className, json)
    {
        this.domain       = "xxx.xxx.xxx"; // この辺はC#側の設定を転記
        this.port         = "xxxx";         // この辺はC#側の設定を転記
        this.className    = className;
        this.objectID     = null;
        // 派生クラスの場合は再帰してしまうので、処理を省略
        if(typeof(className) !== 'undefined')
        {
            // classNameから当該クラスのJavaScript側クラスのオブジェクトを作成
            let cf = new Function('className', `return new ${className.replace('.', '_')}()`);
            let obj = undefined;
            try {
                obj = cf(className);
            } catch (ex) {
                throw new Error(`★new ActiveXObject("${className}")未定義クラス`);
            }
            // 初期設定値(json)があれば、設定する
            if (typeof (json) !== 'undefined') {
                obj['objectID'] = json['objectID'];
                obj.__setup__(json);
            }
            // 無ければ, C#のサーバに問い合わせる。
            else
            {
                // サーバー側にnewしたことを通知
                obj.init();
                // 放置してもサーバ側のオブジェクトは解放されないので、オブジェクトが不要になったら、適宜termメソッドを呼び出す事
                // this.term();
            }
            return obj; // new の戻り値
        }
    }
    // 設定する
    __setup__(json) {
        // プロパティにコピー
        for (let key of this.propertyNames) {
            let jj = json[key]
            if (typeof(jj) !== "undefined") {
                this[`_${key}`] = json[key];
            }
        }
        // プロパティ(コレクション)にコピー
        for (let key in this.propertyCollections) {
            let jj = json[key]
            if (typeof(jj) !== "undefined") {
                if (typeof (this[`_${key}`]) === "undefined" || this[`_${key}`] === null) {
                    this[`_${key}`] = {};
                }
                for (let idx in jj) {
                    let k = jj[idx].Name;
                    this[`_${key}`][`${k}`] = jj[idx];
                }
                // this[key]['Item'] = this[key]
            }
        }
    }
    // サーバー側にnewしたことを通知
    async init() {
        let json = await ActiveXObject.staticSendMessage('POST', this.domain, this.port, `ActiveXObject/${this.className}`);
        ActiveXObject.staticInit(this, json);
    }
    // オブジェクトの解放
    async term() {
        return await ActiveXObject.staticSendMessage('DELETE', this.domain, this.port, `ActiveXObject/${this.objectID}`);
    }
    // サーバー側にnewしたことを通知
    static staticInit(obj, json)
    {
        // 必須
        if (typeof (json["objectID"]) === 'Nothing')
        {
            alert("objectIDが取得できません。");
        }
        obj.objectID = json["objectID"];
        if (typeof (obj.onobjectID) !== 'undefined')
        {
            obj.onobjectID();
        }
        obj.SetProperties(json['activeXobject']);
        //
    }
    // 送信
    static staticSendMessage(method, domain, port, page, data)
    {
        let promise = new Promise( (resolve, reject) => {
            try {
                let parserFunction = this.parserFunction;
                let xhr = new XMLHttpRequest();
          // httpsにした方がいいのかもしれない
                let url = encodeURI(`http://${domain}:${port}/${page}`);
                xhr.open(method, url, true);
                xhr.responseType = 'text';
                xhr.send(this.staticEncodeHTMLForm(data));
                xhr.onload = function(e) {
                    if (xhr.readyState == 4) {
                        if (xhr.status == 200 ) {
                            try {
                                let json = xhr.response;
                                json = JSON.parse(json, parserFunction);
                                resolve(json);
                                return;
                            } catch (ex) {
                                console.log(`'${ex.toString()}'\n url:http://${domain}:${port}/${url}\n json:'${xhr.response}'`);
                            }
                        }
                    }
                };
            }
            catch (ex) {
                reject(ex);
                return;
            }
        });
        return promise;
    }
    // データをBODYの形式に変換
    static staticEncodeHTMLForm(data) {
        if (typeof(data) === 'undefined' || data === null)
        {
            return null;
        }
        let a = [];
        for (let aa of data) {
            a.push(aa);
        }
        return "params=" + JSON.stringify(a);
    }
    //
    static staticMakeUrlParameters(arg) {
        let rc = [];
        for (let idx = 1; idx <= arg.length; idx++) {
            rc.add(`p${idx}=JSON.stringify(${arg[idx - 1]})`);
        }
        return rc.join('&');
    }
    //
    static staticParserFunction(k,v)
    {
        try
        {
            if (typeof(v) === "string" && ((v.indexOf("function") === 0) || (v.indexOf("async") === 0)))
            {
                return eval(`(${v})`);
            }
            else
            {
                return v;
            }
        } catch (ex) {
            console.log(ex.toString());
        }
    }
}

newで使用する各ActiveXObjectのテンプレ・クラスは・・・

//  ADODB_Connection クラス
class ADODB_Connection extends ActiveXObject {
    constructor() {
        // とりあえず定番
        super();
        this.className = this.constructor.name.replace('_', '.');
        // プロパティ
        // 名前リストからプロパティを作成
        this.propertyNames = ['Attributes', 'CommandTimeout', 'ConnectionString', 'ConnectionTimeout', 'CursorLocation', 'DefaultDatabase', 'IsolationLevel', 'Mode', 'Provider', 'State', 'Version'];
        this.makeProperties(this.propertyNames);
        // プロパティ(コレクション)
        // 名前リストからダミーのメソッド(コレクション)を作成
        this.propertyCollections = {
            // https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/errors-collection-properties-methods-and-events
            'Errors': {
                'properties': ['Count', 'Item'],
                'methods': ['Clear', 'Refresh'],
                'events': []
            },
            // https://docs.microsoft.com/ja-jp/office/client-developer/access/desktop-database-reference/properties-collection-properties-methods-and-events
            'Properties': {
                'properties': ['Count', 'Item'],
                'methods': ['Refresh'],
                'events': []
            }
        };
        this.makePropertiesCollection(this.propertyCollections);
        // メソッド
        // 名前リストからダミーのメソッドを作成
        this.methodNames = ['BeginTrans', 'CommitTrans', 'RollbackTrans', 'Cancel', 'Close', 'Execute', 'Open', 'OpenSchema'];
        this.makeMethods(this.methodNames);
        // イベント
        // 名前リストからダミーのメソッドを作成
        this.eventNames = ['BeginTransComplete', 'CommitTransComplete', 'RollbackTransComplete', 'ConnectComplete', 'Disconnect', 'ExecuteComplete', 'InfoMessage', 'WillConnect', 'WillExecute'];
        this.makeEvents(this.eventNames);
    }
}

“ADODB.Connection”というクラス名に使えなかいので、”ADODB_Connection”にしている。

makeXXXXは使いまわすのでActiveXObjectクラスで実装

    //
    /**
     * 名前リストからプロパティを作成
     * 
     * @param {String[]} propertyNames   名前リスト
     */
    makeProperties(propertyNames) {
        for (let propName of propertyNames) {
            Object.defineProperty(this, propName, {
                get() {
                    return this[`_${propName}`];
                },
                set(value) {
                    this[`_${propName}`] = value;
                }
            });
        }
    }
    /**
     * 名前リストからメソッドを作成
     * 
     * @param {String[]} methodNames   名前リスト
     */
    makeMethods(methodNames) {
        for (let methodName of methodNames) {
            this[methodName] = function () {
                let args = arguments;
                if (this.objectID == null) {
                    return new Promise(async (resolve, reject) => {
                        this.onobjectID = async function () {
                            let rc = await this[methodName].apply(this, args);
                            resolve(rc);
                            return;
                        }
                    });
                } else {
                    let path = `ActiveXObject/${this.objectID}/${methodName}`;
                    return new Promise(async (resolve, reject) => {
                        let json = await ActiveXObject.staticSendMessage('PUT', this.domain, this.port, path, args);
                        // エラーがあれば返す。
                        if (typeof (json['error']) !== 'undefined') {
                            reject(json['error']);
                            return;
                        }
                        //
                        this.SetProperties(json['activeXObject']);
                        //
                        let rc = json['returnValue'];
                        if (rc != null && rc["__constructor_name__"]) {
                            let className = rc["__constructor_name__"];
                            delete rc["__constructor_name__"];
                            let obj = new ActiveXObject(className, rc);
                            resolve(obj);
                            return;
                        }
                        resolve(rc);
                        return;
                    });
                }
            };
        }
    }
    /**
     * 名前リストからプロパティ(コレクション)を作成
     * 
     * @param {String[]} propertyCollectionNames   名前リスト
     */
    makePropertiesCollection(propertyCollections) {
        for (let collectionName in propertyCollections) {
            if (typeof (this[collectionName]) === "undefined") {
                this[collectionName] = {};
            }
            let co = this[collectionName];
            // コレクション本体
            Object.defineProperty(this, collectionName, {
                get() {
                    return this[`_${collectionName}`];
                },
                set(value) {
                    this[`_${collectionName}`] = value;
                }
            });
            // コレクションの設定内容
            let collection = propertyCollections[collectionName];
            // コレクションのプロパティ
            // property
            for (let propertyName of collection.properties) {
                Object.defineProperty(co, propertyName, {
                    get() {
                        return co[`_${propertyName}`];
                    },
                    set(value) {
                        co[`_${propertyName}`] = value;
                    }
                });
            }
            // コレクションのメソッド
            for (let methodName of collection.methods) {
                if (typeof (co[methodName]) === "undefined")
                {
                    co[methodName] = function () {
                        let args = arguments;
                        if (this.objectID == null) {
                            this.onobjectID = async function () {
                                let rc = await co[methodName].apply(co, args);
                                resolve(rc);
                                return;
                            }
                        } else {
                            let path = `ActiveXObject/${this.objectID}/${collectionName}/${methodName}`;
                            return new Promise(async (resolve, reject) => {
                                let json = await ActiveXObject.staticSendMessage('PUT', this.domain, this.port, path, args);
                                // エラーがあれば返す。
                                if (typeof (json['error']) !== 'undefined') {
                                    reject(json['error']);
                                    return;
                                }
                                //
                                this.SetProperties(json['activeXObject']);
                                //
                                let rc = json['returnValue'];
                                if (rc != null && rc["__constructor_name__"]) {
                                    let className = rc["__constructor_name__"];
                                    delete rc["__constructor_name__"];
                                    let obj = new ActiveXObject(className, rc);
                                    resolve(obj);
                                    return;
                                }
                                resolve(rc);
                                return;
                            });
                        }
                    };
                }
            }
            // event 未定
            //
        }
    }

コレクションはプロパティとメソッドのソースをマージした感じで雑になっている。

これで準備が整ったところで、

new ActiveXObject("ADODB.Connection");

すれば、いいはず。

これって、new 演算子をオーバーライドした事になるのかな?(知らんけど

真っ先にプロパティ・チェックするケースは見当たらなかったので、

最初にメソッド呼び出し時に、C#側からobjectIDが送られるまで待って処理する様にしている。

プロパティ参照時もobjectIDが送られるまで待って処理を入れた方がいいけど、

var table_name = schema.Fields["TABLE_NAME"].Value;

の様に呼び出されると、await が入れにくい。

うまくawaitが入っても、絶望的に読みにくくなりそう。

var table_name = await (await (await schema.Fields)["TABLE_NAME"]).Value;



【javascript】indexedDB

まだ、イマイチ感がある。

注意点

1.PC上のHTMLの場合、データベースのスコープ(有効範囲)はPC単位。

つまりPC上のHTMLで共有してしまう

2.データベースをオープンしたら、必ず自分でクローズすること

クローズし忘れると、次のオープン時にロックしやすい

大抵はF5のオートコミットで済むが、ブラウザを閉じないとダメな場合もある

successイベントは1回のみなので、try catchの後のfinallyでクロースすればいい

const request = window.indexedDB.open("TestDatabase");
request.addEventListener('success', (event) => {
  const database = event.target.result;
  try {
    ・・・データ処理・・・
  } catch (ex) {
    console.log(`${ex}`);
  } finally {
    database.close();
  }
});

しかし、Promiseを使って処理の同期を取りたい場合は・・・

const promise = new Promise( (resolve, reject) => {
  const request = window.indexedDB.open("TestDatabase");
  request.addEventListener('success', (event) => {
    const database = event.target.result;
    try {
      ・・・データ処理・・・
   resolve(`xxxx(...): success`);
    } catch (ex) {
      console.log(`${ex}`);
   reject(`xxxx(...): catch(${ex})`);
    } finally {
      database.close();
    }
  });
  *** エラー処理とか ***
});
return promise;

とすると、resolveの後にfinally句のクローズ処理が通るかどうか?

あまり自信が無い。

と云うのも

function test() {
  return  true;
  alert('OK');
}

のalert(‘OK’);がいつのまにか処理されなくなってたからだ。

upgradeneeded発生時は、後にsuccessイベントが続くので、successイベントに任せればいい

errorイベントも忘れずにクローズ。

request.addEventListener('error', (event) => {
  const error = event.target.error;
  database.close();
  reject(`xxxx(...): error\n${error}`);
});

3.オブジェクトストアの生成・削除はデータベースのバージョンアップ時のみ

4.インデックスの生成・削除もデータベースのバージョンアップ時のみ

5.だが、トランザクションはデータベースのバージョンアップ時は不可

6.トランザクションも自分でcommitabortすること

commitabortを忘れるとタイムアウトするまで次の処理がロックする

const trans = database.transaction(オブジェクトストア・リスト);
try {
  const objectStore = trans.objectStore(オブジェクトストア);
  ***データ処理***
  trans.commit();
} catch (ex) {
  trans.abort();
  throw ex;
}

だからと云ってcursorのsuccessイベントは何回も降ってくることが多いので

毎回commitすると、2周目でつまずく

const trans = database.transaction(オブジェクトストア・リスト);
try {
  const objectStore = trans.objectStore(オブジェクトストア);
  const cursor objectStore.createCursor().onsuccess(event => {
    const cursor = event.target.result;
    ***データ処理***
    cursor.continue();
    trans.commit(); //毎回コミットは・・・
  });
} catch (ex) {
  trans.abort();
  throw ex;
}

7.インデックスの生成・削除できるタイミングはオブジェクトストアの生成直後だけ

const request = indeedDB.openDatabase(データベース名)
request.addEventListener(`onupgradeneed', (event) => {
  const database = event.target.result;
  const objectStore = database.createObjectStore(オブジェクトストア名);
  objectStore.createIndex(インデックス名);
  objectStore.deleteIndex(インデックス名);
});

後でインデックスを調整したい場合は、オブジェクトストアのexports機能を自作して、イジって、importsするしかない。

8.カーソルの生成はデータベースのバージョンアップ時は不可だと思う

9.カーソルのsuccessイベントはカーソルの移動回数分繰り返すので、Promise使った方が良さそう

最終のsuccessイベントは、event.target.result === undefined なので、うっかりcursor.valueすると痛い。resolveだけ処理すればいい。

const trans = database.transaction(オブジェクトストア名);
const promise = new Promise( (resolve, reject) => {
  try {
    const objectStore = trans.objectStore(オブジェクトストア名);
    const cursor = objectStore.openCursor();
    cursor.onsuccess = (event) => {
      var cursor = event.target.result;
      if (cursor) {
        console.log(`${cursor.key} is ${cursor.value}.`);
        cursor.continue();
      } else {
        console.log("end");
        resolve(true);
      }
    };
    cursor.onerror = (event) => {
      const error = event.target.error;
      reject(error);
    };
  } catch (ex) {
    reject(ex);
  }
});
return promise;

な感じだった。

データベースの構成をイジる処理をコードしてみると、

何かと面倒くさかったので、

全データベースまたはオブジェクトストア単位でのexportsimports機能を作って、

データを編集するコード(createDatabasedeleteDatabaseとか)を

使いまわしをした方が良さそう。

どうせやることは、

class IndexedDbOp {
  constractor(json) {
    this.json;
  };
  const createDatabase = (databaseName) => {
    this.json.databases[this.json.databases.length] = {'database':{
    'name': databaseName,
    'version': 1
    }}
    return this;
  }
  const deleteDatabase = (databaseName) => {
    this.json.databases = this.json.databases.filter( database => database.name !== databaseName );
    return this;
  }
  ・・・以下同文・・・
}

なのにブラウザにはindexedDBのexportsimports機能が付いていない不思議。

※上記のソースコードは雰囲気です。



[JavaScript]Promise

Promiseオブジェクトの作り方には大きく2つある。

  1. new Promise( )
  2. Promise.resolve( )

new Promise( ) は、新たにオブジェクトを作るけど、Promise.resolve( ) の方はstaticなPromiseオブジェクトを使いまわししているだろう。

class Promise {
    constractor () {
    ・・・
    }
    static staticResolve = new Promise().resolve(undefined);
    resolve() {
      if (typeof(this) === "undefined") {
        return Promise.staticResolve;
      ・・・
    }
}

と考えると、Promise.resolve( ) はpromise状態が履行 (fulfilled)に確定しているところから始まるから

Promise.resolve().then( (a)=>{
    /* なんたら */ 
}).then( (b)=>{
   /* かんたら */
}).then( (c)=>{
   /* それから */
}).then( (d)=>{
   /* こうして */
}).then( (e)=>{
   /* こうなった */
}).catch((e)=>{
   /* どうして、そうなった? */
});

と書いても、「なんたら」部の最後まで進めば、直ぐに「かんたら」に突入し、ストレートに「こうなった」まで進んで一気に進むので、完全な同期処理である。

単に、try…catch…finally なんて美しくないという人向けでしかない。

ただ、ドコが壊れているのかイミフなコードを切り分けた状況のエビデンスを作る時には非常に便利である。

実際には<% try { %><% throw(“ココか?”); %> <%} catch(e) {consol.log(e); } %>式に

プローブ針を垂らすけど、エビデンスとしては判りにくいからね。

強いて言えば、「なんたら」「かんたら」「それから」「こうして」「こうなった」の全てにawaitで強制的にワーカースレッドを停止させる箇所が無いと意味が無いのだ。

そんな訳で、非同期で処理したいものを1つづつ、「非同期化」するしかないが、そう難しくはない。

class hoge {
    // 非同期型 同期型と同じ数だけ作成する
    asyncFunc(params) {
    return new Promise((resolve, reject) => {
        try {
            let result = syncFunc(params);
            resolve(result);
        } catch(e) {
            reject(e);
        }
    });
    }
    // 同期型
    syncFunc(params) {
        return foo;
    }
    ・・・ 
}

と非同期呼び出し口を用意するだけ、

しかし、async接頭子のメソッドがいっぱい増えるのが嫌なら

class hoge {
    // 非同期型 1つだけ作って使いまわし。※毎回 new Promise しなくていいのが嬉しい。
    asyncCall(syncFunc,params) {
    return new Promise((resolve, reject) => {
        try {
            let result = syncFunc(params);
            resolve(result);
        } catch(e) {
            reject(e);
        }
    });
    }
    // 同期型
    syncFunc1(params) {
        return foo;
    }
    ・・・ 
    // 同期型
    syncFuncN(params) {
        return foo;
    }
}

と、非同期化メソッドを用意しておくといいだろう。

引き渡すパラメータはthenチェインする前に確定してしまうから、後々トラブりそうなのが難点で、

しかも、下のコードを見て判るように、各処理に引き渡すパラメータは事前に用意することになるけど

paramを変数宣言しておいて使用すれば、各処理が動き出す前に調整は可能だし、何よりデバッグが楽だ。

ちゃんとデバッグが済んでから、試作型に実処理を埋め込めばいいだろう。

とりあえず、

// 非同期処理を準備する
let param1;
let param2;
let param3;
let param4;
let param5;
let promFunc1 = asyncFunc1(param1);
let promFunc2 = asyncFunc2(param2);
let promFunc3 = asyncFunc3(param3);
let promFunc4 = asyncFunc4(param4);
let promFunc5 = asyncFunc5(param5);
//
promFunc1.then(()=>{              /* なんたら param2を最新化 */ 
    promFunc2.then(()=>{          /* かんたら param3を最新化 */
      promFunc3.then(()=>{        /* それから param4を最新化 */
        promFunc4().then(()=>{    /* こうして param5を最新化 */
          promFunc5().then(()=>{  /* こうなった */
});});});});}).catch((e)=>{      /* どうして、そうなった? */
});

とコードすることで完全に非同期になると思う。

でも、thenチェインって云われているものは普通コレではない。

//同期っぽい書いてある非同期処理 ※パラメータは暗黙の了解で繋がっている
promFunc1(a)       /* なんたら 暗黙のparam2を最新化 */
.then(promFunc2)     /* かんたら 暗黙のparam3を最新化 */
.then(promFunc3)     /* それから 暗黙のparam4を最新化 */
.then(promFunc4)     /* こうして 暗黙のparam5を最新化 */
.then(promFunc5)     /* こうなった */
.catch((e)=>{
   /* どうして、そうなった? */
});

あるいは

//同期っぽく書いてある非同期処理
promFunc1(a).then( (b)=>{        /* なんたら */    
    let b = await promFunc2(b);  /* かんたら */
    return b;
}).then( (b)=>{   
    let c = await promFunc3(b);  /* それから */
    return c;
}).then( (c)=>{
    let d = await promFunc4(c);  /* こうして */
    return d;
}).then( (d)=>{
    let e = await promFunc5(d);   /* こうなった */
    return e;
}).catch((e)=>{
   /* どうして、そうなった? */
});

のハズだ。この書き方の最大の問題点はawaitが本当に動作してくれるかどうかだ。

awaitはドコかのPromiseオブジェクトが「履行 (fulfilled): 処理が成功して完了」になるのを待つせいで、

ワーカースレッド内で同時に使用できるのはたったの1回だけ。

ネストできないから、上のコードを参照する処理や各async内でawaitしてるとawaitした途端にエラってしまう。

ので、

awaitを使ってエレガントなコードに酔いしれることができるのは「お一人様」つまり「貴族様仕様」なのだ。

つまり、awaitのaは、「とあるドコかの何か」ではなく「お一人様」の意味なのだ。

※試験には出ないけど重要です。

流石、文化が違うよね。ボクらはネストしてるthenチェーンか綺麗になおしたらデバッグ困難なコードに立ち向かう事になる。

だから、「ボタンをクリック」した直後に一回くらいに限定した方がいい。

サーバーと通信する際に使うと、画面隅に通知機能を搭載した途端にケンカ沙汰になってしまう。

というのが、ググった結果から得られた感想。

本当にこうなっているのかコードしてみると予想が結構ハズれているような気がする。

何せ、数年経てば、仕様書上は全く変わらないのに、全く違う動作になっている世界だけに。

実際、new Promiseのコールバックは今では即実行されてしまうので、

Promiseのthenの設定を全て終えメインスレッドが完了してから動いてくれると思ったら大間違い。

Promise.thenのコードを見つけると即実行するので、全く非同期ではない。

正確には、new Promiseのコールバックの中だけ、resolveかrejectを呼ぶまで非同期になってるダケで

Promise.resolveは、非同期に見える振りをしているだけ。

非同期にしたいなら

//同期っぽい書いてある非同期処理 ※パラメータは暗黙の了解で繋がっている
promFunc1(a)       /* なんたら 暗黙のparam2を最新化 */
.then(promFunc2)     /* かんたら 暗黙のparam3を最新化 */
.then(promFunc3)     /* それから 暗黙のparam4を最新化 */
.then(promFunc4)     /* こうして 暗黙のparam5を最新化 */
.then(promFunc5)     /* こうなった */
.catch((e)=>{
   /* どうして、そうなった? */
});

の書き方以外は意味が無い。

正解を引き当てたると後々面倒なことになるのはいつもの事。(大笑

「非同期な処理」≒「後でやって欲しい処理」を書くには、

各promFuncNの中でsetTimeoutしてresolveする以外に方法は無い。

ps.2021/12/13

サンプル追加。

thenチェインする処理を関数化すると、returnした値が引き継がれないのは痛いね。

thenチェインでreturnした値は、thisにぶら下げて引き渡しているんだろう。

1つの関数の中で閉じ籠って then チェイン して

うまく動いてるけど、ゴチャゴチャしているので

リファクタリングして美しいthenチェインにすると・・・

壊れます。(痛すぎ



[JavaScript]finallyが壊れている様な気がする

イミフなスタックオーバーフローに悩まされた。

static dataDumpNest = [];
static DATADUMPNEXT_MAX = 100;
static dataDump(objectName, obj, pOptions) {
  try {
    ・・・
    Helper.dataDumpNest.push(obj);
    rc = true;
    ・・・
    return true;
    ・・・
    //  再帰呼び出し時はtrueを返す
    if (Helper.dataDumpNest.length > 1) {
      return true;
    }
    //  それ以外は result を結合し返す
    return Helper.result.join("\n");
  } catch (e) {
    throw e;
  } finally {
    Helper.dataDumpNest.pop();
  }
}

finallyで無条件でpopしているので、

重複チェックで弾かれてpushしてない時に

無駄にjoinしてしまうのが気になり

執拗にF10でトレースしていると・・・

finallyブロックの終わりで呼び出し元のメソッドに戻るべきところが、

  1. ソース画面が何行か戻ってしまう。
  2. スタックトレースが妙に長くなっていた。

という摩訶不可解な現象にぶち当たった。

どうやら、return文無しで、finallyブロックの末尾にぶち当たると、ヤバイ事が起きている。

1万個の配列を何度もjoinさせ、しかも結果は捨てているのでブチ切れさせてしまったかもね。(大笑

普通、ドコかのreturn文の後に処理するハズのfinally句なのに。

延々と待たされた挙句にスタックオーバーフローになるソースを

finally句でtryの外の変数rcをreturnする様にしてみた。

static dataDumpNest = [];
static DATADUMPNEXT_MAX = 100;
static dataDump(objectName, obj, pOptions) {
  let rc = false;
  try {
    ・・・
    Helper.dataDumpNest.push(obj);
    rc = true;
    ・・・
    rc = true;
    return rc;
    ・・・
    //  再帰呼び出し時はtrueを返す
    if (Helper.dataDumpNest.length > 1) {
      rc = true;
      return rc;
    }
    //  それ以外は result を結合し返す
    rc = Helper.result.join("\n");
    return rc;
  } catch (e) {
    throw e;
  } finally {
  if (rc != false) {
    Helper.dataDumpNest.pop();
  }
    return rc;
  }
}

と変えたら、即終わったった

setTimeoutでワーカースレッドっぽい状態なんで、

finallyが、Promiseと勘違いしてスタックを遡ってみると配列がいっぱい積んであるので

Array[ Array[1], Array[2] ] あたりが、

new Promise((resolve, reject) {
・・・
}

に、そっくりだったのかもしれない。

しかし、違うと気が付いて、throw new Error(“Don’t mind.”);

して、普通のfinally になるつもりが、ソコのfinallyでまた躓いて、無限ループしてそう。

でも、そのうち治るでしょ?

やっと、本題の文法解析に戻れそう。

ん?三国英雄たちの夜明けがフリーズしやすいのは

もしやコレなのか?



[javascript]パーサ・コンビネータって

Java パーサコンビネータ 超入門というのを見つけた。

使い方を説明してくれるのでとても助かるけど、

JavaScriptでパーサコンビネータのコンセプトを理解する(「正規表現だけに頼ってはいけない」の続き)

の方がパーサ・コンビネータの仕組みを理解でき、且つ、使わなくなったけどJavascriptの便利な手法を思い出させてくれた。

function 関数(...) {
  return [1,2,3,4, ...];
}

と配列等で容易に複数の値を変えすことができるし、そんな関数を

let [a, b, c, d, ...] = 関数(...)

と呼び出すと、すんなり配列中の a, b, c, d, … に値を入れてくれるトコが地味に嬉しい。

いつのまにかいっぱい返す事になる未来が待っていそうな気がするから new classを返すことが多いけど。

また

function token(str) {
  var len = str.length;
  return function(target, position) {
    if (target.substr(position, len) === str) {
      return [true, str, position + len];
    } else {
      return [false, null, position];
    }
  };
}

の様にreturn function {…} することで、関数ジェネレータを簡易に記述できるのは便利、

但し、呼び出す側が、

token('foobar')('foobar', 0); // => [true, 'foobar', 6]を返す

な感じになってしまうとワークスペース内のファイルの関数を全部チェックする開発環境ではなく、

sakuraエディタで チョコ チョコ叩いている場合は、【見た目文法エラー!】なのが難点。

やはり、

const func = token('foobar');
func('foobar', 0); // => [true, 'foobar', 6]を返す

と書いて関数が戻ってくる雰囲気を醸し出した方が無難な気がする。

many(token('hoge'))('hogehoge', 0); // => [true, ['hoge', 'hoge'], 8] を返す

も、token(‘hoge’)部分がmanyな場合は、あーもーな感じになりそうなんで

let parsers = [];
parsers.push(token('hoge1'));
...
parsers.push(token('hogeN'));
let text = 'hoge1hoge2...hogeN';
let myParser =seq(parsers);
myParser(text, 0); // => [true, ['hoge1', ['hoge2', ...['hogeN']...]]], 8] を返す

と1行がいっぱい膨れ上がってしまいそう。

てか、実際に’hoge1hoge2’と直書きすることは滅多に無いから

もっと、もっと膨れ上がる。

先の記事のソースは「あくまで説明しやすく表記」したものなのは明白だけど、

テストコードってこんな風に書いてたりするよね?。(笑

ps.

先の「理解する」のページのコードをまとめてみた。

簡易のテストケースもパーサーの種類ごとに1つダケ書いてみたら、結構長くなってしまった。

作りかけだからFalseしまくってるのはいいとして

Errorオブジェクトから発生原因を判りやすくしてほしいなぁ。

字句解析とか構文解析は、「,」1つの修正だけでも

見えないところがトンデモない結果になり果ててしまいがちなので、テストはガチでやった方がいいからね。

ソースの説明

  • index.html   テスト画面
  • parcom.js パーサの記事からコードをコピペしてチョコっと加工したもの
  • sample.js パーサのテストパターンを起動し、結果を表示するスクリプト
  • testparcom.js  パーサのテストパターン
  • testbase.js    テストパターンを多少楽にするための testparcom.js の基底クラス
  • libs.js オブジェクトのダンプやErrorオブジェクトから情報を引き出したりするD級パーツ群
  • sample.css テスト画面用CSS

※ん-。まだまだな感じがする。

そもそも、各パーサはサブクラス化した方が良いんじゃにのかな?

更にパーサ コンテナ クラスで括った方が・・・。

アカン。どんどん膨れ上がる一方だ。

ps.2021/12/3

BNFっぽい文法”Call = [ Call ] procedureName [ (argumentList) ] ”と書かれたテキストを

解析するコードを作成中。やっと単純な文法をクリアしたものの、2つ目の文法に進まない。

デバッグでハマりすぎ長いログをスクロールするのが大変。

  • 機能を大幅改善
    • ログ末尾のstartとendを検出しリストタグ(UL,OL,LIとか)で手軽にインデント
      • 以前見つけたTreeViewのコードは流用できた
      • FIXTED:cssは全部見直しするハメになった
      • TODO:try…catch…filannyで確実にendを出力させないと右雪崩が発生する
    • TreeView風に[+][ーで折りたためる
      • TODO: +、ー小さすぎた!
      • TODO: endの行に+を配置するのは良くない。
    • trueやfalseを色分け
      • FIXED: あるあるな後付けデグレードと既存バグ発覚
        • objDumpで処理済みのテキストを再度objDumpしてが気が付かなかった
        • replaceで単語にタグ付けする場合は二度漬け禁止のハズが・・・
          • <span> ⇒ &lt;span> 的な出来事
    • FOXED:ログが雪だまり式に増えていた
      • Case2にCase1のログが
      • Case3にCase2とCase1のログが
      • 以下、増殖
        • 各テストCaseでログクリアする
    • TODO:typeof xxx === “undefined”はしてるけど、xxx === null チェックが甘い

コードの見直しが多すぎて、メソッド単体テストが壊滅状態。

修正してみたら、半壊状態。←いまこのへん。

全文法(287行中7行)をクリアするのはまだまだ先の話。

本当に終わりが見えないなぁ(大笑

            //      syntax = [ rule ]  ;
            setSyntax("syntax", many(refSyntax("defsymbol")));
            // defsymbol: { "\n" } IDENTIFIER "=" rule
            //###setSyntax("defsymbol", seq(optEol,  identifire,  asign,  refSyntax("rule")));
            setSyntax("defsymbol", seq(optEolWs, identifire, asign, refSyntax("rule")));
            // rule: list { "|" list }
            setSyntax("rule", seq(refSyntax("list"), repeat(seq(vl, refSyntax("list")))));
            // list: { seq | block | repeat | option }  "\n" { notes }
            // TODO: repeat(...)の中にoption系が挟まると無限ループするので、何か対策案を考える
            setSyntax("list", seq(repeat(choice(refSyntax("seqList"), refSyntax("block"), refSyntax("repeat"), refSyntax("option"))), eol, opt(refSyntax("notes"))));
            // seqList = seq { seqList }
            setSyntax("seqList", seq(refSyntax("seq"), repeat(seq(/*ws,*/ refSyntax("seqList")))));
            // seq = (regExp | IDENTIFIER | STRING | INTEGER | , )
            setSyntax("seq", choice(reExFmt, identifire, string, number, comma));
            // block: "(" rule ")"
            setSyntax("block", seq(lpr, refSyntax("rule"), rpr));
            // repeat: "{" rule "}"
            setSyntax("repeat", seq(lbr, refSyntax("rule"), rbr));
            // option: "[" rule "]"
            setSyntax("option", seq(lbk, refSyntax("rule"), rbk));
            // notes: "#Notes" "Syntax" eol rule "}}" "#Notes" "End" eol
            setSyntax("notes", seq(notes, ws, syntax, eol, refSyntax("syntax"), notes, ws, end, eol));

見直すのはコレだけなのに。

  • ログの大半がホワイトスペースだから何とかしたい
    • パーサから空白を読み飛ばす字句解析結果のを参照する?
    • パーサで作成したtoken(文字と正規表現)を適切な順に並べる
      • 勝手に空白を読み飛ばすので楽なハズ

クリアしたら、

  • 残りのテストを・・・
  • パーサコンテナを作る
  • 各パーサをサブクラス化
  • コードを読む部分を作る
  • 何かの言語に変換する部分を作る

先はまだまだ長い。

ps.2021/12/6

サブクラス化したもののソースは1000行を越えた。何か間違えている様だ。

サブクラス化 の過程で

  • リテラルにはプロパティが付けられない
    • String(“hohegoge”)ではダメ。
    • new String(“hohegoge”) でOK.
    • その辺は throw Error(“hogehoge”);も同じなんだろう。
  • RegExpにgオプションを付けlastIndexプロパティに位置を指定しても文字列の最初から検索する
    • 一度素振り(検索)させ自力でlastIndexを初期化しないとダメ
    • gは空振り(null)すると、とても機嫌が悪い。
  • RegExpのinputプロパティにオリジナルの文字列のコピーが作られる
    • メモリが心配
  • Refer.call を使ってみた
    • 横に長くなる
      • 伸びた部分だけ見れば良いので見やすいと思う人もいるかも
      • 元に戻す
  • super.メソッドみたいな呼び方ができる
    • なんちゃってオーバーライド風に書いてみると、無限ループする

18行1列まで読めた。 行末の _ 結合演算子がムズイ。

ps.2021/12/7

事前にザックリと空白、改行、その他でトークン化したついでにコメント文を空白化。後は _ かな。

全文からワード単位で字句解析するようになってブラウザの動作は軽くなった気がする。

ワード単位で取り出す時に、文末が” _”なら、次の改行を無視すればいいのかな?

ps.2021/12/8

手直しをしたら余計悪化。

0回ループOKなRepeatがネストしたら無限ループしてしまうので、Sequenceで評価がTrueでもpositionが進まない時は、評価をFalseにしたけど、まだループ。

仕方が無いので、catch exceptionで強制停止。300万行とか凄い行数になってたので、1000行までに制限。

やっと途中経過が可視化できた。ログのネストを深く掘り下げると

ParserInfo (ParserInfo)で、字句解析まで見れるけど。文法解析まで見れない。

ps.2021/12/9

手直し。 ダンプの仕方を見直し。

もう、本題を忘れてダンプに力が入る。

ps.2021/12/11

手直。 ダンプの仕方を見直し。  TreeView.markingをPromiseってみた。設定できてるけど、処理が終わったと返ってこない。

ps.2021/12/13

関数のダンプが修正漏れでエラっていたを訂正。



[JavaScript]dropイベント

MDNにフォルダをドロップして中身をリストアップするサンプルを見つけた。

FireFoxではうまく動くが、ChromeやEdgeでは・・・・・・・・・

code: 0
message: "A URI supplied to the API was malformed, or the resulting Data URL has exceeded the URL length limitations for Data URLs."
name: "EncodingError"

が出てしまう。

「APIに提供されたURIの形式が正しくないか、結果のデータURLがデータURLのURL長の制限を超えています。」

ってファイル名のせい?

しかし、C:¥aaaでも同じ結果だった。

原因は判らないけど、コードをアップしてみると・・・何故かうまくいく(アップ先)。

どうやら、httpなURLなパスにあるHTMLファイルをブラウザで開いている場合はOKらしい。

微妙だ。どっちかと云えば、

PCにコピったHTMLファイルでウマくいく方が安心なのに

得体のしれないのURLの画面のページに自分のPCのフォルダをドラッグするのは・・・

素で怖いw

と思う。

後、先の参考URLの注釈通りにreadEntries は、100個までしか読んでくれない。

forEachはPromiseを返さないので、非同期処理を抱えてる部分を含めて、

ローカル関数に書き換えたが forEachの最終ループで再起すると

どう見ても無限ループにしか見えない

ここは、forEachをmapにして暗黙の同期化を発動する。

ついでにmapの戻り値から readEntries が空っぽを渡たさなかった場合のみ再起するようにした。

readEntries  も非同期っぽいから、ローカル関数のままか。

フォルダに203個ファイルがあっても全部書き出してくれたから、

多分大丈夫。

メデタシ、メデタシ。(かな?

あ、HTMLのINPUTタグでwebkitdirectory を指定すると

フォルダも選択できるらしい。

こっちの方が良さそう。

でも、非標準らしい。



[JavaScript clipboard]表の行の背景色を

表をTABLEタグ風のテキストをEXCELに貼ると、それっぽい表になる。

でも安易に

TD付の行リスト.push("<td>" + data1 + "</td><td>" + ... + "</td");
・・・
TD付の行リスト.push("<td>" + data1 + "</td><td>" + ... + "</td");
var str="<table><tr>" +TD付の行リスト.join("</tr><tr>") + "</tr></table>");
window.clipboardData.setData("Text" , str);

な感じで作ってしまったら、行の背景色を交互に変えるのがめんどくさくなってしまった。

TD付の行リスト.push("<td>" + data1 + "</td><td>" + ... + "</td");
・・・
TD付の行リスト.push("<td>" + data1 + "</td><td>" + ... + "</td");
var p = "<table><tr ${bgColorRow}>" +TD付の行リスト.join("</tr><tr>") + "</tr></table>";

// 行の背景色(${bgColorRow})を交互に変える
var i=0;
var str= p.replaceAll(/\$\{bgColorRow\}/g, function(pat){
   i++;
   return  (i%2)? '': 'bgcolor="lightgrey"';
}));
window.clipboardData.setData("Text" , str);

でもいいけど、10,000行をJoinした後にReplaceAllは重すぎる様な気がした。




top