とりあえず書きなぐる

とりあえず、マイノリティーなプログラムを極力説明せずに書きなぐります

GitHubにあげちゃる

流行に乗ってGitHubにあげてみました
VisualStudiのソリューションをそのまま挙げてます
訳も分からやってますので変だったらゴメンナサイ
github.com

非VisualStudiの場合はhtml header内の"@Url.Content"を直接のパスに書き換えてください
ページ移動にはcm.changeLocationを読んでますのでローカルで動作させる場合はcm.jsに修正が必要です

GtiHub謹製GitHub Desktopを使ってみました
これが使いやすいかはわかりません
リポジトリは公開、プライベートとも無料で作成可能なようです
プライベート→公開はWebでしか出来ないようです
1.対象リポジトリからSettingsタブ選択
2.リストメニューからOptionsを選択
3.ページ下端のDanger Zoneで公開、プライベートの変更

ドロップダウン

jQuery Mobileにはドロップダウンは標準でもあるのですが、いろいろ痛い目に合わされるので、ボタンとポップアップで作ってみます

/*
 * ドロップダウン
 */
interface DropdownOptions {
    alignRight?: boolean;
    onSelected?: (selItem: Dropdown.Item) => void; // 選択された
    onClosed?: any; // 閉じられた
}

namespace Dropdown {
    "use strict";

    export class Item {
        text: string;
        value: any;
        constructor(text?: string, value?: any) {
            this.text = text;
            this.value = value;
        }
    }

    /*
     * ドロップダウンリスト表示
     */
    export function open(selector: JQuery | string, items: Item[], options?: DropdownOptions) {
        let target = $(selector);
        let style = "min-height:1em;";
        if (options && options.alignRight) {
            style += "text-align:right;";
        }

        let content = "";
        content += "<div";
        content += " data-role='popup'";
        content += " data-history='false'";
        content += " data-dismissible='true'";
        content += ">";
        content += " <div role='main' class='ui-content'>";
        content += "  <ul data-role='listview' data-icon='false'>";
        for (let i = 0; i < items.length; i++) {
            content += "   <li idx=" + i + ">";
            content += "    <a href='#' style='" + style + "'>" + items[i].text + "</a>";
            content += "   </li >";
        }
        content += "  </ul>";
        content += " </div>";
        content += "</div>";

        // ポップアップ作成
        let box = $(content)
            .appendTo(cm.getActivePage())
            .enhanceWithin()
            .popup();

        // 選択
        box.find("li").off("click").on("click", function (event: Event) {
            let idx = parseInt($(this).attr("idx"), 0);
            if (options && options.onSelected) {
                options.onSelected(items[idx]);
            }
            Popup.close();
        });

        // 表示
        let pos = Popup.Pos.downLeft(target);	// 位置取得
        if (options && options.alignRight) {
            pos = Popup.Pos.downRight(target);
        }

        Popup.open(box, {
            transition: "none"
            , positionFixed: false
            , x1: pos.x1
            , y1: pos.y1
            , x2: pos.x2
            , y2: pos.y2
            , onClosed: function () {
                box.remove();
                if (options && options.onClosed) {
                    options.onClosed();
                }
            }
        });
    }
}

ポップアップの表示には以前に作成したものを使用します
コールバックはオプションで取得します
固有のCSSはありません

使い方

        $(document).on("click", "#ボタンID", function (event: Event) {
            try {
                // Pageをoverflow:hideでないと2重スクロール
                Dropdown.open(
                    $(this)
                    , [
                        { text: "選択1", value: 1 }
                        , { text: "選択2", value: 2 }
                        , { text: "選択3", value: 3 }
                        , { text: "選択4", value: 4 }
                        , { text: "選択5選択5", value: 5 }
                    ]
                    , {
                        onSelected: (selItem: Dropdown.Item) => {
                            if (selItem) {
                                $(this).text(selItem.text).attr("value", selItem.value);
                            }
                        }
                        , onClosed: () => {
                            console.log("dropdown closed");
                        }
                        , alignRight: false
                    });

            } catch (e) {
                cm.showErrMsg(e);
            }
        });

Dropdown.Itemで表示するアイテムを作成してDropdown.Openします
コールバックはアロー式でやってみました
アロー式を使用した場合、thisは呼び出し元のままになります

HandsonTableの編集の続き

以前にHandsonTableの編集でPopupを表示して・・・とやりましたが死角がありました

その1
Popup表示後のタブキー押下で制御がHandsonTableに戻ってしまい、裏でセル選択が移動します
対策としてキー入力禁止メソッドを作成しておきます
合わせてカレントセル保存復元メソッドも追加します
全景載せておきます
/Src/ht.ts

/// <reference path="../scripts/typings/jquery/jquery.d.ts" />
/// <reference path="../scripts/typings/jquerymobile/jquerymobile.d.ts" />
/// <reference path="../scripts/typings/handsontable/handsontable.d.ts"/> // 改変済

/*
 * Handsontable Helper
 */
namespace Ht {
    "use strict";

    export namespace CurrentCell {
        let curRow = -1;
        let curCol = -1;

        export function save(ht: Handsontable, row: number, col: number) {
            curRow = -1;
            curCol = -1;
            if (row < 0) {
                curRow = getCurrentRow(ht);
            }
            if (col < 0) {
                curCol = getCurrentCol(ht);
            }
        }

        export function restor(ht: Handsontable, row: number, col: number) {
            let isRestor = false;

            if (curRow >= 0) {
                row = curRow;
                isRestor = true;
            }
            if (curCol >= 0) {
                col = curCol;
                isRestor = true;
            }

            if (isRestor) {
                curRow = -1;
                curCol = -1;
                ht.selectCell(row, col);
            }
        }
    }

    /*
     * キー入力禁止
     */
    export function setDisableKey(event: any) {
        // 漢字系
        if ([28, 29, 241, 242, 243, 244].indexOf(event.keyCode) >= 0) {
            event.isImmediatePropagationEnabled = false;
            event.isImmediatePropagationStopped = function () {
                return true;
            };
        }

        // ポップアップ表示中
        // サブウィンドウ表示中
        if (Popup.isActive() || SubWindow.isVisible()) {
            event.isImmediatePropagationEnabled = false;
            event.isImmediatePropagationStopped = function () {
                return true;
            };
        }
    }

    /*
     * 親コンテナ取得
     */
    export function getContainerParent(ht: Handsontable): JQuery {
        return $(ht.container).parent("div");
    }

    /*
     * 垂直スクロール位置取得
     */
    export function getVScrollPos(ht: Handsontable): number {
        return $(ht.container).children(".wtHolder").scrollTop();
    }

    /*
     * 垂直スクロール位置設定
     */
    export function setVScrollPos(ht: Handsontable, value: number) {
        $(ht.container).children(".wtHolder").scrollTop(value);
    }

    /*
     * 水平スクロール位置取得
     */
    export function getHScrollPos(ht: Handsontable): number {
        return $(ht.container).children(".wtHolder").scrollLeft();
    }

    /*
     * 水平スクロール位置設定
     */
    export function setHScrollPos(ht: Handsontable, value: number) {
        $(ht.container).children(".wtHolder").scrollLeft(value);
    }

     /*
      * 現在行列位置取得
      */
    export function getCurrentRowColPos(ht: Handsontable): number[] {
        let row = -1;
        let col = -1;
        if (ht) {
            let sel = ht.getSelected();
            if (sel && sel.length > 0) {
                row = sel[0][0];
                col = sel[0][1];
            }
        }
        let vpos = getVScrollPos(ht);
        let hpos = getHScrollPos(ht);
        return [row, col, vpos, hpos];
    }

    /*
      * 現在行列位置設定
      */
    export function setCurrentRowColPos(ht: Handsontable, pos: number[]) {
        if (!pos || !ht.countRows() || !ht.countCols()) {
            return;
        }
        let row = pos[0];
        if (!row) {
            row = 0;
        } else {
            row = Math.max(row, 0);
            row = Math.min(row, ht.countRows() - 1);
        }

        let col = pos[1];
        if (!col) {
            col = 0;
        } else {
            col = Math.max(col, 0);
            col = Math.min(col, ht.countCols() - 1);
        }

        let vpos = pos[2];
        if (!vpos) {
            vpos = 0;
        }

        let hpos = pos[3];
        if (!hpos) {
            hpos = 0;
        }

        ht.selectCell(row, col);
        setVScrollPos(ht, vpos);
        setHScrollPos(ht, hpos);
    }

     /*
      * 現在行取得
      */
    export function getCurrentRow(ht: Handsontable): number {
        let row = -1;
        if (ht) {
            let sel = ht.getSelected();
            if (sel && sel.length > 0) {
                row = sel[0][0];
            }
        }
        return row;
    }

     /*
      * 現在列取得
      */
    export function getCurrentCol(ht: Handsontable): number {
        let col = -1;
        if (ht) {
            let sel = ht.getSelected();
            if (sel && sel.length > 0) {
                col = sel[0][1];
            }
        }
        return col;
    }

     /*
      * 現在列取得
      */
    export function getCurrentProp(ht: Handsontable): string {
        let prop = "";
        if (ht) {
            let sel = ht.getSelected();
            if (sel && sel.length > 0) {
                prop = <string>ht.colToProp(sel[0][1]);
            }
        }
        return prop;
    }

     /*
      * 全面表示
      */
    export function setFullSize(ht: Handsontable) {
        if (ht) {
            let sz = cm.getPageContentSize();

            ht.updateSettings({
                width: sz[0]
                , height: sz[1]
            }, false);
            getContainerParent(ht).width(sz[0]).height(sz[1]);
            ht.render();
        }
    }

     /*
      * 表示位置へスクロール
      */
    export function setRowScroll(ht: Handsontable, row?: number): boolean {
        if (typeof row === "undefined") {
            row = getCurrentRow(ht);
        }
        let col = getCurrentCol(ht);

        // ビュー内に無ければスクロール
        {
            let first = ht.view.wt.wtScroll.getFirstVisibleRow();
            if (row < first) {
                return ht.scrollViewportTo(row, col);
            }
        }
        {
            let last = ht.view.wt.wtScroll.getLastVisibleRow();
            if (row > last) {
                return ht.scrollViewportTo(row, col, true);
            }
        }
        return false;
    }

    export function setColScroll(ht: Handsontable, col?: number): boolean {
        if (typeof col === "undefined") {
            col = getCurrentCol(ht);
        }
        let row = getCurrentRow(ht);

        // ビュー内に無ければスクロール
        {
            let first = ht.view.wt.wtScroll.getFirstVisibleColumn();
            if (col < first) {
                return ht.scrollViewportTo(row, col);
            }
        }
        {
            let last = ht.view.wt.wtScroll.getLastVisibleColumn();
            if (col > last) {
                return ht.scrollViewportTo(row, col, true);
            }
        }
        return false;
    }

    /*
     * 選択と表示位置へスクロール
     */
    export function setRowSelectAndScroll(ht: Handsontable, row: number): boolean {
        let col = getCurrentCol(ht);
        if (ht.selectCell(row, col) === true) {
            return setRowScroll(ht, row);
        } else {
            return false;
        }
    }

    export function setColSelectAndScroll(ht: Handsontable, col: number): boolean {
        let row = getCurrentRow(ht);
        if (ht.selectCell(row, col) === true) {
            return setColScroll(ht, col);
        } else {
            return false;
        }
    }

    /*
     * データ初期化
     */
    export function initData(ht: Handsontable) {
        ht.loadData([]);
        ht.updateSettings({ maxRows: 0 }, false);
    }

    /*
     * カラムヘッダタイトル取得
     */
    export function getColumnTitle(ht: Handsontable, col: number | string): string {
        let colidx = 0;
        if (typeof col === "string") {
            colidx = ht.propToCol(<string>col);
        } else {
            colidx = col;
        }
        let hd = ht.getColHeader(colidx);
        return $(hd).text();
    }

}

HandsonTable作成時オプションの対象イベントで呼び出します
/Src/table.ts

	/*
	 * テーブル
	 */
        namespace HTable {
 		/*
		 * 作成
		 */
		export function build() {
                _ht = new Handsontable($("#list_table")[0], {
                    data: null
                    , columns: getColumns()                 // 列設定
                    , colHeaders: getColHerders				// 列見出し
                    , rowHeaders: true						// 行見出し
                    , rowHeaderWidth: 30					// 行見出し幅
                    , manualColumnResize: true				// 列幅変更
                    , multiSelect: false					// 複数選択
                    , stretchH: "none"						// 水平ストレッチ
                    , autoColumnSize: false					// 自動サイズ調整
                    , wordWrap: false						// セル内折り返し
                    , outsideClickDeselects: false			// 選択を維持
                    , disableVisualSelection: "area"		// 範囲選択不可
                    , selectionMode: "single"               // 選択モード
                    , startRows: 0							// データ無時の行数
                    , trimWhitespace: false					// 前後の空白トリム
                    , currentRowClassName: "current-row"	// 選択列にクラス名付加
                    , rowHeights: function (row: number) {	// 行高さ
                        return 50;
                    }
                    , enterMoves: { row: 0, col: 0 }        // Enterキー移動先
                    , autoWrapCol: false					// 列移動ループ
                    , autoWrapRow: false					// 行移動ループ
                    , fillHandle: false                     // 選択範囲を埋める
                    , beforeOnCellMouseDown(event: Event, coords: any) {
                        if (_ht) {
                            Ht.CurrentCell.save(_ht, coords.row, coords.col);
                        }
                    }
                    , afterSelection(row: number, col: number) {
                        if (_ht) {
                            Ht.CurrentCell.restor(_ht, row, col);
                        }
                    }
                    , beforeKeyDown(event: any) {
                        Ht.setDisableKey(event);	// 無効なキー入力設定
                    }
                });
		.setFullSize(_ht);	// 全面表示
	}

    ....

}

漢字系潰しは他所にあったものをパクッてきました

その2
Android上でPopupの表示を維持できませんでした
出た瞬間に閉じられます
タイマー使って回避します

            let CustomEditor = Handsontable.editors.TextEditor.prototype.extend();
	    {
		CustomEditor.prototype.beginEditing = function () {		// 編集開始前

                    ....

		    let td = this.TD;
                    // ポップアップ表示
		    setTimeout(function () {	
			Popup.open(dlg, {        // dlgがPopup
			    positionTo: td
			    , focusSelector: ipt
			    , onOpend: function () {
				ipt.NumberSpin({ digits: 0, slider: true });    // iptがPopup上のinput
			    }
			});
		    }, 0);

                    ....

	        };
            }

メッセージボックスを一捻り

スクロールするページでcm.MsgBoxを表示した時、ヘッダやフッタのボタンを基準にcm.MsgBallonを表示した時に「あ”」ってなったので、位置固定フラグを追加します

/Src/cm.ts

namespace cm{

    ....

    /*
     * メッセージバルーン
     */
    export class MsgBallon {
        onClosed(result: boolean) { ; };
        constructor(message: string, positionTo: any, positionFixed: boolean, hasCancel?: boolean) {
            let scope = this;

            let content = "";
            content += "<div";
            content += " data-role='popup'";
            content += " data-history='false'";
            content += " data-overlay-theme='a'";
            content += ">";

            content += " <div role='main' class='ui-content'>";
            content += "  <p>" + message + "</p>";
            content += " </div>";

            content += " <div data-role='footer' style='text-align:right;padding:0.3em;'>";
            content += "  <a href='#' class='ui-btn ui-btn-inline ui-btn-active' cmd='apply'>OK</a>";
            if (hasCancel) {
                content += "  <a href='#' class='ui-btn ui-btn-inline' cmd='cancel'>キャンセル</a>";
            }
            content += " </div>";
            content += "</div>";

            // 作成
            let box = $(content)
                .appendTo(cm.getActivePage())
                .enhanceWithin()
                .popup();

            // ボタン押下
            let result = false;
            box.find(".ui-btn").off("click").on("click", function (event: Event) {
                let cmd = $(this).attr("cmd");
                switch (cmd) {
                    case "apply":
                        result = true;
                        box.popup("close");
                        break;

                    case "cancel":
                        box.popup("close");
                        break;
                }
            });

            // 表示
            Popup.open(box, {
                transition: "pop"
                , positionTo: positionTo
                , positionFixed: positionFixed
                , arrow: true
                , closeButton: true
                , onClosed: function () {
                    scope.onClosed(result);
                    box.remove();
                }
            });
        }
    }

    export namespace MsgBallon {
        export function show(message: string, positionTo: any, positionFixed: boolean, hasCancel?: boolean) {
            return new MsgBallon(message, positionTo, positionFixed, hasCancel);
        }
    }

    /*
     * メッセージボックス
     */
    export class MsgBox {
        onClosed(result: boolean) { ; };

        constructor(message: string, title: string, hasCancel?: boolean) {
            let scope = this;

            let content = "";
            content += "<div";
            content += " data-role='popup'";
            content += " data-history='false'";
            content += " data-overlay-theme='b'";
            content += " data-dismissible='false'";
            content += ">";

            content += " <div data-role='header'>";
            content += "  <h1>" + title + "</h1>";
            content += " </div>";

            content += " <div role='main' class='ui-content'>";
            content += "  <p style='text-align:center;'>" + message + "</p>";
            content += " </div>";

            content += " <div data-role='footer' style=text-align:center;padding:0.3em;>";
            content += "  <a href='#' class='ui-btn ui-btn-inline ui-btn-active' cmd='apply'>OK</a>";
            if (hasCancel) {
                content += "  <a href='#' class='ui-btn ui-btn-inline' cmd='cancel'>キャンセル</a>";
            }
            content += " </div>";
            content += "</div>";

            // 作成
            let box = $(content)
                .appendTo(cm.getActivePage())
                .enhanceWithin()
                .popup();

            // ボタン押下
            let result = false;
            box.find(".ui-btn").off("click").on("click", function (event: Event) {
                let cmd = $(this).attr("cmd");
                switch (cmd) {
                    case "apply":
                        result = true;
                        box.popup("close");
                        break;

                    case "cancel":
                        box.popup("close");
                        break;
                }
            });

            // 表示
            Popup.open(box, {
                transition: "pop"
                , positionFixed: true
                , onClosed: function () {
                    scope.onClosed(result);
                    box.remove();
                }
            });
        }
    }

    export namespace MsgBox {
        export function show(message: string, title: string, hasCancel?: boolean) {
            return new MsgBox(message, title, hasCancel);
        }
    }

    ....

}

ページのスクロールに付いていかなようにMsgBoxは常に位置固定、MsgBallonはパラメータ指定としました

ついでにPopupをボタンのドロップダウンメニューとして使えるように設定位置取得ヘルパーを追加
Popupオプションのx1,y1,x2,y2に与える数値を取得します
/Src/cm.ts

namespace Popup {

    ....

    export namespace Pos {
        export function downLeft(selector: JQuery | string, fixedParent: boolean) {
            return getPosition(selector, "down", "left", fixedParent);
        }
        export function downRight(selector: JQuery | string, fixedParent: boolean) {
            return getPosition(selector, "down", "right", fixedParent);
        }
        export function upLeft(selector: JQuery | string, fixedParent: boolean) {
            return getPosition(selector, "up", "left", fixedParent);
        }
        export function upRight(selector: JQuery | string, fixedParent: boolean) {
            return getPosition(selector, "up", "right", fixedParent);
        }
        export function rightDown(selector: JQuery | string, fixedParent: boolean) {
            return getPosition(selector, "right", "down", fixedParent);
        }
        export function rightUp(selector: JQuery | string, fixedParent: boolean) {
            return getPosition(selector, "right", "up", fixedParent);
        }
        export function leftDown(selector: JQuery | string, fixedParent: boolean) {
            return getPosition(selector, "left", "down", fixedParent);
        }
        export function leftUp(selector: JQuery | string, fixedParent: boolean) {
            return getPosition(selector, "left", "up", fixedParent);
        }

        function getPosition(selector: JQuery | string, location: string, align: string, fixedParent: boolean) {
            let target = $(selector);
            let offset = target.offset();

            let x1: number = NaN;
            let y1: number = NaN;
            let x2: number = NaN;
            let y2: number = NaN;

            switch (location) {
                case "down":
                    y1 = offset.top + target.outerHeight();
                    break;
                case "up":
                    y2 = $(window).height() - offset.top;
                    break;
                case "right":
                    x1 = offset.left + target.outerWidth();
                    break;
                case "left":
                    x2 = $(window).width() - offset.left;
                    break;
            }

            switch (align) {
                case "left":
                    x1 = offset.left;
                    break;
                case "right":
                    x2 = $(window).width() - offset.left - target.outerWidth();
                    break;
                case "down":
                    y1 = offset.top;
                    break;
                case "up":
                    y2 = $(window).height() - offset.top - target.outerHeight();
                    break;
            }
            return { x1: x1, y1: y1, x2: x2, y2: y2 };
        }
    }
}

TryCatch

所謂例外処理です
が、特に特別な処理を行うつもりはありません
あくまで想定外の例外に対する備えなので、発生した時点でおしまいです
イベントドリブンではイベントの入り口に片っ端から仕掛けて、ありのままのエラーを表示すれば良いと思います
逆にメソッドなんかに仕掛けたり、例外先でリカバリなんてもことにすると軽く地獄を見られます

汎用的なエラー表示メソッドを作成します
./Src/cm.ts

namespace cm {

    ... 

    /*
     * エラー表示
     */
    export function showErrMsg(e: any) {
        let msg: any;
        if (e.stack) {
            msg += e.stack + "\n";
        }
        if (e.status) {
            msg += e.status + ":" + e.statusText + "\n";
        } else if (e.statusText) {
            msg += e.statusText + "\n";
        }
	if (e.responseText) {
            msg += e.responseText + "\n";
	}
	if (e.message) {
            msg += e.message + "\n";
	}
        if (!msg) {
            msg = e;
	}
        alert(msg);
    }

    ...

とりあえず身の回りにあるエラー関連のメンバーはこんな感じでした
取れる情報は全て表示を是としています

        $(document).on("pagecontainershow", function (event: Event, ui: any) {
            try {

                ...

            } catch (e) {
                cm.showErrMsg(e);
            }
        });

        $(document).on("click", "#popupMenu > li > .ui-btn", function (event: Event) {
            try {
                $(this).blur();
                let cmd = $(this).attr("cmd");

               ...
                
            } catch (e) {
                cm.showErrMsg(e);
            }
		});

こんな感じのワンパターンを一心不乱に追加し続けます
ボタン押下等には$(this).blur()を追加し、二度押し防止します
フォーカスが外れてしまいますが気にしません

まあ、この辺はお好みで

ブラウザのスクロール位置を復元してみる

シングルページ内でのスクロール位置の保持、復元してみます
シングルページで他ページへ移動後、元ページへ戻ってきてスクロール位置が復元されていないと"イラッ"とします
スクロール位置の保持と復元用クラスを作成してみます

/Src/cm.ts

class ScrollPos {
    private _value = 0;
    private _target: JQuery;

    constructor(selector?: string | JQuery) {
        // 対象無しはドキュメント対象
        if (typeof selector !== "undefined") {
            this._target = $(selector);
        }
    }

    /*
     * スクロール位置取得
     */
    getScrollValue(): number {
        if (this._target) {
            return this._target.scrollTop();
        } else {
            return document.documentElement.scrollTop || document.body.scrollTop;
        }
    }

    /*
     * スクロール実行
     */
    doScroll(value?: number) {
        if (typeof value === "undefined") {
            value = this._value;
        }
        if (this._target) {
            this._target.scrollTop(value);	// 対象BOX位置
        } else {
            $.mobile.silentScroll(value);	// ドキュメント位置
        }
    }

    /*
     * 設定値設定
     */
    setValue(value?: number) {
        if (typeof value === "undefined") {
            value = this.getScrollValue();  // 現在のスクロール位置
        }
        this._value = value;
    }

    /*
     * 設定値参照
     */
    getValue(): number {
        return this._value;
    }
}
  • コンストラクタ 対象を指定、無い場合はdocument
  • getScrollValue 対象の現在のスクロール位置を取得
  • doScroll 指定位置、もしくは保持された位置へスクロール
  • setValue スクロール位置を保持、引数がない場合は現在位置を保持
  • getValue 保持されている値を取得

サンプルではHandsontableのスクロール位置保持と合わせて位置復元します
自ページ移動前に位置を保持しています
/Src/mobile.ts

    namespace Page1 {
    
    ....

        let _pos = new ScrollPos();

        ....

        $(document).on("pagecontainershow", function (event: Event, ui: any) {

            ....

            $(window).resize();
            _pos.doScroll();	// スクロール位置復元
            mq.update();
        });

	/*
	 * ページ終了前
	 */
        $(document).on("pagecontainerbeforehide", function (event: Event, ui: any) {
            if (cm.getPrevPageId(ui) !== "page1") {
                return;
            }
            _pos.setValue();	// スクロール位置保持
        });

        ....

    }

    namespace Page2 {

        ....

        let _pos: number[] = [];				// 現在位置

        ....

	/*
	 * ページ表示前
	 */
        $(document).on("pagecontainerbeforeshow", function (event: Event, ui: any) {
            if (cm.getToPageId(ui) !== "page2") {
                return;
            }
            if (_ht) {
                Ht.initData(_ht);
                _ht.render();
            }
        });
	/*
	 * ページ表示
	 */
        $(document).on("pagecontainershow", function (event: Event, ui: any) {

            ....

            Ht.setCurrentRowColPos(_ht, _pos);	// 位置復元
            _ht.render();							// 再描画
            mq.update();
        });
	/*
	 * ページ終了前
	 */
        $(document).on("pagecontainerbeforehide", function (event: Event, ui: any) {
            if (cm.getPrevPageId(ui) !== "page2") {
                return;
            }
            if (_ht) {
                _pos = Ht.getCurrentRowColPos(_ht);
            }
        });
    }

ロケーションを移動してみる

シングルページとはいっても、やはりアプリケーションは複数のHTMLで構成されます

ロケーション移動メソッドを作成します
/Src/cm.ts

namespace cm {
    "use strict";

    /*
     * サイトのルート取得
     */
    let _siteRoot = "";
    export function getSiteRoot(): string {
        if (!_siteRoot) {
            let scripts = document.getElementsByTagName("script");
            let i = scripts.length;
            while (i--) {
                let match = scripts[i].src.match(/(^|.*\/)cm\.js*/);
                if (match) {
                    _siteRoot = match[1] + "../";		// cm.jsの場所からルートを設定
                    break;
                }
            }
            let emt: HTMLAnchorElement = <HTMLAnchorElement>$("<a>").attr("href", _siteRoot).get(0);
            _siteRoot = emt.href;
        }
        return _siteRoot;
    }

    /*
     * ロケーション移動
     */
    export interface PostData {
        [index: string]: string | number;
    }

    export function changeLoacation(url: string, data?: PostData) {
        // オフライン
        if (isOffline() === true) {
            return;
        }
	if (!url) {
		url = "";
	}

        url = getSiteRoot() + url;
        if (data) {
            // POST
            let f = $("<form/>", { "action": url, "method": "post", "target": "_self" });
            for (let key in data) {
                if (key) {
                    f.append($("<input/>", { "type": "hidden", "name": key, "value": data[key] }));
                }
            }
            f.appendTo(document.body);
            f.submit();
            f.remove();

        } else {
            // GET
            location.replace(url);
        }
    }

    ....

}

トレイリングスラッシュ対策としてgetSiteRoot()内で"cm.js"の場所を基準にしてルートのURLを取り直しています
サーバー側の設定次第なのでしょうが、念のため
基準とするファイル名、フォルダ構成によっては修正が必要です
大抵の場合GETで間に合うのですが、POSTも用意しておきます
Key-Valueで値が渡された場合はFORMを作成してsubmit()でPOSTしています
(FORMはBodyに追加する必要があるかもしれません)

サンプルです
/Src/mobile.ts

	/*
	 * ツールバーボタン押下
	 */
        $(document).on("click", "#page1_toolbar1 .ui-btn, #page1_toolbar2 .ui-btn", function (event: Event) {
            let cmd = $(this).attr("cmd");
            switch (cmd) {
                case "home":
                    cm.changeLoacation("home");
                    break;

                case "table":
                    cm.changeLoacation("table");
                    break;
            }
        });

/Views/Mobile/Index.vbhtmlでツールバーにidを付け、適当なボタンにcmd属性を追加してやります