クラス操作関数

以前に正規表現を使用したことを思い出し調べていたときにでてきたソースです。実際、正規表現は一か所でしか使用していないのですが、こうゆう使い方もあるということで、クラス操作関数とあわせて備忘録として残しておこうと思います。
クラス操作は classList のメソッドで容易に行うことができますが、Can I use で調べてみると Android 4.4 からの対応になっています。それ以前のバージョンに対応する場合は classList の代用が必要になりますが、そのようなときに使えるかと思います。(indexOf()を使用しているのでクラス名の重複部分には注意が必要になります)

/***
 * クラス追加
 */
function add_class(elem, cls) {
    if (!elem || !cls) return;
    elem.className = add_str(elem.className, cls);
}
/***
 * クラス削除
 */
function remove_class(elem, cls) {
    if (!elem || !cls) return;
    elem.className = remove_str(elem.className, cls);
}
/***
 * クラス確認
 */
function has_class(elem, cls) {
    if (!elem || !cls) return;
    return has_str(elem.className, cls);
}
/***
 * クラス追加・削除
 */
function toggle_class(elem, cls) {
    if (!elem || !cls) return;
    elem.className = toggle_str(elem.className, cls);
}

// 文字列 strings に文字列 str を追加する
function add_str(strings, str) {
    var i = strings.indexOf(str);
    if (i === -1) {
        var len = strings.length;
        if (len !== 0 && strings.charAt(len -1) !== " ") {
            strings += " ";
        }
        strings += str;
    }
    return strings;
}

// 文字列 strings から文字列 str を削除する
function remove_str(strings, str) {
    var i = strings.indexOf(str);
    if (i !== -1) {
        strings = strings.substr(0, i)
                + strings.substr(i + str.length + 1);
    }
    return strings;
}

// 文字列 strings に文字列 str が含まれているか検査する
function has_str(strings, str) {
    var regex = new RegExp("(^|\\s)" + str + "(\\s|$)");
    if (regex.test(strings)) return true;
    else return false;
}

// 文字列 strings に文字列 str があれば str を削除し、
// str がなければ追加する
function toggle_str(strings, str) {
    if (has_str(strings, str)) {
        return remove_str(strings, str);
    } else {
        return add_str(strings, str);
    }
}

Meditation Noteアップデート作業中

現在、瞑想アプリ「Meditation Note」のアップデート作業を行っています。このアプリは私が初めて作成したアプリで、これまでにほとんどダウンロードされていないのですが、思い入れがとても強いアプリです。瞑想関連のアプリはすでにたくさんありますし、現在も新しいタイプのアプリがリリースされ続けているので、PLAYストアで「瞑想」で検索しても表示すらされない状態です。それでも、もともと「自分で使いたい」という想いで作った記念すべき初アプリなので、少しでもいいものにしたいと思いアップデートすることにしました。

まず、今回グラフを追加しようと思い、ソースを調べました。2作目、3作目のアプリでグラフを使ったことがあるので今回は楽かなと思っていたのですが、ソースを見て固まってしまいました。

… グラフに必要なデータである日付と時間が一つの文字列になってる …

// 日本語版
record: {
  date: "2016/10/31(月) 09:00",
  ...
}

// 英語版
record: {
  date: "Mon. Oct.27, 2016, 09:00 am",
  ...
}

作成当初グラフ化なんて考えてもいなかった(そんな余裕がなかった)からか「日付は表示するだけだから」と文字列にしてしまいました。グラフは、横軸を日付、縦軸を累積時間や回数にしようと思っているので、これを、

record: {
  date: {
    y: 2016,
    m: 9,
    d: 31,
    w: 0,
    s: {
      h: 9,
      m: 0
    }
  },
  ...
}

のような構造にして、同じ月にあたるレコードの時間と個数を集計できる形にしないと。

元の文字列から値を取り出す方法としてまず思いつくのが slice()などを使う方法。文字列の先頭から年にあたる4文字を取り出し、そこから一つ飛ばして、月にあたる2文字を取り出し、さらに、、、って、できないことはないんですけど、なんかイヤな感じが。文字列のインデックスを指定しなくちゃいけないので、ソースコードがごちゃごちゃしちゃうなと。で、悩んだ挙句、正規表現を使うことにしました。前に使ったことはあったのですが、ものの見事にすっかり忘れていて、本読みました。
ソースはこんな感じになりました。

result = date.match(/[^/() :.,]+/g);
if (mdt.lang_jp) {
  // "2016/10/27(木) 9:00"
  // [0]/[1]/[2]([3]) [4]:[5]
  record.date.y = Number(result[0]);
  record.date.m = Number(result[1]);
  record.date.d = Number(result[2]);
  record.date.w = Number(WEEK[result[3]]);
  record.date.s.h = Number(result[4]);
  record.date.s.m = Number(result[5]);
} else {
  // "Thu. Oct.27, 2016, 9:00 am"
  // [0]. [1].[2], [3], [4]:[5] [6]
  record.date.y = Number(result[3]);
  record.date.m = Number(MONTH[result[1]]);
  record.date.d = Number(result[2]);
  record.date.w = Number(WEEK[result[0]]);
  record.date.s.h = Number(result[4]) + ((result[6] === "pm") ? 12 : 0);
  record.date.s.m = Number(result[5]);
}

正規表現で「/」、「(」、「)」、「 」、「:」、「.」、「,」以外にマッチするパターンを見つけて配列に格納します。この抽出処理がたった一行で済むので、かなり便利です。
正規表現を使うことで複雑な処理を簡潔に記述するということは、些細なことのようでけっこう重要な気がします。なんとか使いこなせるようになりたいです。

ons-list-item の drag&drop

ons-list-item の Drag & Dropのサンプルです。

See the Pen ons-list-item drag&drop (jQuery UI) by senmyou (@senmyou) on CodePen.

jQuery UIのsortable機能を使っています。使用しているファイルは「jquery.min.js」、「jquery-ui.min.js」、「jquery.ui.touch-punch.min.js」です。
リストを2つ並べましたが、違いは「線(ボーダー)」だけです。ほんとは下のリストのほうがいいと思うのですが、下のリストは一番下のリストアイテムをドラッグしたとき、一番下の線が太く見えてしまうという問題があります。上のリストはたんにその応急処置的なものです。
なぜ太く見えるかというと、ドラッグ時にjQuery UIにより生成されるヘルパー用のons-list-itemがリストの一番下に配置されるため、そのひとつ前のリストアイテムに、
.list__item:last-child {
border-bottom: none;
}
が適用されず、ボーダーが引かれるからだと思われます。ヘルパー用の ons-list-item は position:absolute の指定によりドラッグポイントを追従して描画されますが、要素はドラッグ開始位置に生成されるため、一番下のリストアイテムをドラッグするとヘルパーは一番下に生成されます。

例えば、.list__item の last-childが .ui-sortable-helper のとき、そのひとつ前の要素が選択できればよさそうですが、やり方がわかりませんでした。なにかいい方法があればいいのですが。
とりあえず、ご参考まで。