1 var
  2 	WINDOW_APP = WINDOW_APP || {};
  3 
  4 WINDOW_APP.util = {};
  5 
  6 /**
  7  * location hashの変更を通知
  8  *
  9  * @class
 10  */
 11 WINDOW_APP.util.hashchange = (function () {
 12 	var
 13 		_DELAY = 50,	//監視のタイミング
 14 
 15 		_doc = document,
 16 
 17 		_isPlaying = false,	//動作中か
 18 		_listeners = [],	//リスナー
 19 		_numListener = 0,	//追加された要素の数
 20 		_timer,	//タイマー
 21 		_prevHash;	//前回のハッシュ
 22 
 23 
 24 	/**
 25 	 * イベントの登録
 26 	 *
 27 	 * @param	func {Function} リスナー
 28 	 */
 29 	function on (func) {
 30 		var
 31 			isAdded = false;	//すでに追加されているか
 32 
 33 		if(typeof func === "function") {
 34 			//同じものがあれば上書き
 35 			for(var i = 0; i < _numListener; i++) {
 36 				if(_listeners[i] === func) {
 37 					_listeners[i] = func;
 38 					isAdded = true;
 39 					break;
 40 				}
 41 			}
 42 
 43 			if(!isAdded) {
 44 				_listeners.push(func);
 45 				_numListener++;
 46 			}
 47 		}
 48 	}
 49 
 50 	/**
 51 	 * イベントの登録
 52 	 *
 53 	 * @param func {Function} リスナー
 54 	 */
 55 	function bind(func) {
 56 		on(func);
 57 	}
 58 
 59 	/**
 60 	 * イベントの削除
 61 	 *
 62 	 * @param	func {Function} リスナー
 63 	 * @return	{Boolean} 削除されたか
 64 	 */
 65 	function off (func) {
 66 		var result = false;
 67 		for(var i = 0; i < _numListener; i++) {
 68 			//一致したリスナーを削除
 69 			if(_listeners[i] === func) {
 70 				delete _listeners[i];
 71 				_numListener--;
 72 				result = true;
 73 				break;
 74 			}
 75 		}
 76 
 77 		return result;
 78 	}
 79 
 80 	/**
 81 	 * イベントの削除
 82 	 *
 83 	 * @param	func {Function} リスナー
 84 	 * @return	{Boolean} 削除されたか
 85 	 */
 86 	function unbind(func) {
 87 		return off(func);
 88 	}
 89 
 90 	/**
 91 	 * モニターの開始
 92 	 *
 93 	 */
 94 	function start () {
 95 		stop();
 96 
 97 		//開始時のハッシュを保持
 98 		_prevHash = _doc.location.hash;
 99 
100 		_monitor();
101 		_timer = setInterval(_monitor, _DELAY);
102 		_isPlaying = true;
103 	}
104 
105 	/**
106 	 * モニターの停止
107 	 *
108 	 */
109 	function stop () {
110 		if(_timer) {
111 			clearInterval(_timer);
112 		}
113 		_isPlaying = false;
114 	}
115 
116 	/**
117 	 * 通知
118 	 *
119 	 */
120 	function trigger() {
121 		_monitor(true);
122 	}
123 
124 	/**
125 	 * 監視&通知
126 	 *
127 	 * @param [isForce] {Boolean} 強制的に通知するか
128 	 */
129 	function _monitor(isForce) {
130 		var
131 			currentHash = _doc.location.hash,
132 			isChange = false;	//変更されたか
133 
134 		if(currentHash !== _prevHash) {
135 			isChange = true;
136 		}
137 
138 		//通知
139 		if(isChange || isForce) {
140 			for(var i = 0; i < _numListener; i++) {
141 				_listeners[i].apply(_listeners[i], [currentHash]);
142 			}
143 		}
144 
145 		_prevHash = currentHash;	//保持
146 	}
147 
148 	//public
149 	return {
150 		on: on,
151 		off: off,
152 		bind: bind,
153 		unbind: unbind,
154 		start: start,
155 		stop: stop,
156 		trigger: trigger
157 	};
158 }());