MediaWiki:Gadget-musicMap-core.js: Difference between revisions
Jump to navigation
Jump to search
Content added Content deleted
(Created page with "→Music map * Generates an interactive music map that has toggleable polygons on it. Can be used as a 'checklist' to track music track unlock progression. * See [[Map:Music tracks]]: var MM = {}; MM.touch = false; MM.getUnlocked = function() { var ls = localStorage.getItem('musicMap-'+mw.config.get('wgPageName')); if (!ls) return []; // map characters back to numbers and convert to var bitstr = Array.prototype.map.call(ls, function(x) { // go through each cha...") |
No edit summary |
||
Line 1: | Line 1: | ||
"use strict"; |
|||
/* Music map |
/* Music map |
||
* Generates an interactive music map that has toggleable polygons on it. Can be used as a 'checklist' to track music track unlock progression. |
* Generates an interactive music map that has toggleable polygons on it. Can be used as a 'checklist' to track music track unlock progression. |
||
Line 6: | Line 8: | ||
var MM = {}; |
var MM = {}; |
||
MM.touch = false; |
MM.touch = false; |
||
MM.getUnlocked = function() { |
MM.getUnlocked = function () { |
||
var ls = localStorage.getItem('musicMap-' + mw.config.get('wgPageName')); |
|||
if (!ls) return []; |
|||
// map characters back to numbers and convert to |
|||
var bitstr = Array.prototype.map.call(ls, function (x) { |
|||
// go through each character in the string |
|||
var str = '00000' + parseInt(x, 32).toString(2); // parse back to bit string with sufficient leading zeroes to turn it into 5 bits long |
|||
var str = '00000' + parseInt(x, 32).toString(2); // parse back to bit string with sufficient leading zeroes to turn it into 5 bits long |
|||
return str.slice(-5); // the actual bits that were parsed, plus any leading zeroes if needed |
|||
}).join(''); // convert array of bitstrings to single long strong |
|||
}).join(''); // convert array of bitstrings to single long strong |
|||
// convert bitstring back into an array of bools |
|||
var bits = Array.prototype.map.call(bitstr, function(x) { return parseInt(x); }); |
|||
var bits = Array.prototype.map.call(bitstr, function (x) { |
|||
return bits; |
|||
return parseInt(x); |
|||
} |
|||
}); |
|||
MM.saveUnlocked = function(arr) { |
|||
return bits; |
|||
var bits = []; |
|||
}; |
|||
for (var i=0; i<arr.length; i++) bits[i] = arr[i] ? 1 : 0; // fill in empty array elements |
|||
MM.saveUnlocked = function (arr) { |
|||
var bitstr = bits.join(''); // array in bit representation |
|||
var bits = []; |
|||
// Split up in chunks of 5 bits. The array length should be a multiple of 5 based on the musicMap function. |
|||
for (var i = 0; i < arr.length; i++) bits[i] = arr[i] ? 1 : 0; // fill in empty array elements |
|||
// Use 32-bit string, because toString(64) is not supported in plain JS. |
|||
var bitstr = bits.join(''); // array in bit representation |
|||
var b32 = bitstr.match(/.{1,5}/g).map(function(x) { return parseInt(x, 2).toString(32); }); |
|||
// Split up in chunks of 5 bits. The array length should be a multiple of 5 based on the musicMap function. |
|||
localStorage.setItem('musicMap-'+mw.config.get('wgPageName'), b32.join('')); |
|||
// Use 32-bit string, because toString(64) is not supported in plain JS. |
|||
} |
|||
var b32 = bitstr.match(/.{1,5}/g).map(function (x) { |
|||
return parseInt(x, 2).toString(32); |
|||
}); |
|||
localStorage.setItem('musicMap-' + mw.config.get('wgPageName'), b32.join('')); |
|||
}; |
|||
MM.arrIdx = function (arr, i) { |
|||
if (i < 0) { |
|||
return arr[arr.length + i]; |
|||
} else { |
|||
return arr[i]; |
|||
} |
|||
}; |
|||
MM.toggleTrack = function (track, ids, state, $targets) { |
|||
if ($targets) { |
|||
$targets = $targets.add('.mw-kartographer-interactive'); |
|||
} else { |
|||
$targets = $('.mw-kartographer-interactive'); |
|||
} |
|||
if (state == undefined) { |
|||
state = MM.arrIdx(unlockedTracks, track) ? 0 : 1; |
|||
} |
|||
// update all maps when one map is clicked |
|||
$targets.each(function () { |
|||
for (var i in ids) { |
|||
var el = $(this).find('path.leaflet-interactive').eq(ids[i]); |
|||
if (state) el.addClass('unlocked');else el.removeClass('unlocked'); |
|||
} |
|||
}); |
|||
if (window.unlockedTracks) { |
|||
unlockedTracks[track] = state; |
|||
} |
|||
}; |
|||
MM.unlockTrack = function (e) { |
|||
if (!e.ctrlKey && !e.metaKey && !(MM.touch && e.type == 'selectstart')) { |
|||
// not ctrl+click, AND not cmd+click, AND not a long press touch event |
|||
return; // neither ctrl+click nor longpress |
|||
} |
|||
var map = $(e.target).closest('.mw-kartographer-interactive').data('musicMap'); |
|||
var i = $(e.target).index(); |
|||
var el = $('#musicMap [value~="' + i + '"]'); |
|||
MM.toggleTrack(parseInt(el.html()), el.val().split(' ').map(Number)); |
|||
MM.saveUnlocked(unlockedTracks); |
|||
map.closePopup(); // close popups on current map if there were any that were open. |
|||
e.preventDefault(); |
|||
e.stopPropagation(); |
|||
return false; |
|||
}; |
|||
MM.unlockAll = function (state) { |
|||
var btn = this; |
|||
btn.setDisabled(true); |
|||
// doing the track toggles ensures the button gets disabled properly before rendering the other DOM changes |
|||
setTimeout(function () { |
|||
$('#musicMap data').each(function () { |
|||
var track = parseInt(this.innerHTML); |
|||
var ids = this.value.split(' ').map(Number); |
|||
MM.toggleTrack(track, ids, state ? 1 : 0); |
|||
}); |
|||
MM.saveUnlocked(unlockedTracks); // save once at the end |
|||
}, 1); |
|||
setTimeout(function () { |
|||
// prevent doubleclicking the button: disable for 3 seconds |
|||
btn.setDisabled(false); |
|||
}, 3000); |
|||
}; |
|||
MM.musicMap = function (map) { |
|||
if ($('#musicMap').length == 0) return; |
|||
$target = $(map._container); |
|||
if ($target.data('musicMap')) return; // already added event handlers |
|||
$target.data('musicMap', map); |
|||
/* Local storage format: |
|||
MM.arrIdx = function(arr, i) { |
|||
* base32-encoded string |
|||
if (i < 0) { |
|||
* All songs with an associated cache ID will be in the array at that position |
|||
return arr[arr.length + i]; |
|||
* A gap to make this array's total length a multiple of 5 bits (since 2^5 = 32) |
|||
} else { |
|||
* A gap of 20 to prevent newly released songs from being marked as unlocked |
|||
return arr[i]; |
|||
* All N songs without a cache ID will be placed at the end, alphabetically sorted: |
|||
} |
|||
* with [length-1] being a, and [length-N] being z. |
|||
} |
|||
*/ |
|||
var ls = MM.getUnlocked(); |
|||
MM.toggleTrack = function(track, ids, state, $targets) { |
|||
var unlocked = [], |
|||
if ($targets) { |
|||
idless = []; |
|||
$targets = $targets.add('.mw-kartographer-interactive'); |
|||
$('#musicMap data').each(function () { |
|||
} else { |
|||
// rebuild local storage data based on the <data>, because the track list might have changed. |
|||
$targets = $('.mw-kartographer-interactive'); |
|||
var track = parseInt(this.innerHTML); |
|||
} |
|||
var ids = this.value.split(' ').map(Number); |
|||
if (state == undefined) { |
|||
if (MM.arrIdx(ls, track)) { |
|||
MM.toggleTrack(track, ids, 1, $target); |
|||
} |
|||
} |
|||
// update all maps when one map is clicked |
|||
if (track >= 0) { |
|||
$targets.each(function() { |
|||
unlocked[track] = MM.arrIdx(ls, track) ? 1 : 0; |
|||
for (var i in ids) { |
|||
} else { |
|||
var el = $(this).find('path.leaflet-interactive').eq(ids[i]); |
|||
idless[-track - 1] = MM.arrIdx(ls, track) ? 1 : 0; |
|||
if (state) el.addClass('unlocked'); else el.removeClass('unlocked'); |
|||
} |
|||
}); |
|||
if (window.unlockedTracks) { |
|||
unlockedTracks[track] = state; |
|||
} |
} |
||
}); |
|||
} |
|||
// gap of 5-(lengths%5) to make unlocked part a multiple of 5 bits (for base32enc). 20 empty slots as a spacer. |
|||
window.unlockedTracks = unlocked.concat(Array(5 - (unlocked.length + idless.length) % 5 + 20)).concat(idless); |
|||
MM.unlockTrack = function(e) { |
|||
MM.saveUnlocked(unlockedTracks); |
|||
if (!e.ctrlKey && !e.metaKey && !(MM.touch && e.type == 'selectstart')) { |
|||
$target.find('path').click(MM.unlockTrack).dblclick(function (e) { |
|||
// not ctrl+click, AND not cmd+click, AND not a long press touch event |
|||
if (e.ctrlKey || e.metaKey) { |
|||
return; // neither ctrl+click nor longpress |
|||
// ctrl+dblclick already gets handled by the click handler; don't fullscreen etc. |
|||
} |
|||
e.preventDefault(); |
|||
var map = $(e.target).closest('.mw-kartographer-interactive').data('musicMap'); |
|||
e.stopPropagation(); |
|||
} |
|||
var el = $('#musicMap [value~="'+i+'"]'); |
|||
}).on('touchstart', function (e) { |
|||
MM.toggleTrack(parseInt(el.html()), el.val().split(' ').map(Number)); |
|||
// Handle long-press touch events to unlock tracks: https://stackoverflow.com/q/66546226/1256925 |
|||
MM.saveUnlocked(unlockedTracks); |
|||
MM.touch = true; |
|||
map.closePopup(); // close popups on current map if there were any that were open. |
|||
}).on('touchend', function (e) { |
|||
e.preventDefault(); |
|||
MM.touch = false; |
|||
e.stopPropagation(); |
|||
}).on('selectstart', MM.unlockTrack); |
|||
return false; |
|||
} |
}; |
||
MM.playTrack = function (e) { |
|||
// This handler will trigger before the audioplayer.js event handler, because |
|||
MM.unlockAll = function(state) { |
|||
// this handler is tied to the map container, and that handler is tied to body. |
|||
var btn = this; |
|||
e.preventDefault(); |
|||
btn.setDisabled(true); |
|||
var $clone = $(e.target).clone(); // make a copy to put back in the tooltip |
|||
// doing the track toggles ensures the button gets disabled properly before rendering the other DOM changes |
|||
var parent = e.target.parentElement; // where to insert the copy |
|||
setTimeout(function() { |
|||
$('#music-playlist .player').html(''); // remove previous player |
|||
$('#musicMap data').each(function() { |
|||
$(e.target).appendTo('#music-playlist .player'); // move the song that will play to the play-box; audioplayer.js will replace this with <audio>. |
|||
var track = parseInt(this.innerHTML); |
|||
$clone.appendTo(parent); // put the link back in the tooltip |
|||
var ids = this.value.split(' ').map(Number); |
|||
}; |
|||
MM.toggleTrack(track, ids, state ? 1 : 0); |
|||
MM.initMap = function (map, fullscreen) { |
|||
}); |
|||
if (!$('#musicMap').length) return; |
|||
MM.saveUnlocked(unlockedTracks); // save once at the end |
|||
if (map instanceof Array) map = map[0]; |
|||
}, 1); |
|||
// wait for this map to be ready and make it a musicmap |
|||
setTimeout(function() { // prevent doubleclicking the button: disable for 3 seconds |
|||
map.on('kartographerisready', function () { |
|||
btn.setDisabled(false); |
|||
MM.musicMap(this); |
|||
}, 3000); |
|||
}); |
|||
} |
|||
if (fullscreen === false) { |
|||
// make the fullscreen maps that may be created also turn into musicmaps |
|||
MM.musicMap = function(map) { |
|||
L.Map.addInitHook(function () { |
|||
if ($('#musicMap').length == 0) return; |
|||
MM.initMap(this, true); |
|||
$target = $(map._container); |
|||
}); |
|||
if ($target.data('musicMap')) return; // already added event handlers |
|||
} |
|||
$target.data('musicMap', map); |
|||
if ($('#music-playlist .player').length == 0) { |
|||
$('#music-playlist').show().append('<div class="player">Click a link in a map tooltip to play that track.</div>'); |
|||
/* Local storage format: |
|||
$(map._container).on('click', 'a[href^="/w/File:"][href$=".ogg"]', MM.playTrack); |
|||
* base32-encoded string |
|||
$(map._container).on('click', 'a:not([href^="/w/File:"][href$=".ogg"])', function () { |
|||
* All songs with an associated cache ID will be in the array at that position |
|||
this.target = '_blank'; // open song links in new tab to prevent having to reload the map |
|||
* A gap to make this array's total length a multiple of 5 bits (since 2^5 = 32) |
|||
}); |
|||
* A gap of 20 to prevent newly released songs from being marked as unlocked |
|||
} |
|||
* All N songs without a cache ID will be placed at the end, alphabetically sorted: |
|||
if ($('.musicMap-buttons').length == 0) { |
|||
* with [length-1] being a, and [length-N] being z. |
|||
var unlockbtn = new OO.ui.ButtonWidget({ |
|||
*/ |
|||
flags: ['progressive'], |
|||
var ls = MM.getUnlocked(); |
|||
label: 'Unlock all tracks' |
|||
var unlocked = [], |
|||
}); |
|||
idless = []; |
|||
unlockbtn.on('click', MM.unlockAll.bind(unlockbtn, 1)); |
|||
$('#musicMap data').each(function() { |
|||
var lockbtn = new OO.ui.ButtonWidget({ |
|||
// rebuild local storage data based on the <data>, because the track list might have changed. |
|||
flags: ['destructive'], |
|||
var track = parseInt(this.innerHTML); |
|||
label: 'Lock all tracks' |
|||
var ids = this.value.split(' ').map(Number); |
|||
}); |
|||
if (MM.arrIdx(ls, track)) { |
|||
lockbtn.on('click', MM.unlockAll.bind(lockbtn, 0)); |
|||
MM.toggleTrack(track, ids, 1, $target); |
|||
// place in a wrapper and add to body |
|||
} |
|||
$('<div>').addClass('musicMap-buttons').append(unlockbtn.$element, lockbtn.$element).appendTo('#musicMap-info'); |
|||
if (track >= 0) { |
|||
} |
|||
unlocked[track] = MM.arrIdx(ls, track) ? 1 : 0; |
|||
return; |
|||
} else { |
|||
}; |
|||
idless[-track - 1] = MM.arrIdx(ls, track) ? 1 : 0; |
|||
} |
|||
}); |
|||
// gap of 5-(lengths%5) to make unlocked part a multiple of 5 bits (for base32enc). 20 empty slots as a spacer. |
|||
window.unlockedTracks = unlocked.concat(Array(5-((unlocked.length+idless.length) % 5) + 20)).concat(idless); |
|||
MM.saveUnlocked(unlockedTracks); |
|||
$target.find('path').click(MM.unlockTrack).dblclick(function(e) { |
|||
if (e.ctrlKey || e.metaKey) { |
|||
// ctrl+dblclick already gets handled by the click handler; don't fullscreen etc. |
|||
e.preventDefault(); |
|||
e.stopPropagation(); |
|||
} |
|||
}).on('touchstart', function(e) { // Handle long-press touch events to unlock tracks: https://stackoverflow.com/q/66546226/1256925 |
|||
MM.touch = true; |
|||
}).on('touchend', function(e) { |
|||
MM.touch = false; |
|||
}).on('selectstart', MM.unlockTrack); |
|||
} |
|||
MM.playTrack = function(e) { |
|||
// This handler will trigger before the audioplayer.js event handler, because |
|||
// this handler is tied to the map container, and that handler is tied to body. |
|||
e.preventDefault(); |
|||
var $clone = $(e.target).clone(); // make a copy to put back in the tooltip |
|||
var parent = e.target.parentElement; // where to insert the copy |
|||
$('#music-playlist .player').html(''); // remove previous player |
|||
$(e.target).appendTo('#music-playlist .player'); // move the song that will play to the play-box; audioplayer.js will replace this with <audio>. |
|||
$clone.appendTo(parent); // put the link back in the tooltip |
|||
} |
|||
MM.initMap = function(map, fullscreen) { |
|||
if (!$('#musicMap').length) return; |
|||
if (map instanceof Array) map = map[0]; |
|||
// wait for this map to be ready and make it a musicmap |
|||
map.on('kartographerisready', function() { MM.musicMap(this); }); |
|||
if (fullscreen === false) { |
|||
// make the fullscreen maps that may be created also turn into musicmaps |
|||
L.Map.addInitHook(function() {MM.initMap(this, true);}) |
|||
} |
|||
if ($('#music-playlist .player').length == 0) { |
|||
$('#music-playlist').show().append('<div class="player">Click a link in a map tooltip to play that track.</div>'); |
|||
$(map._container).on('click', 'a[href^="/w/File:"][href$=".ogg"]', MM.playTrack) |
|||
$(map._container).on('click', 'a:not([href^="/w/File:"][href$=".ogg"])', function() { |
|||
this.target = '_blank'; // open song links in new tab to prevent having to reload the map |
|||
}); |
|||
} |
|||
if ($('.musicMap-buttons').length == 0) { |
|||
var unlockbtn = new OO.ui.ButtonWidget( { |
|||
flags: [ 'progressive' ], |
|||
label: 'Unlock all tracks', |
|||
} ); |
|||
unlockbtn.on('click', MM.unlockAll.bind(unlockbtn, 1)); |
|||
var lockbtn = new OO.ui.ButtonWidget( { |
|||
flags: [ 'destructive' ], |
|||
label: 'Lock all tracks', |
|||
} ); |
|||
lockbtn.on('click', MM.unlockAll.bind(lockbtn, 0)); |
|||
// place in a wrapper and add to body |
|||
$('<div>').addClass('musicMap-buttons').append(unlockbtn.$element, lockbtn.$element).appendTo('#musicMap-info'); |
|||
} |
|||
return; |
|||
} |
|||
// hook init to maps loading |
// hook init to maps loading |
Latest revision as of 12:06, 20 October 2024
"use strict";
/* Music map
* Generates an interactive music map that has toggleable polygons on it. Can be used as a 'checklist' to track music track unlock progression.
* See [[Map:Music tracks]]
*/
var MM = {};
MM.touch = false;
MM.getUnlocked = function () {
var ls = localStorage.getItem('musicMap-' + mw.config.get('wgPageName'));
if (!ls) return [];
// map characters back to numbers and convert to
var bitstr = Array.prototype.map.call(ls, function (x) {
// go through each character in the string
var str = '00000' + parseInt(x, 32).toString(2); // parse back to bit string with sufficient leading zeroes to turn it into 5 bits long
return str.slice(-5); // the actual bits that were parsed, plus any leading zeroes if needed
}).join(''); // convert array of bitstrings to single long strong
// convert bitstring back into an array of bools
var bits = Array.prototype.map.call(bitstr, function (x) {
return parseInt(x);
});
return bits;
};
MM.saveUnlocked = function (arr) {
var bits = [];
for (var i = 0; i < arr.length; i++) bits[i] = arr[i] ? 1 : 0; // fill in empty array elements
var bitstr = bits.join(''); // array in bit representation
// Split up in chunks of 5 bits. The array length should be a multiple of 5 based on the musicMap function.
// Use 32-bit string, because toString(64) is not supported in plain JS.
var b32 = bitstr.match(/.{1,5}/g).map(function (x) {
return parseInt(x, 2).toString(32);
});
localStorage.setItem('musicMap-' + mw.config.get('wgPageName'), b32.join(''));
};
MM.arrIdx = function (arr, i) {
if (i < 0) {
return arr[arr.length + i];
} else {
return arr[i];
}
};
MM.toggleTrack = function (track, ids, state, $targets) {
if ($targets) {
$targets = $targets.add('.mw-kartographer-interactive');
} else {
$targets = $('.mw-kartographer-interactive');
}
if (state == undefined) {
state = MM.arrIdx(unlockedTracks, track) ? 0 : 1;
}
// update all maps when one map is clicked
$targets.each(function () {
for (var i in ids) {
var el = $(this).find('path.leaflet-interactive').eq(ids[i]);
if (state) el.addClass('unlocked');else el.removeClass('unlocked');
}
});
if (window.unlockedTracks) {
unlockedTracks[track] = state;
}
};
MM.unlockTrack = function (e) {
if (!e.ctrlKey && !e.metaKey && !(MM.touch && e.type == 'selectstart')) {
// not ctrl+click, AND not cmd+click, AND not a long press touch event
return; // neither ctrl+click nor longpress
}
var map = $(e.target).closest('.mw-kartographer-interactive').data('musicMap');
var i = $(e.target).index();
var el = $('#musicMap [value~="' + i + '"]');
MM.toggleTrack(parseInt(el.html()), el.val().split(' ').map(Number));
MM.saveUnlocked(unlockedTracks);
map.closePopup(); // close popups on current map if there were any that were open.
e.preventDefault();
e.stopPropagation();
return false;
};
MM.unlockAll = function (state) {
var btn = this;
btn.setDisabled(true);
// doing the track toggles ensures the button gets disabled properly before rendering the other DOM changes
setTimeout(function () {
$('#musicMap data').each(function () {
var track = parseInt(this.innerHTML);
var ids = this.value.split(' ').map(Number);
MM.toggleTrack(track, ids, state ? 1 : 0);
});
MM.saveUnlocked(unlockedTracks); // save once at the end
}, 1);
setTimeout(function () {
// prevent doubleclicking the button: disable for 3 seconds
btn.setDisabled(false);
}, 3000);
};
MM.musicMap = function (map) {
if ($('#musicMap').length == 0) return;
$target = $(map._container);
if ($target.data('musicMap')) return; // already added event handlers
$target.data('musicMap', map);
/* Local storage format:
* base32-encoded string
* All songs with an associated cache ID will be in the array at that position
* A gap to make this array's total length a multiple of 5 bits (since 2^5 = 32)
* A gap of 20 to prevent newly released songs from being marked as unlocked
* All N songs without a cache ID will be placed at the end, alphabetically sorted:
* with [length-1] being a, and [length-N] being z.
*/
var ls = MM.getUnlocked();
var unlocked = [],
idless = [];
$('#musicMap data').each(function () {
// rebuild local storage data based on the <data>, because the track list might have changed.
var track = parseInt(this.innerHTML);
var ids = this.value.split(' ').map(Number);
if (MM.arrIdx(ls, track)) {
MM.toggleTrack(track, ids, 1, $target);
}
if (track >= 0) {
unlocked[track] = MM.arrIdx(ls, track) ? 1 : 0;
} else {
idless[-track - 1] = MM.arrIdx(ls, track) ? 1 : 0;
}
});
// gap of 5-(lengths%5) to make unlocked part a multiple of 5 bits (for base32enc). 20 empty slots as a spacer.
window.unlockedTracks = unlocked.concat(Array(5 - (unlocked.length + idless.length) % 5 + 20)).concat(idless);
MM.saveUnlocked(unlockedTracks);
$target.find('path').click(MM.unlockTrack).dblclick(function (e) {
if (e.ctrlKey || e.metaKey) {
// ctrl+dblclick already gets handled by the click handler; don't fullscreen etc.
e.preventDefault();
e.stopPropagation();
}
}).on('touchstart', function (e) {
// Handle long-press touch events to unlock tracks: https://stackoverflow.com/q/66546226/1256925
MM.touch = true;
}).on('touchend', function (e) {
MM.touch = false;
}).on('selectstart', MM.unlockTrack);
};
MM.playTrack = function (e) {
// This handler will trigger before the audioplayer.js event handler, because
// this handler is tied to the map container, and that handler is tied to body.
e.preventDefault();
var $clone = $(e.target).clone(); // make a copy to put back in the tooltip
var parent = e.target.parentElement; // where to insert the copy
$('#music-playlist .player').html(''); // remove previous player
$(e.target).appendTo('#music-playlist .player'); // move the song that will play to the play-box; audioplayer.js will replace this with <audio>.
$clone.appendTo(parent); // put the link back in the tooltip
};
MM.initMap = function (map, fullscreen) {
if (!$('#musicMap').length) return;
if (map instanceof Array) map = map[0];
// wait for this map to be ready and make it a musicmap
map.on('kartographerisready', function () {
MM.musicMap(this);
});
if (fullscreen === false) {
// make the fullscreen maps that may be created also turn into musicmaps
L.Map.addInitHook(function () {
MM.initMap(this, true);
});
}
if ($('#music-playlist .player').length == 0) {
$('#music-playlist').show().append('<div class="player">Click a link in a map tooltip to play that track.</div>');
$(map._container).on('click', 'a[href^="/w/File:"][href$=".ogg"]', MM.playTrack);
$(map._container).on('click', 'a:not([href^="/w/File:"][href$=".ogg"])', function () {
this.target = '_blank'; // open song links in new tab to prevent having to reload the map
});
}
if ($('.musicMap-buttons').length == 0) {
var unlockbtn = new OO.ui.ButtonWidget({
flags: ['progressive'],
label: 'Unlock all tracks'
});
unlockbtn.on('click', MM.unlockAll.bind(unlockbtn, 1));
var lockbtn = new OO.ui.ButtonWidget({
flags: ['destructive'],
label: 'Lock all tracks'
});
lockbtn.on('click', MM.unlockAll.bind(lockbtn, 0));
// place in a wrapper and add to body
$('<div>').addClass('musicMap-buttons').append(unlockbtn.$element, lockbtn.$element).appendTo('#musicMap-info');
}
return;
};
// hook init to maps loading
mw.hook('wikipage.maps').add(MM.initMap);