// ==UserScript==
// @name yalb Unlocker
// @namespace https://greasyfork.org/scripts/400929-yalb-unlocker
// @version 0.6
// @description Enables yalb premium buttons
// @author Djamana
// @match https://*.yalp.io/*
//chords/*
// @grant none
// @run-at document-start //https://tampermonkey.net/documentation.php#_run_at
/*
* Enables buttons "change speed" without the need to login
* Enables buttons "loop", "transpose +/-", "Download MIDI", "tuner" without the need for premium
* "Download PDF", and Upload mp3 is currently not working
How does this work technically:
Well nearly all of the yalb-premium functionality is already there,
what blocks it from been used is that the buttons at the UI (html-page)
don't have the correct IDs or Class attribute.
So later yalb just don't attach the corresponding handler to it.
So the main mission here is the restore correct IDs or class attributes before
yalb'S document_ready() event handler is executed.
Hints for dealing with yalb.min.js:
* search for 'create_google_tag' in the 'formated' JavaScript sources.
It will bring you to all first level function of major interest
and somehow compensate the missing comment and real function names because of the minified source.
Example:
create_google_tag("play", "user_id " + yalpB)
*/
// ==/UserScript==
'use strict';
var appVersion = "0.6"
// Testpage:
// https://yalp.io/chords/gerhard-schoene-der-laden-efc3
//
// Tested with https://www.yalp.io/js/yalp.min.full.js
// VERSION: 1.18.0 DATE: 2015-09-05
//
// History
// 0.6 Mai 2020
// * Improved no login required - use of mutation observer to exchanges javascript files on the fly
var Conf_chordsContainerHeight = 1280
var Conf_FakeLogin = true
// TODO find yalp correct mp3 Upload url !
var Conf_UploadUrl = "//www.yalp.io/submit.php"
var Conf_DownloadUrl = "#" //"//www.yalp.io/submit_yalp.php"
//https://www.yalp.io/index3.php
//https://www.yalp.io/lesson/booking_attempt/slot-id/booktutor.idsong?_=1587026316058
//https://www.yalp.io/lesson/pay_with_stripe/e4
//https://www.yalp.io/store_event
//https://www.yalp.io/store_interface_language
//https://www.yalp.io/lesson/get_tutors_availability?user%5Btime_zone%5D=Europe%2FBerlin&language=2&instrument_category=guitar&played_instrument_category=guitar&page=1&from_date=2020-04-16%2000:00&to_date=2020-04-16%2023:59&origin=undefined&tutorid=undefined&_=1587026316059
//https://www.yalp.io/searchemail
// force to load this jQuery - change to false to use jQuery yalp provide
var Conf_jQueryToUse_Url = false //"//ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.js"
// Blocks loading addtoany.com Facebook and Co social buttons
var Conf_OptFixes_NoSocialbuttons = true
var Conf_OptFixes_NoTracking = true
// Check if a string contains the given RE-pattern
String.prototype.contains = function(RegExpMatchPattern) {
var matches = this.match( RegExpMatchPattern )
matches = matches &&
matches.length
return !!matches
}
///^\/chords|playlist/
var isChordsUrl = !!document.location.pathname.contains(/^\/chords|playlist/)
function otherFixes() {
// Beautify
try {
$("#beats_chords_container").height( Conf_chordsContainerHeight )
$('#dynamic_chords_container').removeClass('hidden')
$('#dynamic_demo_container').addClass('hidden')
} catch (exception) {
console.warn('Error on enlarging chords container', exception);
}
// Remove 12 Secs delayed message after hitting 'play'
// it's triggered only on first play - so
// set numbersOfPlay count to a higher value than 0 will disable this
// Note: Cookies depends on main.min.js
try {
//$("#ModalDailyMax").remove()
if ( typeof Cookies.get("user_id") == "undefined" )
Cookies.set( "user_id", "DummyUser" )
Cookies.set( "nplay", "1" )
} catch (exception) {
console.warn('Error on disabling delayed message after play', exception);
}
}
function removeAds() {
// Remove Premium ads
try {
var PremiumAds = $("a[href$='yalp.io/premium'")
PremiumAds.remove()
console.log( PremiumAds.length + ' Premium ads deleted.' );
var PremiumAds2 = $("a[href$='yalp.io/pricing'")
var MixerOnlyAd = PremiumAds2 && PremiumAds2.parent().parent()// $(".jumbotron:not( [id] )")
MixerOnlyAd.remove()
} catch (exception) {
console.warn('Error on Remove premium ads', exception);
}
// No Tracking
// Overwrite main.min.js ! create_google_tag() with dummy function
if (Conf_OptFixes_NoTracking)
create_google_tag = my_create_google_tag
}
function my_create_google_tag(action, userID) {
console.log ( "Intercepted TrackingEvent: " + action )
}
function RestoreButtonIDs() {
//debugger
// Extend JQuery alittle ...
$.fn.replaceClass = function (pFromClass, pToClass) {
return this.removeClass(pFromClass).addClass(pToClass);
};
try {
// .. and yes we are logged in
// so that will unlock the "speed buttons"
if (Conf_FakeLogin) {
if ( logged == false ) {
logged = true
is_editor = true
//$("#loaded_version")
$(".navbar-text")
.text("yalb Unlocker: Changes are NOT saved - since FAKE login is active!")
// AddYalb_full_js()
}
}
} catch (exception) {
console.warn('Error on FakeLogin', exception);
}
var buttons_locked = $(".notify-premium") //[aria-label]
buttons_locked.removeClass("notify-premium")
var premiumItemsCount = buttons_locked.length
console.log ( premiumItemsCount + " premium buttons found." )
if (premiumItemsCount <= 4 ) {
alert( "yalb Unlocker Script - failed. " +
"Whops just " + premiumItemsCount +
" ?" +
"There is something wrong - to less premium items found.")
}
// KEY: "aria-label" : VALUE: new ID and CLASS Name to be added
var newID_Translator = {
"Add to Playlist": "add-playlist",
"Transpose down" : "transpose-minus" ,
"Transpose up" : "transpose-plus" ,
"start tuner" : "start-tuner" ,
"generate midi" : "generate-midi" ,
"download-midi" : "generate-midi" ,
"enable loop" : "loop" ,
"Print pdf" : "print-grid",
"unlimited-pdf-alert" : "print-grid"
}
var stats_IDsApplied = 0
//debugger
buttons_locked.each( function() {
try {
// Get Attibute "aria-label" or if not exist "data-alert"
var button_label = $( this ).attr("aria-label") ||
$( this ).attr("data-alert")
//button_label = buttons_locked.getAttribute("data-alert")
//newID = newID.match ("(?:unlimited-)(.*)(?:-alert)") [1]
var newID = newID_Translator[ button_label ]
if (newID) {
// add ID
this.id = newID
stats_IDsApplied++;
// add class
$( this ).addClass(newID)
}
console.log ( (newID ? "#" + stats_IDsApplied + " New" : "NOT APPLIED") +
" ID: #" + newID + " <= '" + button_label + "'")
} catch (exception) {
console.warn('Error on update button ', exception);
}
});
console.log ( stats_IDsApplied + " button patched.")
buttons_locked.removeAttr("data-alert" )
buttons_locked.removeAttr("data-toggle")
buttons_locked.removeAttr("data-target")
// reenable Upload
// Restore Upload-click
$(".no-upload-button")
.replaceClass( "no-upload-button", "upload-button")
// reenable drop mp3 files
$('body').addClass("drop")
// Recreate Upload button
// https://github.com/blueimp/jQuery-File-Upload/wiki/Basic-plugin
var form = $("<form >"); //id=fileupload
form
.append(
'<input '+
' type = "file" '+
' id = fileupload '+
' data-url="' + Conf_UploadUrl + '"' +
' name="files[]" '+
'>')
$('body').append(form);
//.attr("id","fileupload")
// Print PDF is not working
// that's the yalb - code this will trigger:
//$("#print-form [name='token']" ).val(token)
//$("#print-form [name='transpose']" ).val(yalpx)
//$("#print-form" ).submit()
// The form 'print-form' is missing
var printForm = $(
"<form " +
"id = 'print-form' " +
"action = '" + Conf_DownloadUrl + "'" +
"method = 'post' >"
); //id=fileupload
printForm
.append('<input type="button" name="token">')
.append('<input type="button" name="transpose">');
"submit_yalp.php"
$('body').append(printForm);
// $('[aria-label="Print pdf"]')
// .addClass("print-grid")
// Enable Tuner Handler
// var tuner = document.getElementById("start-tuner")//"start-tuner")
// console.log( tuner.id );
// debugger
//tuner.addEventListener("click", yalpza )
// Enable Midi-Download
// must satisfy $(".generate-midi") to match with yalb handler ".click(function() {..."
// Not needed anymore since now it's also handled by the 'ID_Translator' above
// $('[aria-label="generate midi"]')
// .addClass("generate-midi")
// $('[aria-label="start tuner"]')
// .attr("name","transform")
// it is important to trigger document_ready handler of yalb which will connect eventhandler to the buttons
// if (!$(document).hasClass("ready")) {
// $(document).triggerHandler("ready");
// $(document).addClass("ready")
// }
}
///////////////////////////
// RestoreHandlers
// Purposes:
// some yalb bugfixing
function RestoreHandlers () {
//Enable Tuner Handler
// document.getElementById("start-tuner").addEventListener("click", yalpza)
$("button#start-tuner").click(yalpza)
// Note that there are 2 Buttons - one is hidden in the "..." Menu (there is transpose and speed)
$(".navbar-text")
.text("yalb Unlocker " + appVersion)
}
///////////////////////////
// M A I N loader
// Purposes:
// * Run RestoreButtonIDs ( to restore/set correct GUI ID )
// BEFORE yalb document_ready()
// * Some Yalb Bugfixing ( start-Tuner handler is not set correct)
function Main_JQueryReady() {
// That will be called when the document is ready...
//$( document ).ready(function() {
//$( RestoreHandlers )
//$(document).on("ready", RestoreButtonIDs)
$( document ).ready( (event) => {
// console.log( "document loaded" );
// Restore Buttons IDs and Classes for premium items
// .. so that yalb will install most of the handler in its document_ready() event handler
if (isChordsUrl) {
RestoreButtonIDs()
otherFixes()
}
removeAds()
});
$( window ).on( "load", (event) => {
console.log( "window loaded" );
RestoreHandlers();
});
$( window ).on( "DOMContentLoaded", (event) => {
console.log('DOM fully loaded and parsed');
});
}
///////////////////////////
// liteToFull
// Hook loading js and css files
// and do manipulation to it (exchange, drop...)
function liteToFull( searchPattern, attrib, replaceWith, postfix ) {
new MutationObserver( ( mutations , observer ) => {
//mutations.forEach( (mutation) => {
// console.log( mutation.target );
//});
// let script = mutations[0].addedNodes[0]
// if ( script.src.match("addtoany.com")!==null ) script.remove()
var tmp
(tmp = document.querySelector( "script[src*='addtoany.com']" ) ) &&
tmp.remove()
const yalp_lite = document.querySelector( "[" + attrib + "$='" + searchPattern + "']" );//'script[src*="jquery"]');
if (yalp_lite) {
var yalp_url = yalp_lite[attrib]
var yalp_full = yalp_url.replace( replaceWith , replaceWith + postfix )
yalp_lite[attrib] = yalp_full
console.log ( "NEW " + attrib + " '" + yalp_full + "' <= '" + yalp_url + "'" )
//yalp_lite.remove();
// We've done what we needed to do, no need for the MutationObserver anymore:
observer.disconnect();
}
})
.observe( document.documentElement , // target ( Type: Node )
{ childList: true, subtree: true, target: "script" } // options ( Type: MutationObserverInit-Object)
);// ( other option: attributes[OldValue] ,characterData[OldValue], attributeFilter)
}
///////////////////////////
// M A I N loader
// Purposes:
// * Exchange js and css files for full version
// * Run Main when Jquery is ready
(function() {
// debugger
/*
let script ;
script = document.createElement('script');
script.src = "//www.yalp.io/js/yalp.min.full.js";
document.body.appendChild(script); // execute the script
*/
// Load full version
liteToFull( "yalp.min.js" , "href", ".min.", "full." )
liteToFull( "yalp.min.js" , "src", ".min.", "full." )
liteToFull( "style.min.css" , "href", ".min.", "full." )
// No Ads
liteToFull( "ads.min.js" , "src", ".min.", "" )
// Anti-blocker disable
var tmp = document.querySelector("head")
tmp && tmp.setAttribute("id","yalp-FfCUXEjbzHqO")
// in case th first pattern changed the following will break Anti-blocker dectection script
tmp = document.querySelector('#ModalAdUnblock')
tmp && tmp.remove()
// Wait for jquery
let attrib = "src"
var run = false
new MutationObserver( ( mutations , observer ) => {
if (run) {
Main_JQueryReady()
// We've done what we needed to do, no need for the MutationObserver anymore:
observer.disconnect();
}
const jQuery = document.querySelector( 'script[' + attrib + '*="jquery"]');
if (jQuery) {
// Replace jQuery with other one ?
if (Conf_jQueryToUse_Url) {
let jQuery = jQuery[attrib]
jQuery[attrib] = Conf_jQueryToUse_Url
}
run = true
}
})
.observe( document.documentElement, { childList: true, subtree: true } );
}) ();