001 : // 002 : // WeeklyAllViewer 003 : // 004 : // Created by M.Miyazaki [2013.11.26] 005 : // Modified by M.Miyazaki [2014.01.16] - 実装の方針を転換(Ajaxの利用) 006 : // Modified by M.Miyazaki [2014.01.18] - 不具合の修正とキーワード欄増減対応 007 : // Modified by M.Miyazaki [2014.05.31] - 作業タイトル押下によるトグル表示、ボタン配置変更、検索結果のリスト表示対応 008 : // Modified by M.Miyazaki [2014.06.01] - 作業ナンバーのリンク化、否定検索対応 009 : // 010 : 011 : //================== 012 : // Settings 013 : //================== 014 : 015 : /** 016 : * 作業ログのファイル 017 : */ 018 : var WORKLOG_FILE = "log_sample.txt"; 019 : 020 : 021 : //=================== 022 : // Constants 023 : //=================== 024 : 025 : /** 026 : * 本文と見出しを表示する 027 : */ 028 : var VISIBLE_DETAIL = 1; 029 : 030 : /** 031 : * 本文を表示せず、見出しのみ表示する 032 : */ 033 : var INVISIBLE_DETAIL = 2; 034 : 035 : /** 036 : * 本文も見出しも表示しない(非表示の画面要素) 037 : */ 038 : var INVISIBLE_LOG = 3; 039 : 040 : /** 041 : * リクエストの状態(一連の動作が完了した) 042 : * 4で固定。 043 : */ 044 : var DONE = 4; 045 : 046 : /** 047 : * キーワード入力欄のIDの接頭辞 048 : */ 049 : var KEYWORD_PREFIX = "keyword"; 050 : 051 : /** 052 : * preタグのID接頭辞 053 : */ 054 : var PRE_PREFIX = "pre"; 055 : 056 : /** 057 : * liタグのID接頭辞 058 : */ 059 : var LI_PREFIX = "li"; 060 : 061 : 062 : 063 : //=================== 064 : // Variables 065 : //=================== 066 : 067 : /** 068 : * 作業ログの配列 069 : */ 070 : var workLogs = null; 071 : 072 : 073 : //====================== 074 : // Main Routine 075 : //====================== 076 : 077 : /** 078 : * 作業ログを吸い出して、見出しのみ一覧表示する。 079 : * 080 : * Created by M.Miyazaki [2014.01.15] 081 : */ 082 : function loadWorkLogs() 083 : { 084 : var handle = new XMLHttpRequest(); 085 : 086 : handle.open("GET", WORKLOG_FILE); 087 : 088 : handle.onreadystatechange = function() 089 : { 090 : if (handle.readyState == DONE) 091 : { 092 : workLogs = getWorkLogs(handle.responseText); 093 : showLogs(workLogs, INVISIBLE_DETAIL); 094 : } 095 : } 096 : 097 : handle.send(); 098 : 099 : return; 100 : } 101 : 102 : /** 103 : * 検索を行って結果を表示する。 104 : * 105 : * Created by M.Miyazaki [2013.11.27] 106 : * Modified by M.Miyazaki [2014.01.15] 107 : * Modified by M.Miyazaki [2014.06.01] 108 : */ 109 : function searchWorkLogs() 110 : { 111 : searchWorkLogsWithVisibleStatus(VISIBLE_DETAIL); 112 : 113 : return; 114 : } 115 : 116 : /** 117 : * 検索を行って結果のリストを表示する。 118 : * 119 : * Created by M.Miyazaki [2014.06.01] 120 : */ 121 : function searchWorkLogsList() 122 : { 123 : searchWorkLogsWithVisibleStatus(INVISIBLE_DETAIL); 124 : 125 : return; 126 : } 127 : 128 : 129 : //====================== 130 : // Sub Routines 131 : //====================== 132 : 133 : /** 134 : * 検索を行って結果を表示する。 135 : * 136 : * Created by M.Miyazaki [2014.06.01] 137 : * 138 : * @param visibleStatus 作業内容を表示するか否か 139 : */ 140 : function searchWorkLogsWithVisibleStatus(visibleStatus) 141 : { 142 : if (workLogs == null) { return; } 143 : 144 : resetVisibleStatus(workLogs); 145 : 146 : var keywords = getKeywords(); 147 : 148 : var resultLogs = search(workLogs, keywords); 149 : 150 : showLogs(resultLogs, visibleStatus); 151 : 152 : return; 153 : } 154 : 155 : /** 156 : * 作業ログのテキストを配列で応答する。 157 : * 158 : * Created by M.Miyazaki [2013.11.27] 159 : * Modified by M.Miyazaki [2013.11.28] 160 : * 161 : * @param logTextString 作業ログのテキスト 162 : * @return 作業ログの配列 163 : */ 164 : function getWorkLogs(logTextString) 165 : { 166 : if (logTextString == null) { return null; } 167 : 168 : var logArray = logTextString.split(/(■No\.[0-9]+ - [0-9]+\.[0-9]+\.[0-9]+\([^\)]+\)[=]{10,})/); 169 : 170 : var workLogs = new Array(); 171 : 172 : var numberOfLogs = (logArray.length - 1) / 2; 173 : 174 : for (var index = 0; index < numberOfLogs; index++) 175 : { 176 : var logString = logArray[index*2 + 1] + "" + logArray[index*2 + 2]; 177 : workLogs[index] = new WorkLog(logString); 178 : } 179 : 180 : return workLogs; 181 : } 182 : 183 : /** 184 : * 検索キーワードの配列を応答する。 185 : * 186 : * Created by M.Miyazaki [2014.01.18] 187 : * 188 : * @return 検索キーワードの配列 189 : */ 190 : function getKeywords() 191 : { 192 : var keywordElement; 193 : var index = 0; 194 : var keywords = new Array(); 195 : 196 : while ((keywordElement = document.getElementById(KEYWORD_PREFIX + (index + 1))) != null) 197 : { 198 : var keywordText = keywordElement.value; 199 : var isNegative = ("negativeKeywordField" == keywordElement.className) ? true : false; 200 : var keyword = new Keyword(keywordText, isNegative); 201 : 202 : keywords.push(keyword); 203 : index++; 204 : } 205 : 206 : return keywords; 207 : } 208 : 209 : /** 210 : * 作業ログの可視性をリセットする。 211 : * 212 : * Created by M.Miyazaki [2014.06.01] 213 : * 214 : * @param workLogs 可視性をリセットする作業ログの配列 215 : */ 216 : function resetVisibleStatus(workLogs) 217 : { 218 : for (var index = 0; index < workLogs.length; index++) 219 : { 220 : workLogs[index].isVisible = true; 221 : } 222 : 223 : return; 224 : } 225 : 226 : /** 227 : * 検索条件で絞り込む。 228 : * 229 : * Created by M.Miyazaki [2013.11.27] 230 : * Modified by M.Miyazaki [2014.01.16] 231 : * Modified by M.Miyazaki [2014.06.01] - 否定検索処理追加 232 : * 233 : * @param workLogs 検索対象となる作業ログの配列 234 : * @param keywords 検索キーワードの配列 235 : * @return 検索で絞り込んだ作業ログの配列 236 : */ 237 : function search(workLogs, keywords) 238 : { 239 : if (keywords.length == 0) { return workLogs; } 240 : 241 : var resultLogs = new Array(); 242 : var keyword = keywords.shift(); 243 : 244 : var keywordText = keyword.getKeywordText(); 245 : var isNegative = keyword.isNegative(); 246 : 247 : if (keywordText != "") 248 : { 249 : var regularExpression = new RegExp(keywordText, "g"); 250 : 251 : for (var index = 0; index < workLogs.length; index++) 252 : { 253 : if (true == workLogs[index].isVisible) 254 : { 255 : var wordArray = workLogs[index].getWorkLogText().match(regularExpression); 256 : 257 : if (isNegative == false) 258 : { 259 : workLogs[index].isVisible = ((wordArray != null) ? true : false); 260 : } 261 : else 262 : { 263 : workLogs[index].isVisible = ((wordArray == null) ? true : false); 264 : } 265 : } 266 : 267 : resultLogs.push(workLogs[index]); 268 : } 269 : } 270 : else 271 : { 272 : resultLogs = workLogs; 273 : } 274 : 275 : return search(resultLogs, keywords); 276 : } 277 : 278 : /** 279 : * 作業ログを一覧表示する。 280 : * 281 : * Created by M.Miyazaki [2013.11.27] 282 : * Modified by M.Miyazaki [2013.11.29] 283 : * Modified by M.Miyazaki [2014.01.16] 284 : * Modified by M.Miyazaki [2014.05.31] 285 : * Modified by M.Miyazaki [2014.06.01] 286 : * 287 : * @param resultLogs 表示する作業ログの配列 288 : * @param visibleStatus 作業内容を表示するか否か(VISIBLE_DETAIL or INVISIBLE_DETAIL) 289 : */ 290 : function showLogs(resultLogs, visibleStatus) 291 : { 292 : if (visibleStatus == INVISIBLE_LOG) 293 : { 294 : // INVISIBLE_LOG is unexpected visibleStatus! 295 : visibleStatus = VISIBLE_DETAIL; 296 : } 297 : 298 : var htmlString = ""; 299 : 300 : htmlString += "<ul>\n"; 301 : 302 : for (var index = 0; index < resultLogs.length; index++) 303 : { 304 : var log = resultLogs[index]; 305 : var workLogId = 'WorkLogNo.' + sanitize(log.getWorkNumber()); 306 : 307 : if (log.isVisible == true) 308 : { 309 : htmlString += "<li id='" + LI_PREFIX + workLogId + "' style='display:;'>"; 310 : } 311 : else 312 : { 313 : htmlString += "<li id='" + LI_PREFIX + workLogId + "' style='display:none;'>"; 314 : } 315 : 316 : htmlString += "<a name='" + workLogId + "' onclick='toggleLog(\"" + workLogId + "\");'>"; 317 : htmlString += "No." + sanitize(log.getWorkNumber()) + ":" + sanitize(log.getWorkTitle()); 318 : htmlString += "</a>"; 319 : 320 : if (visibleStatus == VISIBLE_DETAIL) 321 : { 322 : htmlString += "<pre id='" + PRE_PREFIX + workLogId + "' style='display:;'>"; 323 : } 324 : else 325 : { 326 : htmlString += "<pre id='" + PRE_PREFIX + workLogId + "' style='display:none;'>"; 327 : } 328 : 329 : var workDetail = sanitize(log.getWorkDetail()).replace(/\r\n|\r|\n/g, "<br>"); 330 : workDetail = convertNumberToLink(workDetail); 331 : 332 : htmlString += workDetail; 333 : htmlString += "</pre>"; 334 : htmlString += "</li>\n"; 335 : } 336 : 337 : htmlString += "</ul>\n"; 338 : 339 : document.getElementById("searchResult").innerHTML = htmlString; 340 : 341 : return; 342 : } 343 : 344 : /** 345 : * 346 : * 作業ログの表示形式(タイトルのみor詳細)をトグルする。 347 : * 348 : * Created by M.Miyazaki [2014.05.31] 349 : * 350 : * @param workLogId トグル対象の作業ログのID 351 : */ 352 : function toggleLog(workLogId) 353 : { 354 : var isVisible = ("" == document.getElementById(PRE_PREFIX + workLogId).style.display); 355 : var visibleStatus = isVisible ? INVISIBLE_DETAIL : VISIBLE_DETAIL; 356 : 357 : setVisibleStatus(workLogId, visibleStatus); 358 : 359 : return; 360 : } 361 : 362 : /** 363 : * 364 : * 作業ログの表示形式(タイトルのみor詳細or非表示)を設定する。 365 : * 366 : * Created by M.Miyazaki [2014.05.31] 367 : * Modified by M.Miyazaki [2014.06.01] 368 : * 369 : * @param workLogId 対象の作業ログのID 370 : * @param visibleStatus 作業ログの表示形式(VISIBLE_DETAIL or INVISIBLE_DETAIL or INVISIBLE_LOG) 371 : */ 372 : function setVisibleStatus(workLogId, visibleStatus) 373 : { 374 : if (visibleStatus == VISIBLE_DETAIL) 375 : { 376 : document.getElementById(LI_PREFIX + workLogId).style.display = ""; 377 : document.getElementById(PRE_PREFIX + workLogId).style.display = ""; 378 : } 379 : else if (visibleStatus == INVISIBLE_DETAIL) 380 : { 381 : document.getElementById(LI_PREFIX + workLogId).style.display = ""; 382 : document.getElementById(PRE_PREFIX + workLogId).style.display = "none"; 383 : } 384 : else if (visibleStatus == INVISIBLE_LOG) 385 : { 386 : document.getElementById(LI_PREFIX + workLogId).style.display = "none"; 387 : } 388 : else 389 : { 390 : // Unexpected visibleStatus... 391 : } 392 : 393 : return; 394 : } 395 : 396 : /** 397 : * 文字列をサニタイズ(無害化)する。 398 : * 399 : * Created by M.Miyazaki [2014.01.16] 400 : * 401 : * @param inputString サニタイズ対象の文字列 402 : * @return サニタイズ後の文字列 403 : */ 404 : function sanitize(inputString) 405 : { 406 : if (null == inputString) { return ""; } 407 : 408 : return inputString.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">"); 409 : } 410 : 411 : /** 412 : * 作業ログのナンバリング「No.XXX」をリンク化する。 413 : * 414 : * Created by M.Miyazaki [2014.06.01] 415 : * 416 : * @param inputString リンク化対象の文字列 417 : * @return リンク化した文字列 418 : */ 419 : function convertNumberToLink(inputString) 420 : { 421 : if (null == inputString) { return ""; } 422 : 423 : var regularExpression = new RegExp("(No\.[0-9]+)", "g"); 424 : var replacement = "<a href='#WorkLog$1' onclick='setVisibleStatus(\"WorkLog$1\", VISIBLE_DETAIL);'>$1</a>"; 425 : var outputString = inputString.replace(regularExpression, replacement); 426 : 427 : return outputString; 428 : } 429 : 430 : /** 431 : * キーワード種別(肯定or否定)をトグルする。 432 : * 433 : * Created by M.Miyazaki [2014.06.01] 434 : * 435 : * @param keywordId 種別変更対象のキーワード欄のID 436 : * @return リンク化した文字列 437 : */ 438 : function toggleKeywordType(keywordId) 439 : { 440 : var keywordField = document.getElementById(keywordId); 441 : 442 : if ("negativeKeywordField" == keywordField.className) 443 : { 444 : keywordField.className = "keywordField"; 445 : } 446 : else 447 : { 448 : keywordField.className = "negativeKeywordField"; 449 : } 450 : 451 : return; 452 : } 453 : 454 : /** 455 : * 検索キーワード入力欄を増やす。 456 : * 457 : * Created by M.Miyazaki [2014.01.18] 458 : * Modified by M.Miyazaki [2014.05.20] 459 : * Modified by M.Miyazaki [2014.05.31] 460 : * Modified by M.Miyazaki [2014.06.01] 461 : */ 462 : function addKeywordField() 463 : { 464 : var index = 2; 465 : while (document.getElementById(KEYWORD_PREFIX + index) != null) { index++; } 466 : 467 : //if (index == 2) 468 : //{ 469 : // document.getElementById("deleteKeywordFieldButton").style.display = ""; // visible! 470 : //} 471 : 472 : var bottomOfKeywords = document.getElementById("bottomOfKeywords"); 473 : 474 : var newBreak = document.createElement("br"); 475 : newBreak.id = KEYWORD_PREFIX + "Br" + index; 476 : bottomOfKeywords.parentNode.insertBefore(newBreak, bottomOfKeywords); 477 : 478 : var newField = document.getElementById(KEYWORD_PREFIX + "1").cloneNode(); 479 : newField.id = KEYWORD_PREFIX + index; 480 : newField.ondblclick = new Function("toggleKeywordType('" + KEYWORD_PREFIX + index + "');"); 481 : bottomOfKeywords.parentNode.insertBefore(newField, bottomOfKeywords); 482 : 483 : // Initialize textfield 484 : newField.value = ""; 485 : newField.className = "keywordField"; 486 : 487 : return; 488 : } 489 : 490 : /** 491 : * 検索キーワード入力欄を減らす。 492 : * 493 : * Created by M.Miyazaki [2014.01.18] 494 : * Modified by M.Miyazaki [2014.05.31] 495 : */ 496 : function deleteKeywordField() 497 : { 498 : var index = 2; 499 : while (document.getElementById(KEYWORD_PREFIX + index) != null) { index++; } 500 : 501 : index--; 502 : 503 : if (index == 1) { return; } 504 : //if (index == 2) 505 : //{ 506 : // document.getElementById("deleteKeywordFieldButton").style.display = "none"; // invisible! 507 : //} 508 : 509 : var oldBreak = document.getElementById(KEYWORD_PREFIX + "Br" + index); 510 : oldBreak.parentNode.removeChild(oldBreak); 511 : 512 : var oldField = document.getElementById(KEYWORD_PREFIX + index); 513 : oldField.parentNode.removeChild(oldField); 514 : 515 : return; 516 : } 517 : 518 : 519 : //================= 520 : // Objects 521 : //================= 522 : 523 : /** 524 : * 作業ログのオブジェクト。 525 : * 526 : * Created by M.Miyazaki [2013.11.27] 527 : * Modified by M.Miyazaki [2013.11.28] 528 : * Modified by M.Miyazaki [2014.06.01] 529 : * 530 : * @param logText 原本データ(作業ログ) 531 : */ 532 : function WorkLog(logText) 533 : { 534 : 535 : //===== Properties ===== 536 : 537 : /** 538 : * 作業ナンバー 539 : */ 540 : this.workNumber = null; 541 : 542 : /** 543 : * 日付 544 : */ 545 : this.workDate = null; 546 : 547 : /** 548 : * 作業タイトル 549 : */ 550 : this.workTitle = null; 551 : 552 : /** 553 : * 作業内容 554 : */ 555 : this.workDetail = null; 556 : 557 : /** 558 : * 表示対象か否か 559 : */ 560 : this.isVisible = true; 561 : 562 : /** 563 : * 原本データ(作業ログ) 564 : */ 565 : this.workLogText = logText; 566 : 567 : 568 : //===== Methods ===== 569 : 570 : /** 571 : * 原本データに含まれる各プロパティの値を解析する。 572 : */ 573 : this.parse = function() 574 : { 575 : var regularExpression = /^■No\.([0-9]+) - ([0-9]+)\.([0-9]+)\.([0-9]+)\([^\)]+\)[=]{10,}(?:\r\n|\n){2}【([^】]*)】(?:\r\n|\n)([\d\D]*)$/; 576 : 577 : var resultArray = this.workLogText.match(regularExpression); 578 : if (resultArray == null) return; 579 : 580 : // 作業ナンバー 581 : this.workNumber = resultArray[1]; 582 : 583 : // 日付 584 : var year = resultArray[2]; 585 : var month = resultArray[3]; 586 : var day = resultArray[4]; 587 : this.workDate = new Date(year, month - 1, day); 588 : 589 : // 作業タイトル 590 : this.workTitle = resultArray[5]; 591 : 592 : // 作業内容 593 : this.workDetail = resultArray[6].trim(); 594 : 595 : return; 596 : } 597 : 598 : /** 599 : * 自分自身を文字列で表現して応答する。 600 : */ 601 : this.toString = function() 602 : { 603 : var aString = ""; 604 : 605 : aString += "WorkLog("; 606 : aString += this.workNumber; 607 : aString += ",'"; 608 : aString += this.workTitle; 609 : aString += "')"; 610 : 611 : return aString; 612 : } 613 : 614 : 615 : //===== Accessor ===== 616 : 617 : /** 618 : * 作業ナンバーを応答する。 619 : * 620 : * @return 作業ナンバー 621 : */ 622 : this.getWorkNumber = function() 623 : { 624 : if (this.workNumber == null) this.parse(); 625 : 626 : return this.workNumber; 627 : } 628 : 629 : /** 630 : * 日付を応答する。 631 : * 632 : * @return 日付 633 : */ 634 : this.getWorkDate = function() 635 : { 636 : if (this.workDate == null) this.parse(); 637 : 638 : return this.workDate; 639 : } 640 : 641 : /** 642 : * 作業タイトルを応答する。 643 : * 644 : * @return 作業タイトル 645 : */ 646 : this.getWorkTitle = function() 647 : { 648 : if (this.workTitle == null) this.parse(); 649 : 650 : return this.workTitle; 651 : } 652 : 653 : /** 654 : * 作業内容を応答する。 655 : * 656 : * @return 作業内容 657 : */ 658 : this.getWorkDetail = function() 659 : { 660 : if (this.workDetail == null) this.parse(); 661 : 662 : return this.workDetail; 663 : } 664 : 665 : /** 666 : * 原本データ(作業ログ)を応答する。 667 : * 668 : * @return 作業内容 669 : */ 670 : this.getWorkLogText = function() 671 : { 672 : return this.workLogText; 673 : } 674 : 675 : } 676 : 677 : /** 678 : * 検索キーワードのオブジェクト。 679 : * 680 : * Created by M.Miyazaki [2014.06.01] 681 : * 682 : * @param keywordText 検索キーワード 683 : * @param isNegative 否定検索キーワードか否かの真偽値 684 : */ 685 : function Keyword(keywordText, isNegative) 686 : { 687 : 688 : //===== Properties ===== 689 : 690 : /** 691 : * 検索キーワード 692 : */ 693 : this.keywordText = keywordText; 694 : 695 : /** 696 : * 否定検索キーワードか否かの真偽値 697 : */ 698 : this.isNegativeKeyword = isNegative; 699 : 700 : 701 : //===== Methods ===== 702 : 703 : /** 704 : * 自分自身を文字列で表現して応答する。 705 : */ 706 : this.toString = function() 707 : { 708 : var aString = ""; 709 : 710 : aString += "Keyword('"; 711 : aString += this.keywordText; 712 : aString += "',"; 713 : aString += (this.isNegativeKeyword) ? "negative=true" : "negative=false"; 714 : aString += ")"; 715 : 716 : return aString; 717 : } 718 : 719 : 720 : //===== Accessor ===== 721 : 722 : /** 723 : * 検索キーワードを応答する。 724 : * 725 : * @return 検索キーワード 726 : */ 727 : this.getKeywordText = function() 728 : { 729 : return this.keywordText; 730 : } 731 : 732 : /** 733 : * 否定検索キーワードか否かの真偽値を応答する。 734 : * 735 : * @return 否定検索キーワードか否かの真偽値 736 : */ 737 : this.isNegative = function() 738 : { 739 : return this.isNegativeKeyword; 740 : } 741 : 742 : } 743 : 744 : 745 : //==================== 746 : // Extensions 747 : //==================== 748 : 749 : /** 750 : * 文字列にtrimメソッドを追加する。 751 : * 752 : * Quoted from https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/String/trim 753 : */ 754 : if (!String.prototype.trim) 755 : { 756 : String.prototype.trim = function() 757 : { 758 : return this.replace(/^\s+|\s+$/g, ""); 759 : } 760 : } 761 : 762 : /** 763 : * 非同期通信用オブジェクトを設定する。(Windowsへの対応) 764 : * 765 : * Quoted from http://itpro.nikkeibp.co.jp/article/COLUMN/20070613/274683/ 766 : */ 767 : if (window.ActiveXObject) 768 : { 769 : window.XMLHttpRequest = function() 770 : { 771 : try 772 : { 773 : return (new ActiveXObject("Microsoft.XMLHTTP")); 774 : } 775 : catch (ignore) {} 776 : 777 : return null; 778 : } 779 : } 780 :
This document was generated by NanigashiBiyori on 2014/06/02 at 00:25:56.