2016年12月23日金曜日

javascript 改修作業 ~Blogger テンプレートを作るよ! (36)~

こんにちはー!
今回は、主に javascript の改修作業を行います。やることは主に単純化とモジュール化です。

(前回:『機能の分類について考える ~Blogger テンプレートを作るよ! (35)~』)

大幅に修正をかけてみた

テンプレートをざっと見ていて、一つの関数が長く続くと構造を変更するときにちょっと鬱陶しいと感じたので、なるべく行数を少なくしようと工夫してみました。

以下は、もともとの「フィードガジェットに『一覧はこちら >』を追加する」という機能のソースコードです。


いままでのソースコード(クリックで表示されます)
function addIndexLink(url, id, linkText) {
  // 引数チェック
  if (url == null || id == null) return;
  // blogger のフィード URL の形式か否か
  if (url.search(/^[^:]*:\/\/[^\/\?#]*\/feeds\/posts\/[^\/\?#]*/) == -1) return;
  // urlの基礎(アドレス部分の抽出)
  var urlbase = url.match(/^[^:]*:\/\/[^\/\?#]*/)[0];
  // urlのパス(ラベル部分の抽出)
  var urloption = url.slice(urlbase.length + "/feeds/posts/default".length).match(/^[^#\?]*/);
  // リンク先 URL の部品
  var linkUrl = urlbase + "/search";
  var linkOpt = [];
  // ラベル取得の場合
  if (urloption != null && urloption.length > 0 && urloption[0].search(/^\/-\/.+$/) != -1) {
    var labels = urloption[0].slice(3).split("/");
    // 最初の一つはラベル検索
    if (labels.length > 0) {
      linkUrl += "/label/" + labels[0];
    }
    // 二つ目以降はキーワード検索&関連度順
    if (labels.length > 1) {
      var q = labels[1];
      for (var i = 2; i < labels.length; i++) {
        q += "+" + labels[i];
      }
      linkOpt.push({"name": "q", "data": q});
      linkOpt.push({"name": "by-date", "data": "false"});
    }
  }
  // 特殊ケース対策(PCでモバイル表示中、モバイルでPC表示中)
  var mobileFlag = window.location.search.match(/[\?\&]m=\d[\?\&$]/);
  if (mobileFlag != null && mobileFlag.length > 0) {
    linkOpt.push({"name": "m", "data": mobileFlag[0].match(/\d/)[0]});
  } 
  // リンク URL の生成
  if (linkOpt.length > 0) {
    linkUrl += "?" + linkOpt[0].name + "=" + linkOpt[0].data;
    for(var j = 1; j < linkOpt.length; j++) {
      linkUrl += "&" + linkOpt[j].name + "=" + linkOpt[j].data;
    }
  }
  // リンクをガジェットに追加
  var widget = document.getElementById(id);
  var widgetContent = widget.querySelector(".widget-content");
  var widgetFooter = document.createElement("div");
  var widgetAnchor = document.createElement("a");
  var widgetLinkText = document.createTextNode(linkText);
  widgetAnchor.setAttribute("href", linkUrl);
  widgetAnchor.appendChild(widgetLinkText);
  widgetFooter.setAttribute("class", "widget-footer");
  widgetFooter.appendChild(widgetAnchor);
  widget.insertBefore(widgetFooter, widgetContent.nextSibling);
  var widgetClass = widget.getAttribute("class");
  widget.setAttribute("class", widgetClass + " linkAdded");
}


うーん、もっとすっきりさせたいなぁ。と、いうことで完成したのはこちらになります。

新しいソースコード

//*********************************
// フィードガジェットに「一覧はこちら >」のリンクの挿入をサポート
// Support insertion of "The index page is here >" link to feed gadget.
//*********************************
madoug.getIndexUrlFromFeedUrl = function(url) {
  var urls = /(^[^:]*:\/\/[^\/\?#]*)\/feeds\/(posts)\/(summary|default)(?:\/-\/([^#\?]*)?)?/.exec(url);
  if (urls == null || urls.length < 4) return null;
  var linkUrl = urls[1] + "/search", linkOpt = "";
  if (urls.length >= 5) {
    var labels = urls[4].split("/");
    if (labels.length > 0) linkUrl += "/label/" + labels[0];
    if (labels.length > 1) linkOpt += "&by-date=false&q=" + labels[1];
    for (var i = 2, imax = labels.length; i < imax; i++) linkOpt += "+" + labels[i];
  }
  if (window.location.search != null) {
    var isMobile = window.location.search.match(/[\?\&]m=(\d)(?:[\?\&]|$)/);
    if (isMobile != null && isMobile.length >= 2) linkOpt += "&m=" + isMobile[1];
  }
  if (linkOpt.length > 0) linkUrl += "?" + linkOpt.slice(1);
  return linkUrl;
}
madoug.insertIndexLink = function(id) {
  var widget = null, allData = null, data = null, linkUrl = null, linkText = null;
  if (id == null || _WidgetManager == null
      || (widget = document.getElementById(id)) == null
      || (allData = _WidgetManager._GetAllData()) == null
      || (data = allData.widgets[id]) == null
      || (linkUrl = madoug.getIndexUrlFromFeedUrl(data.feedUrl)) == null
      || (linkText = madoug.getTextSpanText("index_here")) == null) return;
  widget.insertAdjacentHTML("beforeend", "<div class=\"widget-footer\"><a href=\"" + linkUrl + "\">" + linkText + "</a></div>");
  widget.setAttribute("class", widget.getAttribute("class") + " linkAdded");
}
madoug.insertIndexLinkToAllFeedGadget = function() {
 var feedGadgets = document.querySelectorAll(".widget.Feed");
  if (feedGadgets == null || _WidgetManager == null) return;
  for (var i = 0, imax = feedGadgets.length; i < imax; i++) {
    madoug.insertIndexLink(feedGadgets[i].id);
  }
}

変更点1.関数名の変更

今までの関数の addIndexLink 関数に相当するのは、 insertIndexLink 関数です。言葉のニュアンス的にこっちの方が合うと思って変更しただけです。

変更点2.機能がわかる注釈

まず、最初に機能ごとの説明を日本語&英語で書くことにしました。

理由は「なんだか記号が並んでいて気持ちが悪い」と思われないようにするためです。どんなものなのかをわかりやすくすることで親近感を与え、不快感を和らげます。

変更点3.なるべく行数を短くする

スクリプト中に注釈を入れず、行数を省略する工夫を大量に使うようにしました。

これも、スクリプトに興味がない人に不快感を抱かせないようにするための工夫です。なるべくスクロールせずに読める行数で一つの機能を記述することで、邪魔だと思われにくくしています。少ない行数で、かつHTML編集画面を広げた時に画面端で折り返さない程度の文字数であることが重要です。

そのため、プログラマーが考えがちな「コードの美しさ」は最低限に落としています。プログラミングに興味がないユーザーに不快感を与えないコードを書くこともプログラマーの使命だと思えば、こういったテンプレート用の機能では、コードを小さくまとめることもまた「美しさ」の範疇に入ると思います。

スクリプトを外部ファイルとして読み込む形にするならこのような工夫は不要なのですが、現時点では外部ファイルの置き方をどうするか考え中です。

変更点4.関数の分割

今までの関数を二つに分け、新たに一つ関数を追加しました。一つは フィードURL からブログの一覧ページの URL を生成するコード。一つはフィードガジェットにリンクを差し込むコード。さらにもう一つは、新しく実装した機能。すべてのフィードガジェットに一括でリンクを差し込むコードです!

これにより、個々のフィードガジェットに機能を追加するのも、全てのフィードガジェットに機能を追加するのも思いのままです!

変更点5.id の指定だけでフィードURLを取得する

これを公式の機能といっていいのかはわかりませんが、Blogger のWebページには _WidgetManager というオブジェクトが存在します。そこから _GetAllData() で取得したオブジェクトの中には、ブログ全体のデータやガジェットのデータがなどが含まれていることがわかりました。

この機能の詳細についてはそのうち別途書かせてもらおうと思いますが、そこからフィードガジェットの フィードURL を取得することができることがわかったため、個々のガジェットにスクリプト追加する必要がなくなりました。これにより、『全てのフィードガジェットに一括処理』とかの変則的な機能も使用することができるようになりました。

変更点6.madoug オブジェクトと部品化

基本的にまどうぐ工房の独自機能は madoug オブジェクトに追加するというのは前回考えたことですね。

前回考えた通りに、必要なデータも関数も madoug というオブジェクト内に格納しています。これにより、外部ファイルで後からスクリプトを読み込んだときも、共通して madoug オブジェクトにアクセスすればよいので連携がとりやすくなります。

これは機能ごとの外部ファイル化が可能になるということでもあります。ベースとなるレベル1の機能はさすがに外部ファイル化すると誤作動を起こす可能性がありますが、レベル2以降は外部ファイルにして遅延読み込みしても動作するように作れるかな~、という期待があります。

変更点7.多言語対応と文字列リソース

フィードガジェットには「一覧はこちら >」という文字列を追加する仕組みがありますが、これを
他の好きなテキストに変更できるようにしたいという思いから、突発的に思いついて作った多言語対応機能の一部が上記のソースコードに使用されています。getTextSpanText という関数です。

Blogger ユーザーは日本人以外にもたくさんいますので、いろんな人にテンプレートを使ってほしければテキストを変更できるということはとても重要なものだと思います。

どちらかというと「機能」ではなく「外観」ですので、テンプレートデザイナーで言葉を変更できるようになっているといいのですが、残念ながら今のところテンプレートデザイナーでは文字列変数に対応していません。

そこで、文字列リソースというものを登場させようと画策しました。

文字列リソースというのを簡単に言えば、使用するであろう言葉をあらかじめ列挙したものです。プログラムで言葉を使うときにそこから適切な言葉を取得できます。

テンプレートを直接変更してでもテキストを変更したい人にとっては、一か所に集められたテキストの中で自分の気に入らないテキストを自分好みに変更するだけですので、ソースコードの内容を理解する必要がないというのがメリットです。

これを利用して文字列リソースを上書きすることで多言語に対応できるようにしようと考えてもいるのですが、今思えばテンプレートデザイナーのグループ名や説明等もできれば各言語で表示できるようにしてほしい気もします……そうすると、言語ごとにテンプレートを作った方がいいのかなぁ。

それは大変だけど、変数の説明文はそうしないと変わらないし……ううむ。

0 件のコメント :

コメントを投稿