Mac版illustrator CS5 + システムの言語が英語の時に正常に起動しない件

Snow Leopardを英語で使っていて、Mac版のillustrator CS5の日本語版を起動しようとすると
「プラグインの読み込みエラー」というのが発生して、メニューの日本語などが文字化けしてしまいます。
さらに、終了しようとしても正常終了してくれないという状態に会いました。



Adobeのサポートにも掲載されている様です。
http://kb2.adobe.com/jp/cps/858/cpsid_85840.html
素直に日本語使えゴルァってことですね...


Snow Leopard以前はアプリケーションごとに使用する言語を選べたみたいなのですが
現在はできないので、なんとかできないものかと思って見つけたのがLanguage Switcher


インストール後にアプリケーション一覧が表示されますが、illustratorの様にフォルダ内に入っている.appは初期表示されないみたいなので、「File -> Add」からillustrator.appを追加します。

右下の「Language」から「Japanese」を選択。同時に「Always open in this language」を選択しておけばillustratorだけ常に日本語で起動できる様になります。

JavaScriptパターン #3

リテラルコンストラクタ

オブジェクトの定義
// オブジェクトリテラル記法で定義
var dog = {
  name: 'Benji',
  getName: function() {
    return this.name;
  }
};

// 組み込みのコンストラクタで定義
var dog = new Object();
dog.name = 'Benji';
dog.getName = function() {
  return this.name;
};


リテラルで定義する利点

  • オブジェクトは単純で変更可能なハッシュなのでクラスから生成する必要が無い。
  • スコープの解決がいらない。コンストラクタを使うと、Object()が呼ばれた場所からグローバルなObjectコンストラクタを見つける必要がある。

→オブジェクトリテラルが使えるのであれば、new Object()を使う理由は特にない。

独自のコンストラクタ関数

var Person = function(name) {
  this.name = name;
  this.say = function() {
    return 'I am ' + name;
  };
};

var me = new Person('daigo3');
me.say();    // "I am daigo3"

new Person()の様に呼び出すと、関数内部で空のオブジェクトをが作成されてthisで参照できる様になる。コンストラクタは暗黙的にthisを返す(別のオブジェクトを返していない場合)。
※実際にはプロトタイプを継承しているので「空のオブジェクト」ではない


上の例ではthis.sayの様にメソッドを追加しているが、newする度に同じメソッドが生成されてしまうので、プロトタイプに追加する方が良い。→メソッドの様な再利用可能なメンバはプロトタイプで定義する。

Person.prototype.say = function() {
  return 'I am ' + this.name;
};
newを忘れた場合

コンストラクタ関数内のthisがグローバルオブジェクトを参照してしまう。

var Person = function(name) {
  this.name = name;
};

Person.prototype.say = function() {
  return 'I am' + this.name;
};

var p = Person('daigo3');

console.log(p.name);    // TypeError: p is undefined
console.log(window.name);
console.log(name);

ただしECMAScript5では、strictモードの場合はthisはグローバルオブジェクトを指さなくなる。

配列

リテラルを使う方が簡潔。

// リテラル
var arr1 = [1, 2, 3];
// 組み込みコンストラクタ
var arr2 = new Array(1, 2, 3);

console.log(arr1, arr2);    //[1, 2, 3] [1, 2, 3]
console.log(arr1.constructor === Array);    // true
console.log(arr2.constructor === Array);    // true

// コンストラクタに引数を1つだけ渡した場合は
// その長さを持つ空の配列になる
var arr = new Array(3);
console.log(arr.length);    // 3
console.log(arr[0]);        // undefined

// 浮動小数点を長さにしてしてみる
var arr = new Array(3.14);    // RangeError: invalid array length
配列かどうかのチェック

配列はオブジェクトなのでtypeof演算子を使うと"object"が返ってきてしまう。

var arr = [1, 2 ,3];
console.log(typeof arr === 'object');    // true
var arr = [1, 2, 3];

// instanceofを使う
// IEのバージョンによっては、フレームをまたがって使うと正しくチェックできない
console.log(arr instanceof Array);

// ECMAScript5のArray.isArray()を使う
// console.log(Array.isArray(arr));

// Object.prototype.toString()を使う
// isArrayが使えない場合
if(typeof Array.isArray === 'undefined') {
  Array.isArray = function(arg) {
    return Object.prototype.toString.call(arg) === '[object Array]';
  }
}

console.log(Array.isArray(arr));

JSON

JSONとオブジェクトリテラルの違い

// JSONデータを評価する
var jstr = '{ "key": "value"}';
var data = JSON.parse(jstr);
console.log(data.key);     //"value"

// JSONデータにシリアライズする
var dog = {
  name: 'Maru',
  legs: 4
}

var jsonstr = JSON.stringify(dog);    //{"name":"Maru","legs":4}

正規表現リテラル

var str = 'abc123XYZ';

// リテラル
var no_letters = str.replace(/[a-z]/gi, '');
console.log(no_letters);    // 123

// コンストラクタ
var letters = str.replace(new RegExp('[0-9]', 'gi'), '');
console.log(letters);    //abcXYZ


リテラルの方が簡潔に書けるが、動的に正規表現を作成する場合にはコンストラクタを使った方が良い。

エラーオブジェクト

name,messageの様なプロパティの他にも、エラーが発生した行番号などを示すプロパティがあるがブラウザ拡張だったりするので、一貫性が無い。
throwでは任意のオブジェクトを指定できるのでカスタムのエラーオブジェクトを作る事ができる。

try {
  // ...
  throw {
    name: 'MyErrorType',
    message: 'oops',
    extra: 'extra message',
    remedy: errorHandler  //  エラーを処理するハンドラ
  }
} catch (e) {
  alert(e.message);  // oops
  e.remedy();           // エラーを処理する(errorHandler)
}

JavaScriptパターン #2

必須パターン

高品質なJavaScriptコードを書くための必須パターンとコーディングの習慣。

なぜ?

バグ→修正するのにコストがかかる→時間が経つほどコストは高くなる

修正すべき問題が新しければ、問題を把握しやすいので見つけたら即修正がベスト。

コードを書くよりも読む方が時間がかかる。

コードの変更(新しい機能の追加、バグの修正、レビュー)で、コードを書いた当初よりも
変化したコードを読むのにはより時間がかかる。

保守しやすいコードを書く事が重要
  • 読みやすい
  • 一貫性がある
  • 見通しが良い
  • 一人で書いた様なコードに見える
  • ドキュメントが整備されている

APIのドキュメントを書く

  • YUIDoc
  • JSDoc Toolkit

もう少しシンプルで、個人用にも使えるようなものが良いなと思ったのでdoxを使ってみることにしました。

Doxのインストール

npmから

% npm install dox

git clone

% git clone https://github.com/visionmedia/dox.git
% cd dox
% make install
JavaScriptのコメントにタグを記入する
/**
 * Sample アプリケーションファイル
 *
 * @author daigo3
 * @version 0.1
 */
var MYAPP = {};

/**
 * ユーティリティ
 * @namespace MYAPP
 * @class util
 */
MYAPP.util = {
  hello: function(name) {
    return 'hello,' + name;
  }
};

/**
 * Personオブジェクト
 * @class Person
 * @constructor
 * @namespace MYAPP
 * @param {String} first ファーストネーム
 * @param {String} last ラストネーム
 */
MYAPP.Person = function(first, last) {
  this.first_name = first;
  this.last_name = last;
};

/**
 * フルネームを返す
 * @memberOf Person
 * @returns {String} 名前
 */
MYAPP.Person.prototype = {
  getName: function() {
    return this.first_name + ' ' + this.last_name;
  }
};

@param、@returnの様なタグはJSDoc Toolkitのタグに対応している-http://code.google.com/p/jsdoc-toolkit/w/list

APIドキュメントの生成
% dox --title MyApp app.js > myapp.html
... parsing 1 file(s)
... loading default style
... parsing app.js


こんな感じのmyapp.htmlが生成される

JavaScriptパターン #1

JavaScriptパターン ―優れたアプリケーションのための作法

JavaScriptパターン ―優れたアプリケーションのための作法

JavaScriptでできること

  • クライアントサイド(いわゆるJavaScript)
  • サーバサイド(.NETやNode.js)
  • デスクトップアプリケーション
  • アプリケーションの拡張(ブラウザ、Photoshop...)
  • モバイルアプリケーション
  • コマンドラインスクリプト

パターン

パターン=典型的な問題に対する解決方法(ベストプラクティス)

JavaScriptの重要な概念

オブジェクト指向

数値、文字列、ブール値、null、undefined以外(プリミティブ型以外)は全てオブジェクト。

変数、関数 -> オブジェクト -> キーと値の組(プロパティ)のリスト

オブジェクトの種類は2つ

  • ネイティブ(組み込みオブジェクト(Array、Date...)やユーザが定義するオブジェクト(var o = {})
  • ホスト(windowやDOMオブジェクト)
クラスは無い

必要になったときに空のオブジェクトを作成しておいて、後からメンバを追加する。(「クラス継承よりオブジェクトのコンポジションが好ましい」)

プロトタイプ

継承はプロトタイプ(全ての関数が持つプロパティ)を使う。

> function fn() {}
> fn.prototype
fn
// プロトタイプオブジェクトのconstructorプロパティは関数を指す
> fn.prototype.constructor
function fn() {}

// オブジェクトリテラルで生成したオブジェクトのconstructorプロパティは
// 組み込みのObject()
> var o = {}
> o.constructor
function Object() { [native code] }

ECMAScript 5

DOMなどの余分なホストオブジェクトを含まないコアJavaScript言語の土台。

ECMAScript5の重要な追加機能 : strictモード
function fn() {
  "use strict";
  // ...
}

言語から機能を取り除き、プログラムをよりシンプルにする。(例えば、with文はstrcitモード内では使えない)
strictモードは文字列で起動するので、対応していないブラウザであれば無視される。

JSLint

コードの品質チェックツール
http://www.jslint.com/

コンソール

Firefox = Firebug
Webkit系 = Web Inspector
IE 8~ = Developer Tools

2011 抱負

なにやら、忙しくしていたらブログの更新が滞ってる...

やっとJavaScriptを勉強し始めてから1年と2ヶ月が経ちました。
現在、働かせていただいている会社では、フルJSな案件をデザインから実装まで担当させていただいてます。
今まで、ちょろちょろJSで作ったりはしていましたが本格的に実践な環境になりました。

2011

  1. 企画・提案 -> UI -> フロントエンド開発をやらせていただける環境になりそう
  2. まだ、自分が思い描いているものを作ることに不自由する程度のスキル
  3. 継続してJavaScriptやる!
  4. この辺(Design Driven Development)を実践
  5. design + development = :-)

としていきたいです。


24~5歳辺りからプログラミングにはまったという、かなり遅いタイプの割に運が良いのでしょうが
やりたい事をやらせてもらいつつ色々な課題が見えてきているので、良い環境に持って行けたなぁと思っています。


あと、最近まともに読書をしていなかったので本読もうということで。

女王国の城 (創元クライム・クラブ)

女王国の城 (創元クライム・クラブ)

Console API

普段、console.log()ばかり使ってたのですが
調べてみるといろいろあったのでメモ。

Chromeで試しています。

> console.log('this is %s', 'log');
> this is log

> console.info('this is %s', 'information');
> this is information

> console.debug('this is %s', 'debug string');
> this is debug string

> console.warn('this is %s', 'Warning');
(!) this is Warning

> console.error('this is %s', 'Error');
(x) this is Error

> console.assert(true, 'Yes!');
> undefined

> console.assert(false, 'No!');
(x) Assertion failed: No!

// グループでまとめたログは折り畳める様になる
> console.group('group %d', 1); console.log('in group1'); console.groupEnd();
> group 1
    in group1

// console.time() ~ console.timeEnd()間の処理時間
> console.time('timer');
> (function() { for(var i = 0; i < 1000000000; i++) { var j=0; j++} })();
> console.timeEnd('timer');
timer: 5866ms

// 呼ばれた回数を出力。引数で識別できる
> console.count('a');
> a: 1
> console.count('a');
> a: 2
> console.count('b');
> b: 1
> console.count('a');
> a: 3

// 引数に合ったInspectorのタブが開く
> inspect(document.getElementById('ghead'))
> <div id="ghead">…</div>
> inspect(localStorage)
> Storage
// console.profile()~console.profileEnd()の間にある関数をプロファイル出来るようになる。
<script>
  console.profile();
    function test1() {
      var j = 0;
      for(var i = 0; i < 10000000; i++) {
        j++;
      }
      console.log(j);
    }

    function test2() {
      var i = 0;
      while(i < 10000000) {
        i++;
      }
      console.log(i);
    }
    test1();
    test2();
  console.profileEnd();
</script>

Touch/Gestureイベントの勘所

iPhone/iPad向けのWebアプリでタッチでの操作を扱う際のメモ。

ユーザによるズームは無効にしておいた方が良さそう。

<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />

【追記】
contentの区切りは","が正です

Touch

Touchイベントは

  • touchstart :スクリーンに指が触れた
  • touchmove :スクリーン上で指が動いてる最中
  • touchend :指がスクリーンから離れた
  • touchcancel :システムがタッチイベントをキャンセルした場合?

の4種類です。


注意しないといけないのは、例えばタッチした場所の座標を表示しようとしたとき

target.addEventListener('touchstart', touchStart, false);

function touchStart(ev) {
  var x = ev.screenX;
  var y = ev.screenY;
}

の様にやってしまいがちですが、これは動きません(x/yはundefindになる)。


Touchイベントは、タッチを検知するための

  • touches : Touchオブジェクトの配列
  • targetTouches : イベント発生元のTouchオブジェクトの配列
  • changedTouches : 最後のTouchイベントから変更が起こっているTouchオブジェクトの配列

というプロパティを持っているので、こいつらに入っているTouchオブジェクトのプロパティを参照する必要があります。

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=no;" />
  <title>test</title>
  <style>
  div {
    display: inline-block;
    margin: 0 10px;
  }
  div#target {
    display: block;
    width: 200px;
    height: 200px;
    background-color: blue;
  }
  </style>
</head>
<body>
<h1>Gesture test.</h1>
<div id="start">
  <p>X:<span class="x"></span></p>
  <p>Y:<span class="y"></span></p>
</div>
<div id="move">
  <p>X:<span class="x"></span></p>
  <p>Y:<span class="y"></span></p>
</div>
<div id="end">
  <p>X:<span class="x"></span></p>
  <p>Y:<span class="y"></span></p>
</div>
<div id="target"></div>

<script>
  var startx  = document.querySelector('div#start span.x'),
      starty  = document.querySelector('div#start span.y'),
      movex   = document.querySelector('div#move span.x'),
      movey   = document.querySelector('div#move span.y'),
      endx    = document.querySelector('div#end span.x'),
      endy    = document.querySelector('div#end span.y'),
      target  = document.getElementById('target');


target.addEventListener('touchstart', touchStart, false);
target.addEventListener('touchmove', touchMove, false);
target.addEventListener('touchend', touchEnd, false);

function touchStart(ev) {
  var touch = ev.touches[0];
  startx.innerHTML = touch.screenX;
  starty.innerHTML = touch.screenY;
}

function touchMove(ev) {
  ev.preventDefault();
  var touch = ev.changedTouches[0];
  movex.innerHTML = touch.screenX;
  movey.innerHTML = touch.screenY;
}

function touchEnd(ev) {
  var touch = ev.changedTouches[0];
  endx.innerHTML = touch.screenX;
  endy.innerHTML = touch.screenY;
}
</script>
</body>
</html>

ターゲットの要素の上でスクロールしたい時にページ自体がスクロールしてしまうので
touchMoveでpreventDefault()する様にします。


そもそも、ページ全体でスクロールさせたくない場合には

document.addEventListener('touchmove', function(ev) {
  ev.preventDefault();
}, false);

の様にします。


さらに、touchendイベントが起こっている時点で、touchesには何も無い状態なので、changedTouchesのTouchオブジェクトを使います。

Gestures

Gestureイベントはユーザが2本以上の指を使ってスクリーンに触れたときに発生します。

Touch Gesture Reference Guide
http://www.lukew.com/ff/entry.asp?1071


Touch/Gestureイベントは以下の様な流れで発生します。

  1. スクリーンにタッチ -> touchstart
  2. 他の指もスクリーンにタッチ(都合2本指でタッチ) -> gesturestart -> touchstart
  3. 指が動く -> gesturestart
  4. どちらかの指がスクリーンから離れる -> gestureend -> touchend
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=no;" />
  <title>test</title>
  <style>
  div#log {
    border: 1px solid #333;
    width: 400px;
    height: 500px;
    margin-left: 50px;
    overflow: scroll;
  }
  div#target {
    display: inline-block;
    float: left;
    width: 200px;
    height: 200px;
    background-color: blue;
  }
  </style>
</head>
<body>
<h1>Gesture test.</h1>
<div id="target"></div>
<div id="log"></div>

<script>
  var log = document.getElementById('log'),
      target = document.getElementById('target');


  target.addEventListener('touchstart', touchStart, false);
  target.addEventListener('touchmove', touchMove, false);
  target.addEventListener('touchend', touchEnd, false);
  target.addEventListener('gesturestart', gestureStart, false);
  target.addEventListener('gesturechange', gestureChange, false);
  target.addEventListener('gestureend', gestureEnd, false);

  function logger(msg) {
    var p = document.createElement('p');
    var t = document.createTextNode(msg);
    p.appendChild(t);
    log.appendChild(p);
  }

  function touchStart(ev) {
    logger('touchstart');
  }

  function touchMove(ev) {
    ev.preventDefault();
    logger('touchMove');
  }

  function touchEnd(ev) {
    logger('touchEnd');
  }

  function gestureStart(ev) {
    logger('gestureStart');
  }

  function gestureChange(ev) {
    logger('gestureChange');
  }

  function gestureEnd(ev) {
    logger('gestureEnd');
  }

</script>
</body>
</html>


Gestureイベントが持っているプロパティでTouchイベントには無いものがevent.rotationとevent.scaleです。
要素を操作したい場合などにTouchイベントで2本の指をトラッキングするよりも簡単に扱う事ができます。


要素のドラッグ&ドロップ + 拡大/縮小

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=no;" />
  <title>test</title>
  <style>
  div#target {
    position: absolute;
    height: 200px;
    width: 200px;
    background-color: blue;
  }
  </style>
</head>
<body>
<h1>Gesture test.</h1>
<div id="target"></div>

<script>
var target = document.getElementById('target'),
    styles = document.defaultView.getComputedStyle(target, ''),
    posX, posY, startX, startY,
    width = stripPx(styles.width),
    height = stripPx(styles.height),
    rotation = 0,
    dragging = false;

function touchStart(ev) {
  if(ev.touches.length == 1) {
    var touch = ev.touches[0],
        styles = touch.style;

    startX = touch.pageX;
    startY = touch.pageY;
    posX = stripPx(target.style.left);
    posY = stripPx(target.style.top);
    dragging = true;
  }
}

function touchMove(ev) {
  if(ev.touches.length == 1) {
    if(dragging) {
      var touch = ev.touches[0],
          styles = target.style;

      styles.left = posX + (touch.pageX - startX) + 'px';
      styles.top = posY + (touch.pageY - startY) + 'px';
    }
  }
}

function touchEnd(ev) {
  dragging = false;
}

function gestureChange(ev) {
  target.style.width = (width * ev.scale) + 'px';
  target.style.height = (height * ev.scale) + 'px';
  target.style.webkitTransform =
    'rotate(' + ((rotation + ev.rotation) % 360) + 'deg)';
}

function gestureEnd(ev) {
  width *= ev.scale;
  height *= ev.scale;
  rotation = (rotation + ev.rotation) % 360;
}

function stripPx(v) {
  if(v == '') { return 0 };
  return parseFloat(v.substring(0, v.length - 2));
}

target.addEventListener('touchstart', touchStart, false);
target.addEventListener('touchmove', touchMove, false);
target.addEventListener('touchend', touchEnd, false);
target.addEventListener('gesturechange', gestureChange, false);
target.addEventListener('gestureend', gestureEnd, false);

document.addEventListener('touchmove', function(ev) {
  ev.preventDefault();
}, false);

</script>
</body>
</html>

【追記】
拡大、縮小は

target.style.webkitTransform = 'scale(' + ev.scale + ')';

の方が楽ですね。