PageSourceSearch

https://furukawayutaka.com/files/15/a010/js/scrollsmartly.js

js furukawayutaka.com collected 2026-10-02 19:09:38 UTC 24,761 bytes, 945 lines download raw bytes

1/*!
2 scrollsmartly.js v0.1.0
3 Copyright (c) 2013 - 2014 Shinnosuke Watanabe | MIT License
4
5 scrollsmoothly.js
6 Copyright (c) 2008 KAZUMiX | MIT License
7 http://d.hatena.ne.jp/KAZUMiX/20080418/scrollsmoothly
8*/
9
10(function(window, document, location, history, undefined) {
11  'use strict';
12  
13  var smartly = {};
14  
15  var currentHrefWOHash = location.href.split('#')[0];
16
17  // Internal variables
18  var stillLoading = true,
19      callback,
20      delayedFunctionsQueue = [],
21      transit = [],
22      mutated = null,
23      reachedCurrentTarget = true;
24
25  smartly.set = function() {
26    switch (arguments.length){
27      case 1:
28      if (typeof arguments[0] === 'object') {
29        for(var key in arguments[0]){
30          if(smartly.hasOwnProperty(key)){
31            smartly[key] = arguments[0][key];
32          }
33        }
34      }
35      break;
36
37      case 2:
38      if (smartly.hasOwnProperty(arguments[0])) {
39        smartly[arguments[0]] = arguments[1];
40      }
41      break;
42    }
43
44    dequeue();
45    return smartly;
46  };
47
48  // Delay processing
49  smartly.delay = function() {
50    var func, time = 0, args = [];
51
52    switch (arguments.length) {
53      case 0:
54      break;
55
56      case 1:
57      if (typeof arguments[0] === 'function') {
58        func = arguments[0];
59      } else {
60        time = arguments[0];
61      }
62      break;
63
64      case 2:
65      func = arguments[0];
66      time = arguments[1];
67      break;
68
69      default:
70      func = arguments[0];
71      time = arguments[1];
72      for (var i=2; i < arguments.length; i++) {
73        args[i-2] = arguments[i];
74      }
75    }
76
77    var waitObj = {};
78    for (var key in smartly) {
79      if (typeof smartly[key] === 'function' && key !== 'delay') {
80        waitObj[key] = setDelay(smartly[key], time);
81      } else {
82        waitObj[key] = smartly[key];
83      }
84    }
85
86    function setDelay(origFunc, delay) {
87      return function() {
88        var origArgs = arguments;
89
90        var delayedFunction = function() {
91          setTimeout(function() {
92            if (typeof func === 'function') {
93              func.apply(this, args);
94            }
95            origFunc.apply(smartly, origArgs);
96          }, delay);
97        };
98
99        if (smartly.scrollingTo !== null) {
100          delayedFunctionsQueue[delayedFunctionsQueue.length] = delayedFunction;
101
102        } else {
103          delayedFunction();
104        }
105
106        if (time !== -1) {
107          return smartly;
108        }
109        return smartly.delay(-1);
110      };
111    }
112
113    return waitObj;
114  };
115
116  function dequeue() {
117    if (delayedFunctionsQueue.length > 0) {
118      console.log(delayedFunctionsQueue);
119      var currentQueue = delayedFunctionsQueue.shift();
120        currentQueue();
121    }
122  }
123
124  // Preference
125  smartly.easing = 6;
126  smartly.scrollingTo = null;
127  smartly.scrolledTo = null;
128  smartly.hashScrollSynced = true;
129  smartly.scrollHashSynced = true;
130
131  smartly.currentLeft = 0;
132  smartly.currentTop = 0;
133
134  smartly.position = 'left top';
135  smartly.marginLeft = 0;
136  smartly.marginTop = 0;
137  smartly.velocity = [0, 0];
138
139  var targetX = 0, targetY = 0, targetElm, targetHash = '';
140  var currentX = 0, currentY = 0;
141  var prevX = null, prevY = null;
142  var rootElm = document.documentElement || document.body;
143  smartly.homeElement = rootElm;
144  var windowWidth = 0, windowHeight = 0;
145
146  // if ハッシュが '#' 一文字のみである場合、それを取り除く
147  if (location.hash === ''&& location.href.indexOf('#') !== -1 &&
148  history.replaceState !== undefined) {
149    history.replaceState('', document.title, location.pathname);
150  }
151
152  var addEvent, removeEvent;
153  if (window.addEventListener !== undefined) {
154    addEvent = function(elm, eventType, func) {
155      elm.addEventListener(eventType, func, false);
156    };
157
158    removeEvent = function(elm, eventType, func) {
159      elm.removeEventListener(eventType, func, false);
160    };
161  // IE and old Opera
162  } else {
163    addEvent = function(elm, eventType, func) {
164      elm.attachEvent('on' + eventType, func);
165    };
166
167    removeEvent = function(elm, eventType, func) {
168      elm.detachEvent('on' + eventType, func);
169    };
170  }
171
172  var historyMoved = true;
173
174  var onBackOrForward = function(e) {
175    // 履歴の前後ではなく、
176    // 本ライブラリのスクロールにより hashchange イベントが起きた場合
177    if (! historyMoved || ! smartly.hashScrollSynced) {
178      return;
179    }
180
181    scrollTo(currentX, currentY);
182    smartly.scroll(location.hash.substring(1));
183  };
184
185  var scrollTimerID = null;
186  
187  // requestAnimationFrame polyfill
188  // www.paulirish.com/2011/requestanimationframe-for-smart-animating/
189  var lastTime = 0;
190  var vendors = ['ms', 'moz', 'webkit', 'o'];
191  var requestAnimationFrame, cancelAnimationFrame;
192  for (var x=0; x < vendors.length && !window.requestAnimationFrame; ++x) {
193    requestAnimationFrame = window[vendors[x]+'RequestAnimationFrame'];
194    cancelAnimationFrame = window[vendors[x]+'CancelAnimationFrame'] ||
195                           window[vendors[x]+'CancelRequestAnimationFrame'];
196  }
197  
198  vendors = null;
199
200  if (!requestAnimationFrame || !cancelAnimationFrame) {
201    requestAnimationFrame = function(callback, element) {
202      var currTime = new Date().getTime();
203      var timeToCall = Math.max(0, 16 - (currTime - lastTime));
204      var id = setTimeout(function() {
205        callback(currTime + timeToCall);
206      }, timeToCall);
207      lastTime = currTime + timeToCall;
208      return id;
209    };
210    cancelAnimationFrame = function(id) {
211      clearTimeout(id);
212    };
213  }
214
215
216  var scrollCompleteHandler = function() {
217    getCurrentXY();
218    /*
219    if (scrollTimerID !== null) {
220      clearTimeout(scrollTimerID);
221    }
222    scrollTimerID = setTimeout(function(){
223      getCurrentXY();
224    }, 150);
225    */
226  };
227
228  var resizeTimerID = null;
229
230  var resizeCompleteHandler = function() {
231    if (resizeTimerID !== null) {
232      clearTimeout(scrollTimerID);
233    }
234
235    resizeTimerID = setTimeout(function() {
236      reportMutated();
237    }, 50);
238  };
239
240  // polyfill for MutationObserver
241  MutationObserver = window.MutationObserver ||
242                     window.WebKitMutationObserver ||
243                     window.MozMutationObserver ||
244                     false;
245
246  var observer;
247
248  function reportMutated() {
249    mutated = true;
250  }
251
252  if (MutationObserver) {
253    observer = new MutationObserver( function(mutations) {
254      reportMutated();
255    });
256  } else {
257    // 変更通知を受け取れないので、以後、常にDOM変更が行われていることを前提に処理する
258    mutated = true;
259  }
260
261  var startScroll;
262
263  // on + event type のかたちの名を持つイベントリスナーを、当該イベントに登録する関数
264  var setCustomHTMLEvent;
265  var smartlyStartEvent, smartlyEndEvent;
266
267  // initial setting
268  var basicSettings = function(e) {
269    if (e) {
270      startScroll = function(clickEvent) {
271        clickEvent.preventDefault();
272        clickEvent.stopPropagation();
273        if (this.scrollSmartlyTarget !== undefined) {
274          smartly.scroll(this.scrollSmartlyTarget);
275        } else {
276          smartly.scroll(this.hash.substring(1));
277        }
278      };
279
280      setCustomHTMLEvent = function(eventType) {
281        var htmlEvent = document.createEvent('HTMLEvents');
282        htmlEvent.initEvent(eventType, false, false);
283        window['on' + eventType] = null;
284
285        addEvent(window, eventType, function(e) {
286          var callback = this['on' + eventType];
287          if(typeof callback === 'function'){
288            callback(e);
289          }
290        });
291
292        return htmlEvent;
293      };
294      
295    // old IE
296    } else {
297      startScroll = function() {
298        var self = event.srcElement || event.target;
299        event.returnValue = false;
300        event.cancelBubble = true;
301        smartly.scroll(self.hash.substring(1));
302      };
303
304      setCustomHTMLEvent = function(eventType) {
305        var htmlEvent = document.createEvent('HTMLEvents');
306        htmlEvent.initEvent(eventType, false, false);
307        window['on' + eventType] = null;
308
309        addEvent(window, eventType, function() {
310          var e = event;
311          var callback = this['on' + eventType];
312          if (typeof callback === 'function') {
313            callback(e);
314          }
315        });
316
317        return htmlEvent;
318      };
319    }
320
321    smartlyStartEvent = setCustomHTMLEvent('smartlystart');
322    smartlyEndEvent = setCustomHTMLEvent('smartlyend');
323
324    // https://developer.mozilla.org/docs/DOM/EventTarget.addEventListener
325    // #Multiple_identical_event_listeners
326    addEvent(window, 'hashchange', onBackOrForward);
327    addEvent(window, 'scroll', scrollCompleteHandler);
328    addEvent(window, 'resize', resizeCompleteHandler);
329
330    // noop
331    basicSettings = function(){};
332  };
333
334  addEvent(document, 'DOMContentLoaded', function(e) {
335    basicSettings(e);
336  });
337
338  addEvent(window, 'load', function(e) {
339    basicSettings(e);
340
341    getCurrentXY();
342    getWindowSize();
343
344    // 外部からページ内リンク付きで呼び出された場合
345    if (smartly.hashScrollSynced &&
346        (location.hash !== '' || smartly.homeElement !== rootElm)) {
347      // IE の場合、少し待ってからスクロール
348      if (window.attachEvent !== undefined &&
349          window.opera === undefined){
350        setTimeout(function() {
351          scrollTo(0, 0);
352          smartly.scroll(location.hash.substring(1) || smartly.homeElement);
353        }, 30);
354      } else {
355        scrollTo(0, 0);
356        smartly.scroll(location.hash.substring(1) || smartly.homeElement);
357      }
358    }
359
360    addEvent(document.body, 'click', holdDefaultHashChange);
361
362    stillLoading = false;
363  });
364
365  function holdDefaultHashChange(clickEvent) {
366    if (!smartly.hashScrollSynced) { return; }
367
368    var evt = clickEvent || event; //tmp
369    var elm = evt.target || evt.srcElement;
370    if (elm.href !== undefined && elm.href.indexOf('#') !== -1) {
371      resetHashChangeEvent(false);
372    }
373  }
374
375  var isArray;
376  if (Array.isArray !== undefined) {
377    isArray = Array.isArray;
378  } else {
379    var _isArraySub = Object.prototype.toString.call;
380    isArray = function(obj) {
381      return _isArraySub(obj) === '[object Array]';
382    };
383  }
384
385  smartly.scroll = function() {
386
387    var targets = [''];
388    var callback = null;
389
390    switch (arguments.length) {
391      case 0:
392      break;
393
394      case 1:
395      if (typeof arguments[0] === 'object') {
396        if (arguments[0].nodeType === 1) {
397          // 引数が一つのELEMENT Nodeであった場合
398          targets[0] = arguments[0];
399
400        } else if (arguments[0].via !== undefined) {
401          if (isArray(arguments[0].via)) {
402            // viaプロパティが配列であった場合
403            targets = arguments[0].via;
404          } else {
405            targets = [arguments[0].via];
406          }
407
408          if (arguments[0].to !== undefined) {
409            targets.push(arguments[0].to);
410          }
411
412        } else {
413          targets[0] = arguments[0].to;
414        }
415
416        if (arguments[0].callback !== undefined) {
417          callback = arguments[0].callback;
418        }
419
420      } else if (typeof arguments[0] !== 'function') {
421        targets[0] = arguments[0];
422
423      } else {
424        callback = arguments[0];
425      }
426      break;
427
428      case 2:
429      if (typeof arguments[1] === 'function') {
430        targets[0] = arguments[0];
431        callback = arguments[1];
432      } else {
433        targets[0] = arguments[0];
434        targets[1] = arguments[1];
435      }
436      break;
437
438      default:
439      var lastKey = arguments.length - 1;
440
441      for (var i=0; i < lastKey; i++) {
442        targets[i] = arguments[i];
443      }
444
445      if (typeof arguments[lastKey] === 'function') {
446        callback = arguments[lastKey];
447
448      } else {
449        targets[lastKey] = arguments[lastKey];
450      }
451
452      break;
453    }
454
455    var currentTarget = targets.shift();
456
457    // get coordinate of target element
458
459    // if ELEMENT Node
460    if (currentTarget.nodeType === 1) {
461      targetElm = currentTarget;
462      targetHash = currentTarget.id;
463
464    } else if(typeof currentTarget === 'string') {
465      if (currentTarget !== '') {
466        targetElm = document.getElementById(currentTarget);
467      } else if (smartly.homeElement) {
468        targetElm = smartly.homeElement;
469      } else {
470        targetElm = rootElm;
471      }
472
473      targetHash = currentTarget;
474    }
475
476    if (targetElm === null) {
477      return smartly;
478    }
479
480    if (targets.length > 0) {
481      transit = targets;
482    }
483
484    setTargetXY();
485
486    // スクロール中にターゲット要素が移動した際、追跡するかどうか
487    if (observer !== undefined) {
488      observer.observe( document.body, {
489        attributes: true,
490        subtree: true,
491        characterData: false
492      });
493    }
494
495    // スクロール中ではない場合、またはスクロール中であっても、次の目標にまだ到達していない場合
496    if(smartly.scrollingTo === null || !reachedCurrentTarget){
497      // スクロールの開始処理
498
499      smartly.scrollingTo = targetElm;
500
501      // 'callback' 引数をコールバック関数に設定する
502      callback = callback || null;
503
504      cancelAnimationFrame(scrollProcessID);
505
506      // fire 'smartlystart' event
507      window.dispatchEvent(smartlyStartEvent);
508
509    } else {
510      smartly.scrollingTo = targetElm;
511    }
512
513    removeEvent(window, 'scroll', scrollCompleteHandler);
514    resetHashChangeEvent(true);
515
516    reachedCurrentTarget = false;
517    processScroll();
518
519    return smartly;
520  };
521
522  var scrollProcessID;
523
524  var round = Math.round;
525  var abs = Math.abs;
526  
527  function processScroll() {
528
529    if (mutated === true) {
530      // スクロール中にターゲット要素に対するDOMの変更があった場合、
531      // 再度ターゲット要素の座標を取得する
532      setTargetXY();
533
534      if(observer !== undefined){
535        mutated = false;
536      }
537    }
538
539    getCurrentXY();
540
541    if (smartly.easing < 1) {
542      smartly.easing = 1;
543    }
544
545    var vx = (targetX - currentX) / smartly.easing;
546    var vy = (targetY - currentY) / smartly.easing;
547
548    smartly.velocity = [vx, vy];
549
550    // 目標座標付近に到達した場合
551    if ((abs(vx) < 0.05 && abs(vy) < 0.05) ||
552        (prevX === currentX && prevY === currentY) ||
553        reachedCurrentTarget) {
554
555      if (observer !== undefined) {
556        // DOMの変更通知の受取を止める
557        observer.disconnect();
558      }
559
560      addEvent(window, 'scroll', scrollCompleteHandler);
561
562      // scroll.stop が呼ばれていた場合
563      if (reachedCurrentTarget) { return; }
564
565      scrollTo(targetX, targetY);
566      smartly.scrolledTo = targetElm;
567      // 直近の目標に到達したことを表す
568      reachedCurrentTarget = true;
569      smartly.velocity = [0, 0];
570
571      if (transit.length > 0) {
572        // 経由する要素がまだ残っている場合
573        smartly.scroll(transit.shift());
574
575      } else {
576        // finish scroll
577        // because there is no element to pass through
578        setLocationHash();
579        smartly.scrollingTo = prevX = prevY = null;
580
581        if (typeof callback === 'function') {
582          callback();
583        }
584
585        dequeue();
586        // fire 'smartlyend' event
587        window.dispatchEvent(smartlyEndEvent);
588      }
589
590      return;
591    }
592
593    // reputation
594
595    prevX = currentX;
596    prevY = currentY;
597    
598    scrollTo(
599      currentX + (vx >= 0? Math.ceil(vx): Math.round(vx)),
600      currentY + (vy >= 0? Math.ceil(vy): Math.floor(vy))
601    );
602
603    scrollProcessID = requestAnimationFrame(processScroll);
604  }
605
606  function setLocationHash() {
607    if (! smartly.scrollHashSynced ||
608    targetHash === location.hash.substring(1) || delayedFunctionsQueue.length > 0) {
609      return;
610    }
611
612    if (targetHash !== '') {
613      resetHashChangeEvent(false);
614
615      if (history.pushState !== undefined) {
616        history.pushState('', document.title, location.pathname + '#' + targetHash);
617      } else if(focusKeywordChanged() ||
618                smartly.marginLeft !== 0 ||
619                smartly.marginTop !== 0) {
620
621          // ハッシュを変更する際に、そのハッシュをid属性に持つ要素へ移動するのを防ぐ必要がある。
622          // つまり、location.hash = StringX 呼び出す際に、id属性が StringX である要素が
623          // ドキュメント中に存在しないようにする必要がある。
624
625          targetElm.id = '';
626          location.hash = '#' + targetHash;
627          targetElm.id = targetHash;
628
629      } else {
630        location.hash = '#' + targetHash;
631      }
632
633    } else {
634      if (location.hash !== '' && history.pushState !== undefined) {
635        resetHashChangeEvent(false);
636        history.pushState('', document.title, location.pathname);
637      }
638    }
639  }
640
641  var hashChangeTimerID;
642
643  function resetHashChangeEvent(scrollBeginning){
644    if (!smartly.scrollHashSynced) { return; }
645
646    if (scrollBeginning) {
647      clearTimeout(hashChangeTimerID);
648
649    } else {
650      // 検知対象の HashChangeEvent では「ない」ことを表す
651      historyMoved = false;
652
653      // HashChangeEvent が発生し終わった頃に、
654      // これから起こる HashChangeEvent が検知対象となるよう再設定する
655      hashChangeTimerID = setTimeout(function() {
656        historyMoved = true;
657      }, 30);
658    }
659  }
660
661  function getCurrentXY() {
662    smartly.currentLeft =
663               currentX = document.documentElement.scrollLeft || document.body.scrollLeft;
664    smartly.currentTop =
665              currentY = document.documentElement.scrollTop || document.body.scrollTop;
666    
667    return;
668  }
669
670  var getScrollMaxXY;
671
672  if (window.scrollMaxX !== undefined) {
673    getScrollMaxXY = function() {
674      return {x: window.scrollMaxX, y: window.scrollMaxY};
675    };
676  } else {
677    getScrollMaxXY = function() {
678      var documentSize = getDocumentSize();
679      getWindowSize();
680      return {
681        x: documentSize.width - windowWidth,
682        y: documentSize.height - windowHeight
683      };
684    };
685
686    var getDocumentSize = function() {
687      return{
688        width: Math.max(
689          document.body.scrollWidth,
690          document.documentElement.scrollWidth
691        ),
692        height: Math.max(
693          document.body.scrollHeight,
694          document.documentElement.scrollHeight)
695      };
696    };
697  }
698
699  var getWindowSize;
700
701  if (rootElm.clientWidth !== undefined) {
702    getWindowSize = function() {
703      windowWidth = rootElm.clientWidth;
704      windowHeight = rootElm.clientHeight;
705    };
706
707  } else if(window.innerWidth) {
708    var box = document.createElement('div');
709    var boxStyle = box.style;
710    boxStyle.position = 'absolute';
711    boxStyle.left = boxStyle.top = '0';
712    boxStyle.width = boxStyle.height = '100%';
713    boxStyle.margin = boxStyle.padding = '0';
714    boxStyle.border = 'none';
715    boxStyle.visibility = 'hidden';
716
717    getWindowSize = function() {
718      document.body.appendChild(box);
719      windowWidth = box.offsetWidth;
720      windowHeight = box.offsetHeight;
721      document.body.removeChild(box);
722    };
723  }
724
725  function setTargetXY() {
726    // スクロール先座標をセットする
727    var x = 0;
728    var y = 0;
729    var elm = targetElm;
730
731    while(elm){
732      if (elm.offsetLeft !== undefined) {
733        x += elm.offsetLeft;
734        y += elm.offsetTop;
735
736      } else {
737        var rect = elm.getBoundingClientRect();
738        x += rect.left;
739        y += rect.top;
740      }
741
742      elm = elm.offsetParent;
743    }
744
745    var maxScroll = getScrollMaxXY();
746    var focus = getParsedFocus();
747
748    targetX = Math.min(x + focus.x, maxScroll.x);
749    if (targetX < 0) {
750      targetX = 0;
751    }
752
753    targetY = Math.min(y + focus.y, maxScroll.y);
754    if (targetY < 0) {
755      targetY = 0;
756    }
757  }
758
759  function focusKeywordChanged(){
760    return (smartly.position && smartly.position !== 'left top');
761  }
762
763  function getParsedFocus(){
764    var keywordX = 0;
765    var keywordY = 0;
766
767    if (focusKeywordChanged()) {
768      var words = String(smartly.position).split(' ');
769      if (words.length === 1) {
770        words[1] = words[0];
771      }
772
773      var elmWidth, elmHeight;
774      if (targetElm.offsetWidth !== undefined) {
775        elmWidth = targetElm.offsetWidth;
776        elmHeight = targetElm.offsetHeight;
777
778      } else {
779        var targetRect = targetElm.getBoundingClientRect();
780        elmWidth = targetRect.width;
781        elmHeight = targetRect.height;
782      }
783
784      var fraction;
785
786      fraction = fractionalize(words[0]);
787      keywordX = parseInt((windowWidth - elmWidth) * fraction, 10);
788
789      fraction = fractionalize(words[1]);
790      keywordY = parseInt((windowHeight - elmHeight) * fraction, 10);
791    }
792
793    return {
794      x: - (keywordX + smartly.marginLeft),
795      y: - (keywordY + smartly.marginTop)
796    };
797  }
798
799  function fractionalize(keyword) {
800    if (keyword === 'left' || keyword === 'top') {
801      return 0;
802
803    } else if (keyword === 'center') {
804      return 0.5;
805
806    } else if (keyword === 'right' || keyword === 'bottom') {
807      return 1;
808
809    } else if (keyword.charAt(keyword.length-1) === '%') {
810      return parseFloat(keyword) * 0.01;
811    }
812
813    return 0;
814  }
815
816  smartly.stop = function() {
817    // 目標に到達「したこと」にし、スクロール処理を終了させる
818    reachedCurrentTarget = true;
819    transit= [];
820    smartly.scrollingTo = null;
821
822    dequeue();
823    return smartly;
824  };
825
826  smartly.finish = function() {
827    delayedFunctionsQueue= [];
828    smartly.stop();
829
830    return smartly;
831  };
832
833  function each(obj, func) {
834    if (!obj) { return; }
835    var items;
836    if (obj.length === undefined) {
837      items = [obj];
838    } else {
839      items = obj;
840    }
841
842    for (var i=0; i < items.length; i++) {
843      func.call(smartly, items[i], i);
844    }
845  }
846
847  smartly.on = function(elm, target) {
848    each(elm, function(val) {
849      if (target !== undefined) {
850        val.scrollSmartlyTarget = target;
851      } else {
852        val.scrollSmartlyTarget = '';
853      }
854
855      addEvent(val, 'click', startScroll);
856      val.style.cursor = 'pointer';
857    });
858
859    dequeue();
860    return smartly;
861  };
862
863  smartly.off = function(elm) {
864    each(elm, function(val) {
865      delete val.scrollSmartlyTarget;
866      removeEvent(val, 'click', startScroll);
867      val.style.cursor = '';
868    });
869
870    dequeue();
871    return smartly;
872  };
873
874  smartly.replaceAnchor = function(elm, calledFromMethod) {
875    /*
876    **イベントリスナーを登録するリンク**:
877    ・href属性が '#' で始まるページ内リンクであり、尚かつ、それが指し示すアンカーがページ内に存在するもの
878      -> id='anchor' である要素へのスクロール
879    ・href属性が '#' 一文字のみのリンク -> ドキュメントの最上端へのスクロール
880
881    **イベントリスナーを登録しないリンク**:
882    ・href属性に '#' が含まれないリンク
883    ・href属性に '#' を含むが、当該リンクのあるページ内のものではない、別ページのアンカーへのリンク
884    ・href属性を持たないa要素
885    */
886
887    var hrefStr = elm.href + '';
888    var splitterIndex = hrefStr.lastIndexOf('#');
889
890    // '#' 以降を除いた文字列が、現在表示しているサイトのURLと一致しているかの判定。
891    // '#' が無ければ String.substring(0, -1) つまり '' となり、偽である。
892    if (hrefStr.substring(0, splitterIndex) === currentHrefWOHash) {
893      // In HTML4?
894      if (elm.hash !== undefined) {
895        elm.hash = hrefStr.substring(splitterIndex + 1);
896      }
897      delete elm.scrollSmartlyTarget;
898      addEvent(elm, 'click', startScroll);
899    }
900
901    if (calledFromMethod !== true) {
902      dequeue();
903      return smartly;
904    }
905  };
906
907  smartly.all = function() {
908    if (stillLoading) {
909      addEvent(window, 'load', function() {
910        stillLoading = false;
911        smartly.all();
912      });
913
914      dequeue();
915      return smartly;
916    }
917    // bind events to in-page links
918    var linkElms = document.links;
919    for (var i=0; i<linkElms.length; i++) {
920      smartly.replaceAnchor(linkElms[i], true);
921    }
922
923    dequeue();
924    return smartly;
925  };
926
927  // expose
928  window.smartly = smartly;
929  
930  // some AMD build optimizers like r.js check for condition patterns like the following:
931  if (typeof define === 'function' &&
932      typeof define.amd === 'object' &&
933      define.amd) {
934
935    // define as an anonymous module so, through path mapping, it can be
936    // referenced as the "underscore" module
937    define('scrollsmartly', [], function() {
938      return smartly;
939    });
940  }
941  // check for `exports` after `define` in case a build optimizer adds an `exports` object
942  else if (typeof module !== 'undefined' && module.exports) {
943    (module.exports = smartly).smartly = smartly;
944  }
945}(window, document, location, history));

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.