PageSourceSearch

https://www.town.yoshino.nara.jp/resource/js/add_tel_link.js

js town.yoshino.nara.jp collected 2026-10-02 05:57:18 UTC 7,523 bytes, 148 lines download raw bytes

1$(window).on('load', function() {
2  
3  // スマホ・タブレットのみ処理
4  const userAgent = window.navigator.userAgent.toLowerCase();
5
6  if ((userAgent.indexOf('iphone') > -1 ||
7      userAgent.indexOf('android') > -1 ||
8      userAgent.indexOf('ipad') > -1 || userAgent.indexOf('macintosh') > -1 || 
9      userAgent.indexOf('windows phone') > -1)
10      && 'ontouchend' in document) {
11
12    // 電話番号にtelリンクを付ける処理
13      
14    // JavaScriptのclosestメソッドをIE対応させるためのポリフィル
15    //(スマホ・タブレットのみの処理ではあるが、PC対応もできるように)
16    // https://developer.mozilla.org/ja/docs/Web/API/Element/closest#Polyfill
17    if (!Element.prototype.matches) {
18      Element.prototype.matches = Element.prototype.msMatchesSelector ||
19                                  Element.prototype.webkitMatchesSelector;
20    }
21    
22    if (!Element.prototype.closest) {
23      Element.prototype.closest = function(s) {
24        var el = this;
25    
26        do {
27          if (Element.prototype.matches.call(el, s)) return el;
28          el = el.parentElement || el.parentNode;
29        } while (el !== null && el.nodeType === 1);
30        return null;
31      };
32    }
33
34    /**
35     * 以下の変数(no_number_beforeとno_number_after)を、電話番号パターンの前後に付けることを推奨。
36     * 電話番号パターンの前後に数字がある時は、電話番号と見なさないようにするため。
37     * (例)00XX-0000-0000←この数字の並びは電話番号だとありえないが、XX-0000-0000部分が電話番号パターンとして引っかかってしまう。
38     * 
39     * しかし、IE・Safariでは正規表現の後読みが使えないようなのでno_number_beforeに(?<![0-9])を代入したほうはコメントアウトしておきます。
40     * よって、直前に数字がある時は電話番号ではないものに引っかかることがある状態です。
41     * IE・Safariで後読みが使えるようになった場合は'(?<![0-9])'を代入している行を有効にし、下の空文字代入してる行を無効にすることで、直前に数字がある電話番号パターンに引っかかることを防げます。
42     */
43    // const no_number_before = '(?<![0-9])';
44    const no_number_before = '';
45    const no_number_after = '(?![0-9])';
46
47    /**
48     * 以下の固定電話番号にマッチする正規表現
49     * 市外局番の1桁目は1~9、市内局番の1桁目は2~9
50     * 0X-XXXX-XXXX,0XX-XXX-XXXX,0XXX-XX-XXXX,0XXXX-X-XXXX
51     * (0X)XXXX-XXXX,(0XX)XXX-XXXX,(0XXX)XX-XXXX,(0XXXX)X-XXXX
52     * 0X(XXXX)XXXX,0XX(XXX)XXXX,0XXX(XX)XXXX,0XXXX(X)XXXX
53     */
54    const landline_number = no_number_before + '0([1-9]-[2-9][0-9]{3}|[1-9][0-9]-[2-9][0-9]{2}|[1-9][0-9]{2}-[2-9][0-9]|[1-9][0-9]{3}-[2-9])-[0-9]{4}' + no_number_after;
55    const landline_number_brackets_front = no_number_before + '\\(0([1-9]\\)[2-9][0-9]{3}|[1-9][0-9]\\)[2-9][0-9]{2}|[1-9][0-9]{2}\\)[2-9][0-9]|[1-9][0-9]{3}\\)[2-9])-[0-9]{4}' + no_number_after;
56    const landline_number_brackets_center = no_number_before + '0([1-9]\\([2-9][0-9]{3}|[1-9][0-9]\\([2-9][0-9]{2}|[1-9][0-9]{2}\\([2-9][0-9]|[1-9][0-9]{3}\\([2-9])\\)[0-9]{4}' + no_number_after;
57
58    /**
59     * 以下の携帯電話番号にマッチする正規表現
60     * 070-XXXX-XXXX,080-XXXX-XXXX,090-XXXX-XXXX
61     * (070)XXXX-XXXX,(080)XXXX-XXXX,(090)XXXX-XXXX
62     * 070(XXXX)XXXX,080(XXXX)XXXX,090(XXXX)XXXX
63     */
64    const mobile_number = no_number_before + '0[789]0-[0-9]{4}-[0-9]{4}' + no_number_after;
65    const mobile_number_brackets_front = no_number_before + '\\(0[789]0\\)[0-9]{4}-[0-9]{4}' + no_number_after;
66    const mobile_number_brackets_center = no_number_before + '0[789]0\\([0-9]{4}\\)[0-9]{4}' + no_number_after;
67
68    /**
69     * 以下のIP電話番号にマッチする正規表現
70     * 050-XXXX-XXXX
71     * (050)XXXX-XXXX
72     * 050(XXXX)XXXX
73     */
74    const ip_number = no_number_before + '050-[0-9]{4}-[0-9]{4}' + no_number_after;
75    const ip_number_brackets_front = no_number_before + '\\(050\\)[0-9]{4}-[0-9]{4}' + no_number_after;
76    const ip_number_brackets_center = no_number_before + '050\\([0-9]{4}\\)[0-9]{4}' + no_number_after;
77
78    /**
79     * 以下のフリーダイヤルにマッチする正規表現
80     * 0120-XXX-XXX
81     * (0120)XXX-XXX
82     * 0120(XXX)XXX
83     */
84    const free_number = no_number_before + '0120-[0-9]{3}-[0-9]{3}' + no_number_after;
85    const free_number_brackets_front = no_number_before + '\\(0120\\)[0-9]{3}-[0-9]{3}' + no_number_after;
86    const free_number_brackets_center = no_number_before + '0120\\([0-9]{3}\\)[0-9]{3}' + no_number_after;
87
88    // 上記全ての電話番号パターン
89    const tel_pattern = new RegExp(landline_number+"|"+landline_number_brackets_front+"|"+landline_number_brackets_center+"|"+mobile_number+"|"+mobile_number_brackets_front+"|"+mobile_number_brackets_center+"|"+ip_number+"|"+ip_number_brackets_front+"|"+ip_number_brackets_center+"|"+free_number+"|"+free_number_brackets_front+"|"+free_number_brackets_center,"g");
90
91    // 文字列をNodeListに変換する関数
92    function convertHtml(text){
93      const div = document.createElement("div");
94      div.innerHTML = '<div>
94' + text + '</div>';
95      return div.firstChild.childNodes;
96    }
97
98    // 電話番号のパターンが含まれるテキストノードを取得
99    const nodeIterator = document.createNodeIterator(
100      document.body,
101      NodeFilter.SHOW_TEXT,
102      function (node) {
103        let is_match = tel_pattern.test(node.nodeValue);
104        if(is_match) {
105          let hoge = tel_pattern.test(node.nodeValue); //これを挟むと何故か動く(これを書かないとiPhoneのSafariとIEで一部リンクが付かなくなる)
106          return NodeFilter.FILTER_ACCEPT;
107        } else {
108          return NodeFilter.FILTER_REJECT;
109        }
110      },
111      null
112    );
113
114    let node;
115    while (node = nodeIterator.nextNode()) {
116
117      // HTML構造がおかしくなる箇所は置き換えない
118      if (!node.parentNode.closest('a') &&
119          !node.parentNode.closest('button') &&
120          node.parentNode.tagName !== 'A' &&
121          node.parentNode.tagName !== 'BUTTON' &&
122          node.parentNode.tagName !== 'OPTION') {
123
124        // 本文中の<>をタグとして認識されないように実体参照に置き換える
125        node.textContent = node.textContent.replace(/</g, '&lt;');
126        node.textContent = node.textContent.replace(/>/g, '&gt;');
127
128        // 置き換えるべきHTMLテキスト
129        const replaceHTML= node.textContent.replace(tel_pattern, function (match) {
130          return '<a href="tel:' + match.replace(/[\(\)-]/g, "") + '">' + match + '</a>';
131        });
132
133        // 置き換えるべきDOMオブジェクト
134        const replaceNodeLists = convertHtml(replaceHTML);
135
136        // DOMを置換する
137
138        // IEではfor文でletの挙動がおかしい可能性があるのでvarで対応。
139        // iとするところを、変数名がかぶりづらいようにreplaceNodeListsIndexと長めにしました。
140        for (var replaceNodeListsIndex = 0; replaceNodeListsIndex < replaceNodeLists.length; replaceNodeListsIndex++) {
141          const newNode = replaceNodeLists[replaceNodeListsIndex].cloneNode(true);
142          node.parentNode.insertBefore(newNode, node);
143        }
144        node.parentNode.removeChild(node);
145      }
146    }
147  }
148});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.