//
// Written by Glenn Wiking
// Script Version: 0.2.25
//
//
// ==UserScript==
// @name Shuttle SEO Master
// @namespace SSM
// @description Eye-friendly magic in your browser for Shuttle API
// @version 0.2.25
// @icon https://dlw0tascjxd4x.cloudfront.net/assets/img/symbol.svg
// @include *.shuttle.be/admin/*
// @include *app.shuttle.be/*
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js
// @require https://cdn.jsdelivr.net/npm/js-cookie@2/src/js.cookie.min.js
// ==/UserScript==
$(window).on("load", function() {
// Documentation
let documentation =
"<p>SEO Master is a tool that lets you more easily control <strong>SEO essentials</strong> within Shuttle. Its features are documented in more detail below.</p>"
+
"<p>The primary feature of SEO Master is the <strong>SEO button</strong>. When enabled, this button will appear in a WYSIWYG editors throughout Shuttle. Its behavior can be mapped in settings.</p>"
+
"<p>You can also map this behavior to selecting an item from the Styles dropdown menu.</p>"
+
"<br><p><img src='https://shuttle-storage.s3.amazonaws.com/addons/Flavor/SEO-Button-Doc1.png'></p>"
+
"<p>It's also possible to disable the SEO button, in which case the mapped behavior will be automatically applied to all WYSIWYG fields when a data entry or text editor in pages is opened. If you want per-context control, the SEO button is what you're after.</p>"
+
"<p>Currently supported features:</p>"
+
"<ul>"
+
"<li>Replacing <code>span.custom-style-h<em>x</em></code> with <code>h<em>x</em>.custom-style-h<em>x</em></code> in the current editor.</li>"
+
"</ul>"
+
"<br>"
+
"<p>More features coming soon!</p>"
// Apply CKE
function apply(cke) {
console.log( cke );
cke.addClass("affected");
// Appearance
cke.find(".cke_top").append("<span id='cke_seo' class='cke_toolbar' role='toolbar'></span>");
cke.find("#cke_seo").append("<span class='cke_toolbar_start'></span><span class='cke_toolgroup hidden'></span><span class='cke_toolbar_end'></span>");
cke.find("#cke_seo .cke_toolgroup").append("<a id='cke_seo_button' class='cke_button cke_button_seo cke_button_off' title='SEO' tabindex='-1' hideonfocus='true' role='button' onkeypress='return false;'></a>");
cke.find("#cke_seo_button").append("<span class='cke_button_icon cke_button__seo_icon'></span>");
cke.find("#cke_seo_button").append("<span class='cke_button_label cke_button__seo_label'></span>");
cke.find(".cke_button__seo_label").text("SEO");
if ( localStorage.getItem("SEO-button-active") == null ) {
localStorage.setItem("SEO-button-active", true);
} else {
if ( localStorage.getItem("SEO-button-active") == "true" ) {
$(".cke_button_seo").parent().removeClass("hidden");
}
}
console.log( "GG" );
// Headings Clean (SEO Button)
function clean() {
cke.find(".cke_wysiwyg_frame").contents().find("span[class^='custom-style-h']").each( function() {
let classes = $(this).attr("class").split(" ");
let h;
let content;
for ( i = 0; i < classes.length; i++ ) {
console.log( classes[i] );
if ( classes[i].includes("custom-style-h") ) {
h = classes[i].replace("custom-style-h","");
content = $(this).html();
}
}
//$(this).parent().prepend("<h"+ h +" class='" + classes.toString().replace(/,/g," ") + "'>"+ content +"</h"+ h +">");
$(this).parent("p").before("<h"+ h +" class='" + classes.toString().replace(/,/g," ") + "'>"+ content +"</h"+ h +">");
//$(this).addClass("gg");
console.log( classes.toString().replace(/,/g," ") );
console.log( $(this).parent() );
$(this).parent("p").remove();
});
}
if ( localStorage.getItem("SEO-button-active") != null ) {
if ( localStorage.getItem("SEO-button-active") == "false" ) { clean(); };
};
console.log("G");
cke.find("#cke_seo_button").on("click", clean);
cke.find(".cke_toolbar a.cke_combo_button[aria-haspopup]").on("click", function() {
cke.addClass("style-open");
console.log("Styles");
});
};
/*let applyOnStyles = setInterval( function() {
$(".cke_combopanel[style*='z-index: 10001']").on("click", function() {
if ( localStorage.getItem("SEO-style-apply-active") != null ) {
if ( localStorage.getItem("SEO-style-apply-active") == "false" ) {
setTimeout( function() {
clean();
}, 200);
};
};
});
$(".cke_combopanel[style*='z-index: 10001']").attr("g","gg");
$("[g]").on("click", function() {
console.log("GGG");
});
if ( $(".cke_combopanel[style*='z-index: 10001'].affected").length == 1 ) { clearInterval(applyOnStyles) }
console.log( $(".cke_combopanel[style*='z-index: 10001']:not(.affected)") );
//$(".cke_combopanel[style*='z-index: 10001']").addClass("affected");
}, 500);*/
// Apply Settings
let iconCog = "<svg viewBox='410.9 287.6 20 20'><path d='M429,297.6c0-1.2,0.8-2.2,1.9-2.9c-0.2-0.7-0.5-1.4-0.8-2c-1.3,0.3-2.3-0.2-3.2-1.1c-0.9-0.9-1.2-1.9-0.8-3.2c-0.6-0.3-1.3-0.6-2-0.8c-0.7,1.2-1.9,1.9-3.1,1.9 c-1.2,0-2.5-0.8-3.1-1.9c-0.7,0.2-1.4,0.5-2,0.8c0.3,1.3,0.1,2.3-0.8,3.2 c-0.9,0.9-1.9,1.4-3.2,1.1c-0.3,0.6-0.6,1.3-0.8,2c1.2,0.7,1.9,1.7,1.9,2.9c0,1.2-0.8,2.5-1.9,3.1c0.2,0.7,0.5,1.4,0.8,2 c1.3-0.3,2.3-0.1,3.2,0.8c0.9,0.9,1.2,1.9,0.8,3.2c0.6,0.3,1.3,0.6,2,0.8c0.7-1.2,1.9-1.9,3.1-1.9c1.2,0,2.5,0.8,3.1,1.9 c0.7-0.2,1.4-0.5,2-0.8c-0.3-1.3-0.1-2.3,0.8-3.2c0.9-0.9,1.9-1.4,3.2-1.1c0.3-0.6,0.6-1.3,0.8-2C429.8,299.9,429,298.9,429,297.6z M420.9,302c-2.4,0-4.3-1.9-4.3-4.3c0-2.4,1.9-4.3,4.3-4.3c2.4,0,4.3,1.9,4.3,4.3C425.3,300,423.3,302,420.9,302z'></path></svg>"
let iconLog = "<svg viewBox='0 0 20 20'><g><path d='M16.3,8.6H8c-0.8,0-0.9,0.6-0.9,1.4s0.1,1.4,0.9,1.4h8.3c0.8,0,0.9-0.6,0.9-1.4S17.1,8.6,16.3,8.6z M19.1,15.7H8 c-0.8,0-0.9,0.6-0.9,1.4s0.1,1.4,0.9,1.4h11.1c0.8,0,0.9-0.6,0.9-1.4S19.9,15.7,19.1,15.7z M8,4.3h11.1c0.8,0,0.9-0.6,0.9-1.4 s-0.1-1.4-0.9-1.4H8c-0.8,0-0.9,0.6-0.9,1.4S7.2,4.3,8,4.3z M3.4,8.6H0.9C0.1,8.6,0,9.2,0,10s0.1,1.4,0.9,1.4h2.6 c0.8,0,0.9-0.6,0.9-1.4S4.2,8.6,3.4,8.6z M3.4,15.7H0.9c-0.8,0-0.9,0.6-0.9,1.4s0.1,1.4,0.9,1.4h2.6c0.8,0,0.9-0.6,0.9-1.4 S4.2,15.7,3.4,15.7z M3.4,1.4H0.9C0.1,1.4,0,2.1,0,2.9s0.1,1.4,0.9,1.4h2.6c0.8,0,0.9-0.6,0.9-1.4S4.2,1.4,3.4,1.4z'></path></g></svg>"
let iconPage = "<svg viewBox='0 0 384 512'><path d='M224 136V0H24C10.7 0 0 10.7 0 24v464c0 13.3 10.7 24 24 24h336c13.3 0 24-10.7 24-24V160H248c-13.2 0-24-10.8-24-24zm64 236c0 6.6-5.4 12-12 12H108c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h168c6.6 0 12 5.4 12 12v8zm0-64c0 6.6-5.4 12-12 12H108c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h168c6.6 0 12 5.4 12 12v8zm0-72v8c0 6.6-5.4 12-12 12H108c-6.6 0-12-5.4-12-12v-8c0-6.6 5.4-12 12-12h168c6.6 0 12 5.4 12 12zm96-114.1v6.1H256V0h6.1c6.4 0 12.5 2.5 17 7l97.9 98c4.5 4.5 7 10.6 7 16.9z'/></svg>"
let iconImport = "<svg viewBox='0 0 20 20'><path d='M15,7h-3V1H8v6H5l5,5L15,7z M19.338,13.532c-0.21-0.224-1.611-1.723-2.011-2.114C17.062,11.159,16.683,11,16.285,11h-1.757 l3.064,2.994h-3.544c-0.102,0-0.194,0.052-0.24,0.133L12.992,16H7.008l-0.816-1.873c-0.046-0.081-0.139-0.133-0.24-0.133H2.408 L5.471,11H3.715c-0.397,0-0.776,0.159-1.042,0.418c-0.4,0.392-1.801,1.891-2.011,2.114c-0.489,0.521-0.758,0.936-0.63,1.449 l0.561,3.074c0.128,0.514,0.691,0.936,1.252,0.936h16.312c0.561,0,1.124-0.422,1.252-0.936l0.561-3.074 C20.096,14.468,19.828,14.053,19.338,13.532z'></path></svg>"
let iconHelp = "<svg x='0px' y='0px' viewBox='0 0 20 20' xml:space='preserve'><g><path d='M19,10c0,5-4,9-9,9c-5,0-9-4-9-9c0-5,4-9,9-9C15,1,19,5,19,10z M14.1,7.4c0-2.2-1.9-3.8-4.1-3.8c-2.2,0-3.8,1.9-3.8,1.9 L7.8,7c0,0,1.1-1.1,2.2-1.1s1.9,0.8,1.9,1.5c0,1.5-3,1.5-3,3.8v0.7h2.3v-0.4C11.1,10.4,14.1,10.4,14.1,7.4z M11.5,15.3 c0-0.8-0.7-1.5-1.5-1.5s-1.5,0.7-1.5,1.5c0,0.8,0.7,1.5,1.5,1.5S11.5,16.1,11.5,15.3z'></path></g></svg>";
function settings() {
let panel = $("a[data-active-id='cookies']").closest(".shuttle-Panel-inner");
panel.append("<div class='shuttle-SubNav shuttle-SubNav--alt heading-addons'></div>");
panel.find(".heading-addons").append("<div class='shuttle-SubNav-title'>Add-ons</div>");
panel.find(".heading-addons").append("<ul class='shuttle-SubNav-wrapItems Nav Nav--stacked'></ul>");
}
function settingsSEO() {
let panel = $("a[data-active-id='cookies']").closest(".shuttle-Panel-inner");
let menuItems = [];
menuItems.push({"label":"Settings", "icon":iconCog}, {"label":"Documentation", "icon":iconPage}, {"label":"Changelog", "icon":iconLog}, {"label":"Import/export Settings", "icon":iconImport});
$(".heading-addons ul").append("<li class='shuttle-SubNav-item'><a class='shuttle-SubNav-itemTarget settings-seo' href=''><div class='Icon'></div>SEO Master</a></li>");
$(".heading-addons .settings-seo .Icon").append( iconCog );
$(".settings-seo").on("click", function() {
if ( $(".panel-seo").length == 0 ) {
$(".shuttle-Panel--mainNav").nextAll(".shuttle-Panels:visible").append("<div class='shuttle-Panels shuttle-Panels--nested panel-seo' style='margin-left: 250px; top: 0px;'></div>")
$(".panel-seo").append("<div class='shuttle-Panel shuttle-Panel--withTabs'></div>");
$(".panel-seo > .shuttle-Panel").append("<div class='shuttle-Panel-inner' style='top: 115px; bottom: 0px;'></div>");
$(".panel-seo > .shuttle-Panel").append("<div class='shuttle-Panel-header'></div>");
} else {
$(".panel-seo").parent().find("> .shuttle-Panels--nested:not(.panel-seo)").hide();
}
// Open Settings
panel.find(".is-selected:visible").removeClass("is-selected");
$(this).addClass("is-selected");
// Fill with Appliccable Settings
if ( $(".panel-seo.affected").length == 0 ) {
$(".panel-seo .shuttle-Panel-header").append("<div class='shuttle-Panel-title u-textTruncate'>SEO Master</div>");
$(".panel-seo .shuttle-Panel-header").append("<div class='shuttle-Panel-toolbar'></div>");
$(".panel-seo .shuttle-Panel-header").append("<ul class='PanelNav Nav'></ul>");
for ( i = 0; i < menuItems.length; i++ ) {
let menuLabel = menuItems[i].label;
let menuIcon = menuItems[i].icon;
$(".panel-seo .shuttle-Panel-header ul").append("<li class='PanelNav-item'><a class='PanelNav-itemTarget' href='' data-tab='"+ menuLabel +"'><div class='Icon'>" + menuIcon + "</div>"+ menuLabel +"</a></li>");
}
$(".panel-seo .shuttle-Panel-header ul a").on("click", function(e) {
e.preventDefault();
$(".panel-seo .shuttle-Panel-header ul a").removeClass("is-selected");
$(this).addClass("is-selected");
});
$(".panel-seo").addClass("affected");
}
// Settings Menus
$(".PanelNav-itemTarget").on("click", function() {
let which = $(this).attr("data-tab");
function addPanel() { // Clean up and Add Panel
$(".panel-seo .shuttle-SEO, .panel-seo .Form-item--actions").remove();
$(".panel-seo .shuttle-Panel-inner").append("<div class='shuttle-SEO'></div>");
};
console.log(which);
// Settings
if ( which == "Settings" ) {
addPanel();
$(".shuttle-SEO").parent().parent().append("<div class='Form-item Form-item--actions'><div class='Form-controls'><input class='Button Button--primary' type='submit' value='Save'></div></div>");
// Show SEO Button
$(".shuttle-SEO").append("<div class='Form-item'><label for='seo-button-toggler' class='Form-label'>Show SEO Button</label> <div class='Form-controls'> <span class='switchery switchery-default seo-button-toggler'><small></small></span> </div> </div>");
$(".shuttle-SEO label[for='seo-button-toggler']").append( "<span class='Form-help-icon' data-toggle='tooltip' data-original-title='Disabling the SEO Button will cause the selected behavior to apply automatically to all open WYSIWYG-editors'></span>" );
$(".shuttle-SEO label[for='seo-button-toggler'] .Form-help-icon").append( iconHelp );
$(".shuttle-SEO .seo-button-toggler").on("click", function() {
$(this).toggleClass("on");
if ( $(this).hasClass("on") ) {
localStorage.setItem("SEO-button-active",true);
} else {
localStorage.setItem("SEO-button-active",false);
}
});
if ( localStorage.getItem("SEO-button-active") == null ) {
localStorage.setItem("SEO-button-active", true);
} else {
if ( localStorage.getItem("SEO-button-active") == "true" ) {
$(".seo-button-toggler").addClass("on");
}
};
// Apply on Style Change Button
$(".shuttle-SEO").append("<div class='Form-item'><label for='seo-style-apply-toggler' class='Form-label'>Apply on style choice</label> <div class='Form-controls'> <span class='switchery switchery-default seo-style-apply-toggler'><small></small></span> </div> </div>");
$(".shuttle-SEO label[for='seo-style-apply-toggler']").append( "<span class='Form-help-icon' data-toggle='tooltip' data-original-title='Immediately applies the selected feature to all appropriate elements in the open editor when selecting from the Styles dropdown'></span>" );
$(".shuttle-SEO label[for='seo-style-apply-toggler'] .Form-help-icon").append( iconHelp );
$(".shuttle-SEO .seo-style-apply-toggler").on("click", function() {
$(this).toggleClass("on");
if ( $(this).hasClass("on") ) {
localStorage.setItem("SEO-style-apply-active",true);
} else {
localStorage.setItem("SEO-style-apply-active",false);
}
});
if ( localStorage.getItem("SEO-style-apply-active") == null ) {
localStorage.setItem("SEO-apply-active-active", true);
} else {
if ( localStorage.getItem("SEO-style-apply-active") == "true" ) {
$(".seo-style-apply-toggler").addClass("on");
}
};
$(".shuttle-SEO .Form-help-icon").on("mouseenter", function() {
let tooltipContent = $(this).attr("data-original-title");
let oTop = $(this).offset().top;
let oLeft = $(this).offset().left;
$("body").append("<div class='Tooltip fade in Tooltip--top Tooltip-seo'> <div class='Tooltip-item'>"+ tooltipContent +"</div> </div>");
$(".Tooltip-seo").css({"top": oTop, "left": oLeft});
});
$(".shuttle-SEO .Form-help-icon").on("mouseleave", function() {
$(".Tooltip").remove();
});
$(".shuttle-SEO .switchery, .shuttle-SEO .switchery small").addClass("animatable");
}
// Documentation
if ( which == "Documentation" ) {
addPanel();
$(".shuttle-SEO").append("<div class='shuttle-Panel-title full-width no-float'>Documentation</div><div class='doc-text'>"+ documentation +"</div>");
}
// Changelog
if ( which == "Changelog" ) {
addPanel();
$(".shuttle-SEO").append("<div class='shuttle-Panel-title full-width no-float'>Changelog</div><div class='doc-text'> Coming soon! </div>");
}
// Import/Export
if ( which == "Import/export Settings" ) {
addPanel();
$(".shuttle-SEO").append("<div class='shuttle-Panel-title full-width no-float'>Import / Export settings</div><div class='doc-text'> Coming soon! </div>");
}
});
$(".panel-seo .PanelNav-itemTarget[data-tab='Settings']").trigger("click");
});
};
function settingsClose() {
let panel = $("a[data-active-id='cookies']").closest(".shuttle-Panel-inner");
let otherLink = panel.find(".shuttle-SubNav:not(.heading-addons) .shuttle-SubNav-wrapItems .shuttle-SubNav-itemTarget");
otherLink.off("click");
otherLink.on("click", function() {
$(".heading-addons a").removeClass("is-selected");
$(".panel-seo").remove();
});
$(".shuttle-Panel--mainNav li a").off("click");
$(".shuttle-Panel--mainNav li a").on("click", function() {
$(".heading-addons a").removeClass("is-selected");
if ( $(this).attr("data-active-id") == "settings" && $(this).hasClass("is-selected") == false ) {
console.log("Settings clicked while inactive");
if ( $(".panel-seo").length ) {
console.log("SEO Panel existed");
setTimeout( function() {
$(".shuttle-SubNav-itemTarget").removeClass("is-selected");
$(".settings-seo").addClass("is-selected");
}, 150);
}
}
});
};
setInterval( function() {
if ( $(".cke:not(.affected):not([style*='z-index: 10001']):visible").length ) {
apply( $(".cke:not(.affected):not([style*='z-index: 10001']):visible") );
}
if ( $("a[data-active-id='settings'].is-selected").length && $("a[data-active-id='cookies']").length && $(".heading-addons").length == 0 ) {
settings();
}
if ( $(".heading-addons").length && $(".settings-seo").length == 0 ) {
settingsSEO();
settingsClose();
}
}, 1000);
let css =
".seo-avail {border: 1px solid #7d03ab !important}"
+
".cke_top .cke_seo .cke_button__source_icon {background-position: 0 -624px !important;}"
+
".cke_top .cke_seo .cke_button {width: 46px !important;}"
+
".cke_combo__styles {margin-right: 6px !important}"
+
".cke_button_seo {width: 46px !important}"
+
".cke_button__seo_label {display: block !important}"
+
"#cke_seo_button .cke_button__seo_icon {background: url(https://shuttle-assets-new.s3.amazonaws.com/assets/js/vendor/ckeditor/skins/moono/icons.png) no-repeat 0 -624px !important;}"
+
'.full-width {width: 100%}'
+
'.cke_toolgroup.hidden {height: 0; width: 0; opacity: 0; pointer-events: none;}'
+
'.panel-seo .PanelNav {overflow-y: hidden;}'
+
'.no-float {float: unset !important}'
+
'.doc-text {margin-top: 10px; max-width: 720px; width: 60vw !important;}'
+
".doc-text code {background: #444445; padding: 4px 6px; border: 1px solid #537488; border-radius: 4px; margin: 0 3px;}"
+
".doc-text code em {color: #77c3f4; font-weight: 600;}"
+
".switchery {background-color: #E2E2E2;}"
+
".switchery.on {background-color: #359FE3 !important; border-color: #5ca1e8 !important;}"
+
".animatable {transition: all 200ms ease-in-out 0s;}"
+
".switchery.on small {margin-left: 20px;}"
+
".Form-help-icon svg {height: 18px; width: 20px;}"
+
".Tooltip-seo {transform: translateX( calc(-50% + 10px)) translateY(-100%);}";
$("head").append("<style class='seo-master' type='text/css'></style>");
$(".seo-master").html( css );
});