1 // analyse.js
  2 // date: /^(\d{4}|\d{2})\/(\d{2})\/(\d{2})/
  3 // time: /(\d{2}):(\d{2}):(\d{2}).(\d{2})$/
  4 //       /(\d{2}):(\d{2}):(\d{2})$/
  5 //       /(\d{2}):(\d{2})$/
  6 
  7 /**
  8  * @fileOverview [スレッド情報...]で表示される画面を扱います。
  9  */
 10  
 11 /**
 12  * スレッドから情報を解析してまとめて表示します。
 13  * @static
 14  */
 15 var Analyse = {
 16 	/** [スレッド情報] ダイアログを表示します。 */
 17 	analyse: function() {
 18 		dialogAnalyse.clear();
 19 		// -------------
 20 		dialogAnalyse.addPage ("Thread", "スレッド", SKIN_PATH + "img/thread.png");
 21 		dialogAnalyse.addFrame("Thread", "Summary", "概要");
 22 		dialogAnalyse.addLabel("Thread", "Summary", "", "タイトル:");
 23 		dialogAnalyse.addLabel("Thread", "Summary", "", ThreadDocument.title,null,1);
 24 		dialogAnalyse.addLabel("Thread", "Summary", "", "URL:");
 25 		dialogAnalyse.addLink ("Thread", "Summary", "", EXACT_URL, EXACT_URL,null,1);
 26 		dialogAnalyse.addLabel("Thread", "Summary", "", "総レス数:");
 27 		dialogAnalyse.addLabel("Thread", "Summary", "", ThreadDocument.countAll,null,1);
 28 		dialogAnalyse.addLabel("Thread", "Summary", "", "期間:");
 29 		var lastIndex = (ThreadDocument.countAll > 1000) ? 1000 : ThreadDocument.countAll;
 30 		dialogAnalyse.addLabel("Thread", "Summary", "", this.getDate(1).toLocaleString() + " ~ " + this.getDate(lastIndex).toLocaleString(),null,1);
 31 		dialogAnalyse.addLabel("Thread", "Summary", "", "(" + this.toStringfromTimeDiff((this.getDate(lastIndex).getTime() - this.getDate(1).getTime())) + ")",null,1);
 32 		dialogAnalyse.addLabel("Thread", "Summary", "", "勢い:");
 33 		dialogAnalyse.addLabel("Thread", "Summary", "", this.getPaceString(),null,1);
 34 		// -------------
 35 		dialogAnalyse.addPage ("User", "中の人", SKIN_PATH + "img/user.png");
 36 		dialogAnalyse.addFrame("User", "Name", "名前",96);
 37 		var nameArray = [];
 38 		var nameTable = [];
 39 		var arrayIndex = 0;
 40 		var nameItems = ResNodes.getNames();
 41 		for (var i = 0; i < nameItems.length; i++) {
 42 			var node = nameItems.items(i);
 43 			var name = node.textContent;
 44 			if (nameTable["_" + name]) {
 45 				nameArray[nameTable["_" + name]].count++;
 46 			} else {
 47 				nameTable["_" + name] = arrayIndex;
 48 				nameArray[arrayIndex] = {name: name, count: 1};
 49 				arrayIndex++;
 50 			}
 51 		}
 52 		nameArray.sort(function(a,b){return b.count - a.count});
 53 		for (var i = 0; i < nameArray.length; i++) {
 54 			if (nameArray[i].count > 1) dialogAnalyse.addLabel("User", "Name", "", nameArray[i].name + "(" + nameArray[i].count + ")");
 55 		}
 56 		// ---
 57 		dialogAnalyse.addFrame("User", "ID", "ID",96);
 58 		var idArray = [];
 59 		for (var id in ID.items) {
 60 			idArray.push({id: id, count: ID.items[id].length});
 61 		}
 62 		idArray.sort(function(a,b){return b.count - a.count});
 63 		dialogAnalyse.addLabel("User", "ID", "", "合計 " + idArray.length + " 個のID");
 64 		for (var i = 0; i < idArray.length; i++) {
 65 			if (idArray[i].count > 1) dialogAnalyse.addID("User", "ID", "", idArray[i].id, idArray[i].count);
 66 		}
 67 		// ---
 68 		dialogAnalyse.addFrame("User", "Trackback", "人気レス",96);
 69 		var trackbackArray = [];
 70 		Trackback.traverse();
 71 		for (var index in Trackback.items) {
 72 			trackbackArray.push({index: index, count: Trackback.items[index].length});
 73 		}
 74 		trackbackArray.sort(function(a,b){return b.count - a.count});
 75 		var hasbeenAdded = false;
 76 		for (var i = 0; i < trackbackArray.length; i++) {
 77 			if (trackbackArray[i].count > 1) {
 78 				dialogAnalyse.addTrackback("User", "Trackback", "", trackbackArray[i].index, trackbackArray[i].count);
 79 				hasbeenAdded = true;
 80 			}
 81 		}
 82 		if (!hasbeenAdded) dialogAnalyse.addLabel("User", "Trackback", "", "なし");
 83 		// -------------
 84 		dialogAnalyse.addPage ("Link", "リンク", SKIN_PATH + "img/external.png");
 85 		dialogAnalyse.addFrame("Link", "2ch", "2ch", 128);
 86 		dialogAnalyse.addFrame("Link", "External", "外部サイト",128);
 87 		dialogAnalyse.addPage ("Image", "画像", SKIN_PATH + "img/image.png");
 88 		dialogAnalyse.addFrame("Image", "Image", "一覧",256);
 89 		dialogAnalyse.addPage ("Video", "動画", SKIN_PATH + "img/video.png");
 90 		dialogAnalyse.addFrame("Video", "Video", "一覧",256);
 91 		var externalLinks = ResNodes.getOutLinks();
 92 		var urlTable = [];
 93 		var imageCount = 0;
 94 		var videoCount = 0;
 95 		var extCount = 0;
 96 		var chCount = 0;
 97 		for (var i = 0; i < externalLinks.length; i++) {
 98 			var node = externalLinks.items(i);
 99 			var href = node.rel;
100 			if (urlTable[href]) { continue } else { urlTable[href] = true };
101 			var container = ResNodes.getParentContainer(node);
102 			var index = ResNodes.getIndexByContainer(container);
103 			if (ImagePopup.isImage(href)) {
104 				imageCount++;
105 				dialogAnalyse.addLink ("Image", "Image", "", href, node.href, index);
106 			} else if (VideoPopup.getVideoSource(href)) {
107 				videoCount++;
108 				dialogAnalyse.addLink ("Video", "Video", "", href, node.href, index);
109 			} else {
110 				if (!href.match(/\.2ch\.net\/|\.bbspink\.com\/|\.machi\.to\/|jbbs\.livedoor\.jp\//)) {
111 					extCount++;
112 					dialogAnalyse.addLink ("Link", "External", "", href, node.href, index);
113 				} else {
114 					chCount++;
115 					dialogAnalyse.addLink ("Link", "2ch", "", href, node.href, index);
116 				}
117 			}
118 		}
119 		if (!imageCount) dialogAnalyse.addLabel ("Image", "Image", "", "なし");
120 		if (!videoCount) dialogAnalyse.addLabel ("Video", "Video", "", "なし");
121 		if (!extCount)   dialogAnalyse.addLabel ("Link", "External", "", "なし");
122 		if (!chCount)    dialogAnalyse.addLabel ("Link", "2ch", "", "なし");
123 		// -------------
124 		dialogAnalyse.show();
125 	},
126 	/** 指定されたレス番号の日付を、日付型に変換して返します。
127 	 * @param  {Number} index レス番号
128 	 * @return {Date} 日付
129 	 */
130 	getDate: function(index) {
131 		var str = Nodes.getDate(index).textContent;
132 		str.match(/^(\d{4}|\d{2})\/(\d{2})\/(\d{2})/);
133 		var fullYear  = parseInt(RegExp.$1);
134 		if (fullYear < 100) fullYear += (fullYear >= 90) ? 1900 : 2000;
135 		var date = new Date(RegExp.$2 + "/" + RegExp.$3 + "/" + fullYear);
136 		if (str.match(/(\d{2}):(\d{2}):(\d{2}).(\d{2})(\s|$)/)) {
137 			date.setHours(parseInt(RegExp.$1));
138 			date.setMinutes(parseInt(RegExp.$2));
139 			date.setSeconds(parseInt(RegExp.$3));
140 			date.setMilliseconds(parseInt(RegExp.$4));
141 		} else if (str.match(/(\d{2}):(\d{2}):(\d{2})(\s|$)/)) {
142 			date.setHours(parseInt(RegExp.$1));
143 			date.setMinutes(parseInt(RegExp.$2));
144 			date.setSeconds(parseInt(RegExp.$3));
145 		} else if (str.match(/(\d{2}):(\d{2})(\s|$)/)) {
146 			date.setHours(parseInt(RegExp.$1));
147 			date.setMinutes(parseInt(RegExp.$2));
148 		}
149 		return date;
150 	},
151 	/** ミリ秒を日本語の時間表記に変換します。
152 	 * @param  {Number} arg 時間(ミリ秒)
153 	 * @return {String} 変換された文字列
154 	 */
155 	toStringfromTimeDiff: function(arg){
156 		var strReturn = new String();
157 		var valDate = Math.floor(arg / 31536000000);
158 		if (valDate > 0) strReturn += valDate + "年";
159 		var valDate = Math.floor((arg % 31536000000) / 2628000000);
160 		if (valDate > 0) strReturn += valDate + "ヶ月";
161 		var valDate = Math.floor(((arg % 31536000000) % 2628000000) / 86400000);
162 		if (valDate > 0) strReturn += valDate + "日 ";
163 		var valDate = Math.floor((((arg % 31536000000) % 2628000000) % 86400000) / 3600000) ;
164 		if (valDate > 0) strReturn += valDate + "時間";
165 		var valDate = Math.floor(((((arg % 31536000000) % 2628000000) % 86400000) % 3600000) / 60000);
166 		if (valDate > 0) strReturn += valDate + "分";
167 		if (strReturn.length == 0) {
168 			var valDate = Math.floor((((((arg % 31536000000) % 2628000000) % 86400000) % 3600000) % 60000) / 1000 * 1000) / 1000;
169 		} else {
170 			var valDate = Math.floor((((((arg % 31536000000) % 2628000000) % 86400000) % 3600000) % 60000) / 1000);
171 		}
172 		if (valDate > 0) strReturn += valDate + "秒";
173 		return strReturn;
174 	},
175 	/** スレッドの勢いを取得します。
176 	 * @return {String} 勢い
177 	 */
178 	getPaceString: function(){
179 		var lastIndex = (ThreadDocument.countAll > 1000) ? 1000 : ThreadDocument.countAll;
180 		return this.toStringfromTimeDiff((this.getDate(lastIndex).getTime() - this.getDate(1).getTime()) / lastIndex) + " / ";
181 	}
182 };
183 
184 /**
185  * [スレッド情報] ダイアログを管理します。
186  * @static
187  */
188 var dialogAnalyse = {
189 	/** ダイアログの親要素
190 	 * @type element
191 	 */
192 	content: document.createElement("div"),
193 	/** [OK] ボタンが押されたときの処理をします。*/
194 	onOK: function(){
195 		this.hide();
196 	},
197 	/** リサイズされたときの処理をします。*/
198 	onResize: function(){
199 		dialogAnalyse.content.style.left = (document.body.offsetWidth - dialogAnalyse.content.offsetWidth) / 2
200 		dialogAnalyse.content.style.top  = (window.innerHeight - dialogAnalyse.content.offsetHeight) / 2
201 		var bg = document.getElementById("dialogAnalyseBackground");
202 		if (bg) {
203 			bg.style.width = document.body.offsetWidth;
204 			bg.style.height = window.innerHeight;
205 		}
206 	},
207 	/** ダイアログを表示します。*/
208 	show: function(){
209 		var bg = document.createElement("div");
210 		bg.id = "dialogAnalyseBackground";
211 		bg.style.width = document.body.offsetWidth;
212 		bg.style.height = window.innerHeight;
213 		document.body.appendChild(bg);
214 
215 		this.content.style.position   = "fixed";
216 		this.content.style.width      = 512;
217 		this.content.style.visibility = "hidden"
218 		this.content.style.display    = "block";
219 		this.selectPage(this.content.childNodes[1].id.replace("Page_", ""));
220 		this.content.style.left = (document.body.offsetWidth - this.content.offsetWidth) / 2
221 		this.content.style.top  = (window.innerHeight - this.content.offsetHeight) / 2
222 		this.content.style.visibility = "visible";
223 		window.addEventListener("resize", dialogAnalyse.onResize, false);
224 	},
225 	/** ダイアログを非表示にします。*/
226 	hide: function(){
227 		this.content.style.display = "none";
228 		var bg = document.getElementById("dialogAnalyseBackground");
229 		if (bg) document.body.removeChild(bg);
230 		window.removeEventListener("resize", dialogAnalyse.onResize, false);
231 	},
232 	/** ダイアログの内容をクリアします。*/
233 	clear: function(){
234 		if (document.getElementById("dialogAnalyse")) {
235 			document.body.removeChild(document.getElementById("dialogAnalyse"));
236 			this.content = document.createElement("div");
237 		}
238 	},
239 	/** ダイアログのページを切り替えます。
240 	 * @param {String} id ページの ID
241 	 */
242 	selectPage: function(id){
243 		var divHeader = document.getElementById("dialogAnalyseHeader");
244 		for (var i = 0; i < divHeader.childNodes.length - 1; i++) {
245 			var icon = divHeader.childNodes[i];
246 			if (icon.id == "Icon_" + id) {
247 				icon.className = "icon" + " " + "selected";
248 			} else {
249 				icon.className = "icon";
250 			}
251 		}	
252 		for (var i = 1; i < this.content.childNodes.length - 1; i++) {
253 			var page = this.content.childNodes[i];
254 			if (page.id == "Page_" + id) {
255 				page.style.display = "block";
256 			} else {
257 				page.style.display = "none";
258 			}
259 		}
260 		if ((this.content.offsetTop + this.content.offsetHeight) > window.innerHeight) this.onResize();
261 	},
262 	/** ページを追加します。
263 	 * @param {String} id      ID
264 	 * @param {String} caption キャプション
265 	 * @param {String} icon    アイコンの URI
266 	 */
267 	addPage: function(id, caption, icon){
268 		if (!this.content.id) this.content.id = "dialogAnalyse";
269 		if (!document.getElementById("dialogAnalyse")) document.body.appendChild(this.content);
270 		var divHeader = document.getElementById("dialogAnalyseHeader");
271 		if (!divHeader) {
272 			divHeader = document.createElement("div");
273 			divHeader.id = "dialogAnalyseHeader";
274 			divHeader.className = "header";
275 			var divHeaderEnd = document.createElement("div");
276 			divHeaderEnd.id = "dialogAnalyseHeaderEnd";
277 			divHeaderEnd.className = "null";
278 			divHeader.appendChild(divHeaderEnd);
279 			this.content.appendChild(divHeader);
280 			
281 			var p      = document.createElement("p");
282 			var ok     = document.createElement("input");
283 			ok.setAttribute("type",  "submit");
284 			ok.setAttribute("value", "OK");
285 			ok.setAttribute("onclick", "dialogAnalyse.onOK()");
286 			p.appendChild(ok);
287 			this.content.appendChild(p);
288 		}
289 		
290 		var divIcon = document.createElement("div");
291 		divIcon.id = "Icon_" + id;
292 		divIcon.className = "icon";
293 		divIcon.textContent = caption;
294 		divIcon.style.backgroundImage = "url(" + icon + ")";
295 		divIcon.setAttribute("onclick", "dialogAnalyse.selectPage('" + id + "')");
296 		divHeader.insertBefore(divIcon, document.getElementById("dialogAnalyseHeaderEnd"));
297 		
298 		var divPage = document.createElement("div");
299 		divPage.id = "Page_" + id;
300 		divPage.style.display = "none";
301 		this.content.insertBefore(divPage, this.content.lastChild);
302 	},
303 	/** フレームを追加します。
304 	 * @param {String} page      追加するページの ID
305 	 * @param {String} id        ID
306 	 * @param {String} caption   キャプション
307 	 * @param {Number} maxheight 最大の高さ
308 	 */
309 	addFrame: function(page, id, caption, maxheight){
310 		var fieldset = document.createElement("fieldset");
311 		fieldset.id = "Frame_" + page + "_" + id;
312 		var legend  = document.createElement("legend");
313 		legend.textContent = caption;
314 		fieldset.appendChild(legend);
315 		var ul = document.createElement("ul");
316 		if (maxheight) ul.style.maxHeight = maxheight;
317 		fieldset.appendChild(ul);
318 		var divPage = document.getElementById("Page_" + page);
319 		if (divPage) divPage.appendChild(fieldset);
320 	},
321 	/** ラベルを追加します。
322 	 * @param {String} page      追加するページの ID
323 	 * @param {String} frame     フレームの ID
324 	 * @param {String} id        ID
325 	 * @param {String} caption   キャプション
326 	 * @param {Number} [index]   表示するレス番号へのアンカー
327 	 * @param {Number} [level]   インデントする階層
328 	 */
329 	addLabel: function(page, frame, id, caption, index, level){
330 		var parentObject = document.getElementById("Frame_" + page + "_" + frame).lastChild;
331 		if (level > 0) {
332 			for (var i = 1; i <= level; i++) {
333 				parentObject.appendChild(document.createElement("ul"));
334 				parentObject = parentObject.lastChild;
335 			}
336 		}
337 		var li = document.createElement("li");
338 		var label = document.createElement("label");
339 		label.className = "dialogAnalyseLabel";
340 		var span = document.createElement("span");
341 		span.textContent = caption;
342 		label.appendChild(span);
343 		if (index) {
344 			var a = document.createElement("a");
345 			a.setAttribute("href", "javascript:void(0)");
346 			a.setAttribute("className", "resPointer");
347 			a.textContent = "(>>" + index + ")";
348 			label.appendChild(a);
349 		}
350 		li.appendChild(label);
351 		parentObject.appendChild(li);
352 	},
353 	/** リンクを追加します。
354 	 * @param {String} page      追加するページの ID
355 	 * @param {String} frame     フレームの ID
356 	 * @param {String} id        ID
357 	 * @param {String} caption   キャプション
358 	 * @param {String} uri     URI
359 	 * @param {Number} [index]   表示するレス番号へのアンカー
360 	 * @param {Number} [level]   インデントする階層
361 	 */
362 	addLink: function(page, frame, id, caption, uri, index, level){
363 		var parentObject = document.getElementById("Frame_" + page + "_" + frame).lastChild;
364 		if (level > 0) {
365 			for (var i = 1; i <= level; i++) {
366 				parentObject.appendChild(document.createElement("ul"));
367 				parentObject = parentObject.lastChild;
368 			}
369 		}
370 		var li = document.createElement("li");
371 		var label = document.createElement("label");
372 		label.className = "dialogAnalyseLabel";
373 		var a = document.createElement("a");
374 		a.setAttribute("href", uri);
375 		a.setAttribute("target", "_blank");
376 		a.className = "outLink";
377 		a.textContent = caption;
378 		label.appendChild(a);
379 		if (index) {
380 			label.appendChild(document.createTextNode(" ("));
381 			var a = document.createElement("a");
382 			a.setAttribute("href", "javascript:void(0)");
383 			a.className = "resPointer";
384 			a.textContent = ">>" + index;
385 			label.appendChild(a);
386 			label.appendChild(document.createTextNode(")"));
387 		}
388 		li.appendChild(label);
389 		parentObject.appendChild(li);
390 	},
391 	/** ID を追加します。
392 	 * @param {String} page      追加するページの ID
393 	 * @param {String} frame     フレームの ID
394 	 * @param {String} id        ID
395 	 * @param {String} caption   キャプション
396 	 * @param {Number} count    ID の発言回数
397 	 * @param {Number} [level]   インデントする階層
398 	 */
399 	addID: function(page, frame, id, caption, count, level){
400 		var parentObject = document.getElementById("Frame_" + page + "_" + frame).lastChild;
401 		if (level > 0) {
402 			for (var i = 1; i <= level; i++) {
403 				parentObject.appendChild(document.createElement("ul"));
404 				parentObject = parentObject.lastChild;
405 			}
406 		}
407 		var li = document.createElement("li");
408 		var label = document.createElement("label");
409 		label.className = "dialogAnalyseLabel";
410 		/*
411 		var a = document.createElement("a");
412 		a.setAttribute("href", "javascript:void(0)");
413 		a.setAttribute("target", "_blank");
414 		a.className = "mesID_" + caption;
415 		a.textContent = "ID:" + caption;
416 		label.appendChild(a);
417 		*/
418 		var span = document.createElement("span");
419 		span.className = "mesID_" + caption + " resID2";
420 		span.textContent = "ID:" + caption;
421 		label.appendChild(span)
422 		label.appendChild(document.createTextNode("(" + count + ")"));
423 		li.appendChild(label);
424 		parentObject.appendChild(li);
425 	},
426 	/** 逆参照を追加します。
427 	 * @param {String} page      追加するページの ID
428 	 * @param {String} frame     フレームの ID
429 	 * @param {String} id        ID
430 	 * @param {Number} index     レス番号
431 	 * @param {Number} count    レス番号に対する言及回数
432 	 * @param {Number} [level]   インデントする階層
433 	 */
434 	addTrackback: function(page, frame, id, index, count, level){
435 		var parentObject = document.getElementById("Frame_" + page + "_" + frame).lastChild;
436 		if (level > 0) {
437 			for (var i = 1; i <= level; i++) {
438 				parentObject.appendChild(document.createElement("ul"));
439 				parentObject = parentObject.lastChild;
440 			}
441 		}
442 		var li = document.createElement("li");
443 		var label = document.createElement("label");
444 		label.className = "dialogAnalyseLabel";
445 		var a = document.createElement("a");
446 		a.setAttribute("href", "javascript:void(0)");
447 		//a.setAttribute("target", "_blank");
448 		a.className = "resPointer";
449 		a.textContent = ">>" + index;
450 		label.appendChild(a);
451 		label.appendChild(document.createTextNode("(" + count + ")