ゴミ溜め@技術系日常系雑文

主にWeb技術やそのほかつまづいたこととか引っかかって調べたこととかをまとめてます。

はてなダイアリーから引っ越しました。)

備考録。ユーザーエージェントで処理を分ける。

備考録。
ユーザーエージェントからブラウザ、ブラウザバージョンを特定し、スマフォであれば$is_mobileをtrueにするPHPコード。

<?php
// ユーザーエージェントは下記が参考になる。
// http://www.openspc2.org/userAgent/

// ユーザーエージェントの取得。
$ua = $_SERVER['HTTP_USER_AGENT'];

$browser = 'Unknown';
$version = 0;
$is_mobile = false;

// ブラウザの判別。
if( preg_match("/(MSIE|Chrome|Firefox|Android|Safari|Opera)[\/ ]([0-9.]*)/", $ua, $match ) ) {
  $browser = $match[1];
  $version = explode( ".", $match[2] );
    // ブラウザバージョンはドット区切りで配列として格納。
    // (メジャーバージョンの数値の大小で比較し安くするため。)
}

// デバイスがモバイル端末であるか判別。(タブレットは除く。)
if( preg_match("/(iPhone|iPod|Windows Phone|Opera Mobi|Fennec|Android)/",$ua, $match ) ) {
  // Opera MobiはAndroid版Opera、FennecはAndroid版Firefox。
  // なお、UA文字列中の"Opera Mobi""Fennec"という文字列は"Android"より後に記述されているので、
  // 比較する順序に注意する。
  // ex.
  //   Opera/9.80 (Android 2.3.3; Linux; Opera Mobi/ADR-1111101157; U; ja) Presto/2.9.201 Version/11.50
  //   Mozilla/5.0 (Android; Linux armv7l; rv:9.0) Gecko/20111216 Firefox/9.0 Fennec/9.0
  
  // Androidであり、Mobileを含まない場合は除外。
  // 標準のAndroidブラウザの場合、UAにMobileという文字列を含むので、
  // これの有無によってタブレットかスマフォかを判断する。
  if( !( $match[1] == "Android" && !preg_match("/Mobile/",$ua) ) ) {
    $is_mobile = true;
  }
}
?>

HTMLで出力してみるには

<!DOCTYPE html>
<html><head><meta charset="UTF-8">
<title>ユーザーエージェントを判別する。</title></head><body>

<p>User Agent: <?=$ua?></p>
<p>あなたのブラウザは 
<?=$browser?>                                   <!-- ブラウザ名を表示 -->          
<?=implode(".",$version)?>                      <!-- ブラウザバージョンをドット区切りで表示 -->
( <?= $is_mobile ? "mobile" : "not mobile" ?> ) <!-- モバイルか否かを表示 -->
</p>

</body></html>

なんにせよ、ユーザーエージェントはバージョンが変わると書式が変わる可能性もある不確かなものなので、厳密な判別には使えない。
主にモバイル用とPC・タブレット用でCSSを変更するとか、とか。

JavascriptでCookieを2

前回に続いて、Cookieを簡単に扱えるようにしてみる。

/**
 * Cookieの情報を読み書きする。
 */
var Cookie = {
  
    /**
     * Cookieの値を参照する。
     * @param name string 参照したい情報のNAME。
     * @return 当該の値。
     */
    get: function( name ) {
      
        var match = ('; ' + document.cookie + ';').match('; ' + name + '=(.*?);');
        var value = match ? decodeURIComponent(match[1]) : null;
        
        return value;
    },
    
    /**
     * Cookieに値を記録する。
     * @param name   string 参照名。
     * @param value  string 値。
     * @param option hash   expires(賞味期限:datetime), domain(有効ドメイン:string), path(有効パス:string), secure(セキュア状態でないと参照できないか否か:bool) の各情報。
     * @return 成功時: 記録したクッキー文字列。 失敗時: false。
     */
    set: function(name, value, option) {

        if( typeof name != 'undefined' && name != '' && typeof value != 'undefined' && value != '' ) {
          var buffer = name + '=' + encodeURIComponent(value);
          if ( typeof(option) !== 'undefined' ) {
            if (typeof option.expires != 'undefined' && option.expires != '' ) buffer += '; expires=' + new Date(option.expires).toUTCString();
            if (typeof option.domain  != 'undefined' && option.domain  != '' ) buffer += '; domain='  + option.domain;
            if (typeof option.path    != 'undefined' && option.path    != '' ) buffer += '; path='    + option.path;
            if (typeof option.secure  == 'boolean'   && option.secure        ) buffer += '; secure';
          }
          buffer += ";";
          
          document.cookie = buffer;
          
          return buffer;
        }
        else { return false; }
    },
    
    /**
     * Cookieの値を削除する。
     * @param name string 削除する値の参照名。
     * @return nullだったら無事削除できてる。
     */
    remove: function(name) {
      
      //1970年1月1日00:00:00の日付データをセットする
      var expires = new Date();
      expires.setTime(0);
      
      //有効期限を過去にして書き込む
      document.cookie = name +"=;expires="+ expires.toGMTString();
    }
};

このあたり

//1970年1月1日00:00:00の日付データをセットする
var expires = new Date();
expires.setTime(0);

がちょっとどうなのってかんじだけど、わざわざ内部時計を1970年より前にしてるような巧妙な人はいないだろうからまあいいか。

必要にかられたら

var foo = Cookie.get("name");
console.log( foo );         // -> "hoge"

var options = ["expires","secure"];
var bar = Cookie.get("name", options );
console.log( bar.value );   // -> "hoge"
console.log( bar.expires ); // -> Date {Fri Jul 13 2012 11:40:07 GMT+0900}
console.log( bar.secure );  // -> true

みたいな使い方ができるように・・・・しないなあ、もっとちゃんとしたコード公開されてるから、それに乗り換えよう。

JavaScriptでCookieを

JavaScriptCookieを使ってみる。

参考:
[JavaScript + Cookie]実は有効期限指定がすごく簡単だった件について

<!DOCTYPE HTML>
<html lang="ja">
<head><meta charset="UTF-8">
<script type="text/javascript">
<!--
function printf( value ) {
  var out = document.getElementById("out");
  var str = out.innerHTML;
  
  out.innerHTML = str +"\n"+ value +"<br>\n";
}
var Cookie = {
    get: function(name) {
    
        printf("Loading...");
        
        var match = ('; ' + document.cookie + ';').match('; ' + name + '=(.*?);');
        
        printf( "<b>Load Cookie</b>:" + match ? decodeURIComponent(match[1]) : '' );
        
        return match ? decodeURIComponent(match[1]) : '';
        
    },
    set: function(name, value, option ) {
        // option : { expires, domain, path, secure }
        
        printf("Writing...");
        
        console.log( arguments );
        
        if( typeof name != 'undefined' && name != '' && typeof value != 'undefined' && value != '' ) {
          var buffer = name + '=' + encodeURIComponent(value);
          if (typeof option.expires != 'undefined' && option.expires != '' ) buffer += '; expires=' + new Date(option.expires).toUTCString();
          if (typeof option.domain  != 'undefined' && option.domain  != '' ) buffer += '; domain='  + option.domain;
          if (typeof option.path    != 'undefined' && option.path    != '' ) buffer += '; path='    + option.path;
          if (typeof option.secure  == 'boolean'   && option.secure        ) buffer += '; secure';
          
          document.cookie = buffer + ";";
          
          printf( "<b>Write Cookie</b>:" + buffer);
        }
        else {
          printf( "<b>ERROR:</b>nameあるいはvalueが空です。" );
        }
    }
};
//-->
</script>

</head>
<body>

<form action="" id="form" name="settings">
  <label>name:<input type="text" name="name" id="name" /></label>
  <label>value:<input type="text" name="value" id="value" /></label>
  <label>expires:<input type="text" name="expires" id="expires" /></label>
  <label>domain:<input type="text" name="domain" id="domain" /></label>
  <label>path:<input type="text" name="path" id="path" /></label>
  <label><input type="checkbox" name="secure" id="" class="secure" value="true" />secure</label>
</form><!-- /#form -->
<button onclick="
  Cookie.set(
    settings.name.value,
    settings.value.value,
    {
      expires : settings.expires.value,
      domain : settings.domain.value,
      path : settings.path.value,
      secure : settings.secure.checked
    }
  );">Write</button>

<button onclick="Cookie.get( settings.name.value );">Load</button>

<div id="out"></div><!-- /#console -->

</body>
</html>

実行結果

昨日の件結局妥協した。

昨日の件、妥協した。
やっぱり。heightLine.jsを使おう。ただ、やっぱりいちいちクラス名を指定するのがめんどくさめんどくさいのでheightLine.jsのnew function()内の末尾に下記のコードを追加した。

追記。
しまった。addClassの前にremoveClassしてないので実行のたびに同じクラス名を付与し続けるというとんでもないことしてた。修正。

  jQuery.fn.heightLine = function(groupName) {
    
    var className = "heightLine" + ( typeof(groupName) != "undefined" ? "-"+groupName: "" );
    
    $(this).removeClass(className);
    $(this).addClass(className);
    
    heightLine();
  };

jquery.flatheights.js同様に高さ指定する要素を指定できるようになったのでまあ良しとする。

使い方は

/**
 * @param target    string 高さを揃えたい対象
 * @param groupName string グループ化したい場合に、名前を指定する
 */
$( target ).heightLine( groupName);

例。

$("#side-navi,#main-content").heightLine();
  // -> 「#side-navi」と「#main-content」に「heightLine」というクラス名を付加した後、heightLineを実行する。

$(".section").heightLine("section");
  // -> 「.section」に対して、「heightLine-section」というクラス名を付加した後、heightLineを実行する。

複数個のグループごとに高さを揃えたい時には、groupNameに適当な名前を設定する。

jquery.flatheights.jsが上手く動かなかったので修正した。

jQueryでブロック要素の高さを揃えてみるの「jquery.flatheights.js」が素晴らしかったけど、paddingとかborderの値分高さがずれるので修正してみた。

元の内容の、79行目〜の

  /* 高さ揃えの処理本体 */
  var flatHeights = function(set) {
    var maxHeight = 0;
    set.each(function(){
      var height = this.offsetHeight;
      if (height > maxHeight) maxHeight = height;
    });
    set.css('height', maxHeight + 'px');
  };

の部分を

  /* 高さ揃えの処理本体 */
  var flatHeights = function(set) {
    var maxHeight = padding = border = 0;
    set.each(function(){
      var height  = $(this).outerHeight();

      if ( height  > maxHeight ) {
        maxHeight = height; // 最大の高さを当てなおす。
        
        // 過去の栄冠を剥奪。今、CSSクラスに__maxHeightが設定されている要素から、当クラス名を削除する。君が最も大きい存在だったのは過去のことだよ。
        set.removeClass('__maxHeight');
        
        // 君が新しいチャンプだ!該当の要素に__maxHeightクラス名を付加する。
        $( this ).addClass( "__maxHeight" );
      }
    });
    
    // 高さを設定。
    set.each(function() {
      if( !( $(this).hasClass('__maxHeight') ) ) {
        
        // 設定されたpaddingの合計を計算。
        padding =( ( fullstyle( this, 'padding-top'    ) ).replace( /px/, '' ) *1 )
                + ( ( fullstyle( this, 'padding-bottom' ) ).replace( /px/, '' ) *1 );
        
        // 設定されたborderの合計を計算。
        border  = ( ( fullstyle( this, 'border-top-width'    ) ).replace( /px/, '' ) *1 )
                + ( ( fullstyle( this, 'border-bottom-width' ) ).replace( /px/, '' ) *1 );
        
        // 最大の高さから、この要素のborder-widthとpaddingの値を引いた高さを設定する。
        $(this).css('height', maxHeight - ( padding + border ) + 'px');
      }
    });
    
    // 検査用に付加したCSSクラスを削除。
    set.removeClass('__maxHeight');

とした。

途中で使ってる「fullstyle」メソッドは、要素のスタイルを取得するためのもので、中身は下記な感じ。jquery.flatheights.js実行よりも前に記述しておく。

var fullstyle = (function(target, cssProperty){
  if( typeof( cssProperty ) != "undefined" ) {
    if(target.currentStyle){ return target.currentStyle[cssProperty]; }
    else { return ( document.defaultView.getComputedStyle(target, null) ).getPropertyValue(cssProperty); }
  }
  else {
    if(target.currentStyle){ return target.currentStyle; }
    else { return ( document.defaultView.getComputedStyle(target, null) ); }
  }
});

firefoxIE9で見た限り上手く動いてるけど、IE8だとoffsetHeightの返す値の違いのせいか、結局ずれる。〜IE7だと動かない。

まだまだ調整が必要かも。

/* 追記 */
やっぱりheightLine.jsが優秀。class名を付与しないといけないことさえ許容できれば、最良の選択。

関連テキストファイルが連番のフォルダごとにまとめられてるのをフォルダごとにファイル結合して別のフォルダに出力するバッチ

関連テキストファイルが連番のフォルダごとにまとめられてるのをフォルダごとにファイル結合して別のフォルダに出力するバッチ。

よくわかんないね。
たとえばこんなファイル群を

Dドライブ
 |
 +- <設定資料>
  |
  +- <設定資料01>
  | |
  | +- 設定1_A.txt
  | +- 設定1_B.txt
  | +- 設定1_C.txt
  |
  +- <設定資料02>
  | |
  | +- 設定2_A.txt
  | +- 設定2_B.txt
  | +- 設定2_C.txt
  |
  +- <設定資料03>
  | |
  | +- 設定3_A.txt
  | +- 設定3_B.txt
  | +- 設定3_C.txt
  :

こんな風に結合したいとき

Eドライブ
 |
 +-<設定資料まとめ>
  |
  +- 設定01.txt
  +- 設定02.txt
  +- 設定03.txt
  :

バッチで何とかしたい場合はこんな感じ。

@ECHO OFF

ECHO =======================================
ECHO 初期値の設定...
SET BASE_FILE_NAME=設定
SET TGT_DRIVE_SYMBOL=D
SET TGT_DIR=D:\設定資料
SET OUT_DIR=E:\設定資料まとめ
SET TGT_EXT=txt
SET OUT_EXT=txt

SET /a NUM=0
ECHO =======================================

:ROOP
ECHO =======================================
ECHO ループ処理...

ECHO ---------------------------------------
ECHO 変数設定...
SET /a NUM=%NUM%+1
IF %NUM% LSS 10 (SET NUM_P=0%NUM%) ELSE (SET NUM_P=%NUM%)
SET FILE_A=%BASE_FILE_NAME%%NUM%_A.%TGT_EXT%
SET FILE_B=%BASE_FILE_NAME%%NUM%_B.%TGT_EXT%
SET FILE_C=%BASE_FILE_NAME%%NUM%_C.%TGT_EXT%
SET FILE_OUT=%OUT_DIR%\%BASE_FILE_NAME%%NUM_P%.%OUT_EXT%

ECHO NUM   = [%NUM%]

ECHO ---------------------------------------
ECHO ディレクトリ移動...
@ECHO ON
CD /d %TGT_DRIVE_SYMBOL%:
CD %TGT_DIR%%NUM_P%
@ECHO OFF

ECHO ---------------------------------------
ECHO 結合実行...
COPY /B %FILE_A%+%FILE_B%+%FILE_C% %FILE_OUT%

ECHO =======================================
IF EXIST %FILE% (
ECHO ステップ終了, 次へ.
GOTO ROOP
) ELSE ECHO ファイル[%file%]がありません.処理を終了します.

ECHO 終了...

PAUSE

いくつか便利げなMTプラグインを。

MovableType使う際、いくつか便利げ、むしろ必須といえるMTプラグイン。

  • BodyFieldCustomizer
  • MultiFileUploader
  • RenameLabel
  • CustomFieldSearch
  • EntryImExporter
    • www.skyarc.co.jp/engineerblog/entry/entryimexporter.html
    • ブログ記事をCSV形式でイン/エクスポート可能にする。(サイドメニュー「ブログ記事」→「ブログ記事の一括管理」)(無償)
  • ScriptInjection
    • tec.toi-planning.net/mt/scriptinjection/
    • 記事編集画面で任意のJavascriptを動作させることができるようになる。(フィールドのバリデーションチェックとか、自動記入とかのコードをかけるようになる。)(無償)