PageSourceSearch

https://www.mrhelloworld.com/js/qplayer/QPlayer.js

js mrhelloworld.com collected 2026-10-08 04:57:59 UTC 30,721 bytes, 946 lines download raw bytes

1if (!window.QPlayer) {
2    window.QPlayer = {};
3}
4window.QPlayer.init = function () {
5
6    if (document.getElementById('QPlayer')) {
7        return;
8    }
9
10    $('body').append('<div id="QPlayer"><div id="QPlayer-body"><div id="QPlayer-panel"><div id="QPlayer-cover"><svg id="QPlayer-cover-default" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><circle cx="32" cy="32" r="32" style="fill:#222"/><circle cx="32" cy="32" r="16"/><circle cx="32" cy="32" r="12" style="fill:#e12"/><circle cx="32" cy="32" r="1.2" style="fill:#fff"/></svg></div><div id="QPlayer-control"><div id="QPlayer-title"><strong id="QPlayer-name"></strong> -<span id="QPlayer-artist"></span></div><div id="QPlayer-progress"><div id="QPlayer-progress-current" style="width: 0;"></div></div><div id="QPlayer-control-bar"><div id="QPlayer-time">00:00</div><div id="QPlayer-control-play"><svg id="QPlayer-btn-previous" xmlns="http://www.w3.org/2000/svg" viewBox="6 6 12 12"><path d="M6 6h2v12H6zm3.5 6l8.5 6V6z"/></svg><svg id="QPlayer-btn-next" xmlns="http://www.w3.org/2000/svg" viewBox="6 6 12 12"><path d="M6 18l8.5-6L6 6v12zM16 6v12h2V6h-2z"/></svg><div id="QPlayer-btn-play"><svg id="QPlayer-play" xmlns="http://www.w3.org/2000/svg" viewBox="16 10 22 28"><path d="M16 10v28l22-14z"/></svg><svg id="QPlayer-pause" xmlns="http://www.w3.org/2000/svg" viewBox="12 10 24 28"><path d="M12 38h8V10h-8v28zm16-28v28h8V10h-8z"/></svg></div></div><div id="QPlayer-control-more"><div id="QPlayer-btn-mode"><svg id="QPlayer-btn-repeat" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M7 7h10v3l4-4-4-4v3H5v6h2V7zm10 10H7v-3l-4 4 4 4v-3h12v-6h-2v4z"/></svg><svg id="QPlayer-btn-shuffle" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10.59 9.17L5.41 4 4 5.41l5.17 5.17 1.42-1.41zM14.5 4l2.04 2.04L4 18.59 5.41 20 17.96 7.46 20 9.5V4h-5.5zm.33 9.41l-1.41 1.41 3.13 3.13L14.5 20H20v-5.5l-2.04 2.04-3.13-3.13z"/></svg></div><svg id="QPlayer-btn-list" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><path d="M7 10h9V8H7v2zm0-7v2h9V3H7zm0 12h9v-2H7v2zm-4-5h2V8H3v2zm0-7v2h2V3H3zm0 12h2v-2H3v2z"/></svg><svg id="QPlayer-btn-lyrics" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 18 18"><path d="M4 14v2h10v-2H4zm0-8v2h10V6H4zm-2 6h14v-2H2v2zM2 2v2h14V2H2z"/></svg></div></div></div></div><div id="QPlayer-switch"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg></div></div><div id="QPlayer-more"><ol id="QPlayer-list"></ol><div id="QPlayer-lyrics"></div></div><audio id="QPlayer-audio"></audio></div>');
11
12    var q = window.QPlayer;
13    var v = {};
14
15    var
16        $q = $('#QPlayer'),
17        $audio = $('#QPlayer-audio'),
18        $cover = $('#QPlayer-cover'),
19        $list = $('#QPlayer-list'),
20        $title = $('#QPlayer-title'),
21        $more = $('#QPlayer-more'),
22        $time = $('#QPlayer-time'),
23        $progress = $('#QPlayer-progress'),
24        $progressCurrent = $('#QPlayer-progress-current'),
25        $lyrics = $('#QPlayer-lyrics'),
26        $mode = $('#QPlayer-btn-mode')
27    ;
28
29    var
30        audio = $audio[0],
31        coverTimeout = 500
32    ;
33
34    var $lyricsList, $listLi, isLoadPause, isPrevisionPlay, errorStartIndex, isAllError, setCoverTime;
35
36    q.version = '2.0.6';
37    q.audio = audio;
38    q.defaultProvider = q.defaultProvider || 'default';
39    q.provider = q.provider || {};
40    q.provider.default = q.provider.default || {
41        dataType: '*',
42        lyrics: function (current, success) {
43            var url = current.lrc;
44            if (!url) {
45                return;
46            }
47            var dataType = this.dataType;
48            $.ajax({
49                url: url,
50                dataType: dataType,
51                success: function (lrc) {
52                    if (!lrc) {
53                        return;
54                    }
55                    success(lrc);
56                }
57            });
58        }
59    };
60
61    function Shuffle(index) {
62        var _this = this;
63        if (index === undefined) {
64            this.index = -1;
65            this.list = [];
66        } else {
67            this.index = 0;
68            this.list = [index];
69        }
70        this.next = function () {
71            if (this.index + 1 === this.list.length) {
72                if (this.list.length >= v.list.length) {
73                    this.index = 0;
74                    return _this.list[0];
75                }
76                return push();
77            }
78            return this.list[++this.index];
79        };
80        this.previous = function () {
81            if (this.index === 0) {
82                var length = v.list.length;
83                if (this.list.length === length) {
84                    this.index = length - 1;
85                    return this.list[this.index];
86                }
87                var index = getIndex();
88                this.list.unshift(index);
89                return index;
90            }
91            if (this.index === -1) {
92                return push();
93            }
94            return this.list[--this.index];
95        };
96        function push() {
97            var index = getIndex();
98            _this.list.push(index);
99            ++_this.index;
100            return index;
101        }
102        function getIndex() {
103            var length = v.list.length;
104            if (_this.index === -1) {
105                return random(length);
106            }
107            var list = [];
108            for (var i = 0; i < length; ++i) {
109                if (_this.list.includes(i)) {
110                    continue;
111                }
112                list.push(i);
113            }
114            return list[random(list.length)];
115        }
116        function random(max) {
117            return Math.floor(Math.random() * max);
118        }
119    }
120
121    function getNextIndex() {
122        if (v.list.length === 0) {
123            return false;
124        }
125        if (q.isShuffle) {
126            return q.shuffle.next();
127        }
128        if (++q.index === q.list.length) {
129            q.index = 0;
130        }
131        return q.index;
132    }
133
134    function getPreviousIndex() {
135        if (v.list.length === 0) {
136            return false;
137        }
138        if (q.isShuffle) {
139            return q.shuffle.previous();
140        }
141        if (--q.index < 0) {
142            q.index = q.list.length - 1;
143        }
144        return q.index;
145    }
146
147    function onPlay() {
148        $q.addClass('QPlayer-playing');
149        // $title.marquee('resume');
150    }
151
152    function onPause() {
153        if (isLoadPause) {
154            isLoadPause = false;
155            return;
156        }
157        $q.removeClass('QPlayer-playing');
158        // $title.marquee('pause');
159    }
160
161    function s2m(s) {
162        var m = Math.floor(s / 60);
163        s = Math.floor(s % 60);
164        if (m < 10) {
165            m = '0' + m;
166        }
167        if (s < 10) {
168            s = '0' + s;
169        }
170        return m + ':' + s;
171    }
172
173    function getListLi(index) {
174        return $listLi.eq(index);
175    }
176
177    function Provider(provider, current) {
178        var callbacks = new ProviderCallback();
179        var isStop;
180        this.call = function (name, success, error) {
181            if (!error) {
182                error = function () {}
183            }
184            var data = current[name];
185            if (data) {
186                success(data);
187                return;
188            }
189            var callback = provider[name];
190            if (callback === true) {
191                callbacks.set(name, success, error);
192                return;
193            }
194            if (callback) {
195                callback(current, success, error);
196                return;
197            }
198            error();
199        };
200        this.stop = function () {
201            isStop = true;
202        };
203        this.load = function () {
204            isStop = false;
205            var load = provider.load;
206            if (!load) {
207                return;
208            }
209            load(current, callbacks);
210        };
211
212        function ProviderCallback() {
213            var callbacks = {};
214            var caches = {};
215            this.success = function (name, data, cache) {
216                var callback = callbacks[name];
217                if (!callback) {
218                    caches[name] = {
219                        data: data,
220                        cache: cache
221                    };
222                    return;
223                }
224                callback.success(data, cache);
225            };
226            this.error = function (name) {
227                var callback = callbacks[name];
228                if (!callback) {
229                    caches[name] = false;
230                    return;
231                }
232                callback.error();
233            };
234            this.set = function (name, success, error) {
235                if (isStop) {
236                    return;
237                }
238                var cache = caches[name];
239                if (cache) {
240                    success(cache.data, cache.cache);
241                    return;
242                }
243                if (cache === false) {
244                    error();
245                    return;
246                }
247                callbacks[name] = {success: success, error: error};
248            };
249        }
250    }
251
252    function getProvider(current) {
253        // noinspection JSUnresolvedVariable
254        var providerName = current.provider || q.defaultProvider;
255        if (typeof providerName === 'string') {
256            var provider = q.provider[providerName];
257            if (!provider) {
258                console.warn('没有找到相应的 Provider:' + providerName);
259                provider = {};
260            }
261            provider = current.provider = new Provider(provider, current);
262            provider.name = providerName;
263            return provider;
264        }
265        return providerName instanceof Provider ? providerName : new Provider({}, current);
266    }
267
268    function Lyrics(lrc) {
269        this.time = [];
270        this.text = [];
271        this.index = -1;
272        this.nextTime = 0;
273        this.offset = 0;
274        this.has = false;
275        this.add = function (time, text) {
276            var length = this.time.length;
277            for (var i = 0; i < length; ++i) {
278                var t = this.time[i];
279                if (t < time) {
280                    continue;
281                }
282                if (t === time) {
283                    this.text[i] += '<br>' + text;
284                    return;
285                }
286                if (i !== length - 1) {
287                    this.time.splice(i, 0, time);
288                    this.text.splice(i, 0, text);
289                    return;
290                }
291            }
292            this.time[length] = time;
293            this.text[length] = text;
294        };
295        this.find = function (time) {
296            var length = this.time.length;
297            for (var i = 0; i < length; ++i) {
298                var t = this.time[i] + this.offset;
299                if (t < time) {
300                    continue;
301                }
302                if (t === time) {
303                    return i;
304                }
305                return i - 1;
306            }
307            return length - 1;
308        };
309        this.goto = function (index) {
310            if (this.index !== -1) {
311                $($lyricsList[this.index]).removeClass('QPlayer-lyrics-current');
312            }
313            this.nextTime = this.time[index + 1] + this.offset;
314            this.index = index;
315            if (index < 0) {
316                return;
317            }
318            var $current = $($lyricsList[index]).addClass('QPlayer-lyrics-current');
319            $lyrics.stop(true).animate({
320                scrollTop: $current.offset().top - $lyrics.offset().top + $lyrics.scrollTop()
321                    - (162 - $current.height()) / 2
322            });
323        };
324        this.next = function (time) {
325            if (isNaN(this.nextTime) || this.nextTime > time) {
326                return;
327            }
328            this.goto(this.index + 1);
329        };
330        this.show = function () {
331            var length = this.text.length;
332            if (length === 0) {
333                return;
334            }
335            var html = '';
336            for (var i = 0; i < length; ++i) {
337                html += '<p>' + this.text[i] + '</p>';
338            }
339            $lyrics.removeClass('QPlayer-lyrics-no');
340            $lyrics.html(html);
341            $lyricsList = $lyrics.children();
342            this.goto(-1);
343        };
344        var linePattern = /^/mg;
345        var timePattern = /\t*\[([0-6]?\d):([0-6]?\d)(?:\.(\d{1,3}))?]/g;
346        var textPattern = /.*/mg;
347        var offsetPattern = /^\[offset:\t*([+-]?\d+)]/mg;
348        while ((linePattern.exec(lrc)) !== null) {
349            var lineIndex = linePattern.lastIndex;
350            var result;
351            offsetPattern.lastIndex = lineIndex;
352            if ((result = offsetPattern.exec(lrc)) != null) {
353                linePattern.lastIndex = offsetPattern.lastIndex;
354                this.offset = parseFloat(result[1]);
355                continue;
356            }
357            var index = timePattern.lastIndex = lineIndex;
358            var times = [];
359            function isTime() {
360                // noinspection JSReferencingMutableVariableFromClosure
361                return (result = timePattern.exec(lrc)) != null && result.index === index;
362            }
363            if (!isTime()) {
364                ++linePattern.lastIndex;
365                continue;
366            }
367            do {
368                linePattern.lastIndex = textPattern.lastIndex = timePattern.lastIndex;
369                var time = parseInt(result[1]) * 60000 + parseInt(result[2]) * 1000;
370                var f = result[3];
371                if (f) {
372                    var fLength = f.length;
373                    time += parseInt(fLength < 3 ? f + '0'.repeat(3 - fLength) : f);
374                }
375                times.push(time);
376                index = timePattern.lastIndex;
377            } while (isTime());
378            if (times.length === 0) {
379                continue;
380            }
381            var text = textPattern.exec(lrc)[0].trim();
382            var length = times.length;
383            for (var i = 0; i < length; ++i) {
384                this.add(times[i], text);
385            }
386        }
387        this.has = this.time.length !== 0;
388    }
389
390    function errorWithIndex(index) {
391        if (errorStartIndex === index) {
392            isAllError = true;
393            init();
394            q.pause();
395            return true;
396        }
397        if (errorStartIndex === -1) {
398            errorStartIndex = index;
399        }
400        getListLi(index).addClass('QPlayer-list-error');
401        return false;
402    }
403
404    function initCover() {
405        setTimeout(function () {
406            if (setCoverTime >= new Date().getTime() - coverTimeout) {
407                return;
408            }
409            $cover.css('background-image', '');
410            $cover.addClass('QPlayer-cover-no');
411        }, coverTimeout);
412    }
413
414    function initLoad() {
415        $lyrics.addClass('QPlayer-lyrics-no').html('<p>无歌词,请欣赏。</p>');
416        $title.html('<strong>没有歌曲</strong>');
417        $lyricsList = null;
418        $progressCurrent.width('0');
419        audio.currentTime = 0;
420        initCover();
421        // $title.marquee('destroy');
422    }
423
424    function init() {
425        q.index= -1;
426        q.current = null;
427        initLoad();
428        isLoadPause = false;
429        errorStartIndex = -1;
430    }
431
432    function initNoSongs() {
433        init();
434        $listLi = null;
435        $list.html('<li class="QPlayer-list-current">没有歌曲</li>');
436    }
437
438    function isNeedMarquee() {
439        var width = 0;
440        // noinspection JSUnresolvedFunction
441        $title.children().each(function () {
442            width += $(this).width();
443        });
444        return width > $title.width();
445    }
446
447    function preloadCover(url) {
448        var image = new Image();
449        image.onload = function () {
450            setCoverTime = new Date().getTime();
451            $cover.css('background-image', 'url("' + url + '")');
452            $cover.removeClass('QPlayer-cover-no');
453        };
454        image.src = url;
455    }
456
457    function anchorSongList($li) {
458        $list.scrollTop($list.scrollTop() - $list.offset().top + $li.offset().top + 1);
459    }
460
461    function getArtist(artist) {
462        return Array.isArray(artist) ? artist.join(' / ') : artist;
463    }
464
465    /**
466     * 加载
467     *
468     * @param {Number} index
469     * @return {boolean}
470     */
471    q.load = function(index) {
472        isAllError = false;
473        var current = q.current;
474        if (current) {
475            getProvider(current).stop();
476            isLoadPause = true;
477            audio.pause();
478        }
479        initLoad();
480        var length = v.list.length;
481        if (length === 0) {
482            console.warn('list 为空!');
483            return false;
484        }
485        if (!(length > index || index > 0)) {
486            console.warn('超出 list,length=' + length + ',index=' + index);
487            return false;
488        }
489        $list.children('.QPlayer-list-current').removeClass('QPlayer-list-current');
490        var $li = getListLi(index).addClass('QPlayer-list-current');
491        if (!current) {
492            anchorSongList($li)
493        }
494        current = v.list[index];
495        var title = '<strong>' + current.name + '</strong>';
496        var artist = getArtist(current.artist);
497        if (artist) {
498            title += '<span> - ' + artist + '</span>';
499        }
500        $title.html(title);
501        if (isNeedMarquee()) {
502            // $title.marquee({
503            //     duration: 10000,
504            //     gap: 40,
505            //     delayBeforeStart: 1000,
506            //     duplicated: true,
507            //     startVisible: true
508            // }).marquee('pause');
509        }
510        q.index = index;
511        q.current = current;
512        var provider = getProvider(current);
513        provider.call('cover', function (url, cache) {
514            if (isAllError || !url) {
515                return;
516            }
517            preloadCover(url);
518            if (cache) {
519                current.cover = url;
520            }
521        });
522        provider.call('lyrics', function (lrc) {
523            if (isAllError || !lrc) {
524                return;
525            }
526            if (!(lrc instanceof Lyrics)) {
527                current.lyrics = new Lyrics(lrc);
528            }
529            current.lyrics.show();
530        });
531        provider.load();
532        return true;
533    };
534
535    /**
536     * 播放
537     *
538     * @param index
539     * @param isPrevious
540     * @return {number}
541     * 0 错误
542     * 1 List 为空
543     * 2 播放音频
544     * 3 加载并播放音频
545     * 4 全部错误无法播放
546     */
547    function play(index, isPrevious) {
548        isPrevisionPlay = isPrevious;
549        if (index === false) {
550            console.warn('list 为空!');
551            return 1;
552        } else if (typeof index === 'number') {
553            if (!q.load(index)) {
554                return errorWithIndex(index) ? 4 : 0;
555            }
556        } else {
557            index = q.index;
558            if (audio.readyState !== 0) {
559                onPlay();
560                audioPlay();
561                return 2;
562            }
563        }
564        if (index === -1) {
565            return 0;
566        }
567
568        onPlay();
569        var current = q.current;
570        function getIndex() {
571            var list = v.list;
572            return list[index] === current ? index : list.indexOf(current);
573        }
574        function error() {
575            var _index = getIndex();
576            if (_index === -1) {
577                console.warn('未找到索引');
578                return;
579            }
580            if (errorWithIndex(_index)) {
581                return;
582            }
583            if (isPrevious) {
584                q.previous();
585            } else {
586                q.next();
587            }
588        }
589        function audioPlay() {
590            function catchError(e) {
591                if (e.name === 'AbortError') {
592                    return;
593                }
594                error = true;
595                console.error([e]);
596            }
597            var error = false;
598            var promise = null;
599            try {
600                promise = audio.play();
601                if (typeof Promise === 'function' && promise instanceof Promise) {
602                    promise
603                        .then(function () {
604                            getListLi(getIndex()).removeClass('QPlayer-list-error');
605                        })
606                        .catch(catchError);
607                }
608            } catch (e) {
609                catchError(e);
610            }
611            if (!error && !(typeof Promise === 'function' && promise instanceof Promise)) {
612                getListLi(getIndex()).removeClass('QPlayer-list-error');
613            }
614        }
615        getProvider(current).call('audio', function (url, cache) {
616            if (isAllError) {
617                return;
618            }
619            if (!url) {
620                error();
621                return;
622            }
623            if (cache) {
624                current.audio = cache;
625            }
626            audio.src = url;
627            audio.load();
628            audioPlay();
629        }, error);
630        return 3;
631    }
632
633    q.play = function (index, isPrevious) {
634        var bool = play(index, isPrevious);
635        if (bool) {
636            return bool;
637        }
638        return isPrevious ? q.previous() : q.next();
639    };
640
641    q.pause = function () {
642        onPause();
643        audio.pause();
644    };
645
646    q.next = function () {
647        return q.play(getNextIndex());
648    };
649
650    q.previous = function () {
651        return q.play(getPreviousIndex(), true);
652    };
653
654    q.reload = function () {
655        q.pause();
656        return q.load(q.index);
657    }
658
659    /**
660     * @param {Object|String} options
661     */
662    q.setColor = function(options) {
663        var jq = $('#QPlayer-theme');
664        if (!jq.length) {
665            jq = $('<style id="QPlayer-theme"></style>');
666            $q.append(jq);
667        }
668        // noinspection JSDeprecatedSymbols
669        var all = typeof options === 'string' ? options : options.all;
670        if (!all) {
671            jq.text('');
672            return;
673        }
674        var color;
675        var style = '';
676        function getColor(name) {
677            color = options[name];
678            if (color === undefined) {
679                color = all;
680            }
681        }
682        getColor('switch');
683        if (color) {
684            style += '#QPlayer-switch{background:' + color + ';}';
685        }
686        getColor('progress');
687        if (color) {
688            style += '#QPlayer-progress-current{background:' + color + ';}';
689        }
690        getColor('list');
691        if (color) {
692            style += '#QPlayer-list li:hover, #QPlayer-list li.QPlayer-list-current{border-left-color:' + color + ';}';
693        }
694        getColor('lyrics');
695        if (color) {
696            style += '#QPlayer-lyrics.QPlayer-lyrics-no p,#QPlayer-lyrics p.QPlayer-lyrics-current{color:' + color + ';}';
697        }
698        jq.text(style);
699    };
700
701    $('#QPlayer-switch').click(function () {
702        $q.toggleClass('QPlayer-show');
703    });
704    $('#QPlayer-btn-list').click(function () {
705        var name = 'QPlayer-list-show';
706        if ($more.hasClass(name)) {
707            $more.removeClass(name);
708        } else {
709            var index = q.index;
710            if (index !== -1) {
711                anchorSongList(getListLi(index))
712            }
713            $more.addClass(name);
714        }
715    });
716    $('#QPlayer-btn-lyrics').click(function () {
717        $more.toggleClass('QPlayer-lyrics-show');
718    });
719    $('#QPlayer-btn-play').click(function () {
720        if ($q.hasClass('QPlayer-playing')) {
721            q.pause();
722        } else {
723            q.play();
724        }
725    });
726    $('#QPlayer-btn-next').click(q.next);
727    $('#QPlayer-btn-previous').click(q.previous);
728    $cover.click(function () {
729        q.isRotate = !v.isRotate;
730    });
731    $mode.click(function () {
732        q.isShuffle = !v.isShuffle
733    });
734    $list.on('click', 'li:not(.QPlayer-list-current)', function () {
735        var index = $(this).index();
736        if (v.isShuffle) {
737            q.shuffle = new Shuffle(index);
738        }
739        q.play(index);
740    });
741    $audio
742        .on('playing', onPlay)
743        .on('pause', onPause)
744        .on('ended', q.next)
745        .on('timeupdate', function () {
746            var time = audio.currentTime;
747            $time.text(s2m(time));
748            var lyrics = q.current.lyrics;
749            if (lyrics && lyrics.has) {
750                lyrics.next(time * 1000);
751            }
752            if (!isProgressClicked) {
753                $progressCurrent.width(100 * time / audio.duration + '%');
754            }
755        })
756        .on('error', function () {
757            console.log('error', arguments);
758            var index = v.list.indexOf(q.current);
759            if (index !== -1 && errorWithIndex(index)) {
760                return;
761            }
762            if (isPrevisionPlay) {
763                q.previous();
764            } else {
765                q.next();
766            }
767        })
768        .on('canplay', function () {
769            errorStartIndex = -1;
770        })
771    ;
772
773    // 进度条操作
774    var isProgressClicked;
775    function getXFromEvent(e) {
776        var type = e.type;
777        switch (type) {
778            case 'mousedown':
779            case 'mouseup':
780            case 'mousemove':
781                return e.pageX;
782            case 'touchstart':
783            case 'touchmove':
784            case 'touchend':
785                return e.originalEvent.changedTouches[0].pageX;
786        }
787        return false;
788    }
789    function getProgressFromEvent(e) {
790        return getXFromEvent(e) - $progress.offset().left;
791    }
792    function moveProgress(e) {
793        if (!isProgressClicked) {
794            return;
795        }
796        e.preventDefault();
797        var total = $progress.width();
798        var current = getProgressFromEvent(e);
799        $progressCurrent.width(current < total ? current : total);
800    }
801    $progress.on('mousedown touchstart', function (e) {
802        isProgressClicked = true;
803        moveProgress(e);
804    });
805    $(document)
806        .on('mouseup touchend', function (e) {
807            if (!isProgressClicked) {
808                return;
809            }
810            isProgressClicked = false;
811            var duration = audio.duration;
812            if (isNaN(duration)) {
813                $progressCurrent.width(0);
814                return;
815            }
816            var total = $progress.width();
817            var current = getProgressFromEvent(e);
818            if (current >= total) {
819                q.next();
820                return;
821            }
822            var time = current > 0 ? duration * current / total: 0;
823            audio.currentTime = time;
824            var lyrics = q.current.lyrics;
825            if (lyrics && lyrics.has) {
826                lyrics.goto(lyrics.find(time * 1000));
827            }
828        })
829        .on('mousemove touchmove', moveProgress);
830
831    initNoSongs();
832
833    function getLocalStorageName(name) {
834        return 'QPlayer-' + name;
835    }
836
837    function getBoolFromLocalStorage(name) {
838        var value = localStorage.getItem(getLocalStorageName(name));
839        return !!(value && value !== 'false');
840    }
841
842    function setBoolFromLocalStorage(name, value) {
843        localStorage.setItem(getLocalStorageName(name), value);
844    }
845
846    function hasLocalStorageName(name) {
847        return localStorage.hasOwnProperty(getLocalStorageName(name));
848    }
849
850    function defineProperties(obj, properties) {
851        var keys = Object.keys(properties);
852        var length = keys.length;
853        for (var i1 = 0; i1 < length; ++i1) {
854            var key1 = keys[i1];
855            v[key1] = obj[key1];
856        }
857        Object.defineProperties(obj, properties);
858        for (var i2 = 0; i2 < length; ++i2) {
859            var key2 = keys[i2];
860            if (properties[key2].type === 'bool' && hasLocalStorageName(key2)) {
861                obj[key2] = getBoolFromLocalStorage(key2);
862                continue;
863            }
864            obj[key2] = v[key2] || properties[key2].default;
865        }
866    }
867
868    defineProperties(q,{
869        isShuffle: {
870            get: function () {
871                return v.isShuffle;
872            },
873            set: function (value) {
874                v.isShuffle = value;
875                setBoolFromLocalStorage('isShuffle', value);
876                if (value) {
877                    $mode.addClass('QPlayer-shuffle');
878                    var index = q.index;
879                    q.shuffle = new Shuffle(index === -1 ? undefined : index);
880                } else {
881                    $mode.removeClass('QPlayer-shuffle');
882                    q.shuffle = null;
883                }
884            },
885            type: 'bool',
886            default: false
887        },
888        isRotate: {
889            get: function () {
890                return v.isRotate;
891            },
892            set: function (value) {
893                v.isRotate = value;
894                setBoolFromLocalStorage('isRotate', value);
895                if (value) {
896                    $cover.addClass('QPlayer-cover-rotate');
897                } else {
898                    $cover.removeClass('QPlayer-cover-rotate');
899                }
900            },
901            type: 'bool',
902            default: true
903        },
904        list: {
905            get: function () {
906                return v.list;
907            },
908            set: function (value) {
909                if (!Array.isArray(value)) {
910                    console.warn('list 应该是数组');
911                    return;
912                }
913                v.list = value;
914                var length = value.length;
915                if (length === 0) {
916                    initNoSongs();
917                    return;
918                }
919                var html = '';
920                for (var i = 0; i < length; ++i) {
921                    var item = value[i];
922                    html += '<li><strong>' + item.name + '</strong>';
923                    var artist = getArtist(item.artist);
924                    if (artist) {
925                        html += '<span>' + artist + '</span>';
926                    }
927                    html += '</li>';
928                }
929                q.pause();
930                $list.html(html);
931                $listLi = $list.children();
932                if (q.index > -1 && q.current && length > q.index && value[q.index] === q.current) { // 已加载保持不变
933                    return;
934                }
935                init();
936                // noinspection JSCheckFunctionSignatures
937                q.load(getNextIndex());
938            },
939            type: 'list',
940            default: []
941        }
942    });
943};
944
945$(window.QPlayer.init);
946

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.