itarverne/checklist-seo

View on GitHub
seo/static/js/seoSidePannel.js

Summary

Maintainability
F
1 wk
Test Coverage
document.addEventListener('DOMContentLoaded', function () {

    var e = React.createElement;
    sidePannel = document.createElement('div');
    sidePannel.classList.add("seo_side_pannel")
    document.getElementById('tab-settings').after(sidePannel);
    class SeoSideBar extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                keywordScore: 0,
                lengthScore: 0,
                titleScore: 0,
                urlScore: 0,
                internalLinksScore: 0,
                seoBodyInputTimeout: null,
                seoKeyWordInputTimeout: null,
                seoTitleInputTimeout: null,
                seoSlugInputTimeout: null,
                seoTimeRefresh: document.getElementById("id_delay_keyword").value * 1000,
                keywordValidationColor: "gray",
                keywordValidationPercentage: "NK",
                keywordIndicatorTitle: "There is no keyword, please add a keyword via the seo pannel",
                titleValidationColor: "red",
                lengthValidationColor: "red",
                slugValidationColor: "red",
                internalLinksValidationColor: "red",
                internalLinks: 0,
                length: 0,
                titleWords: 0,
                titleCharacters: 0,
                h1_in_content_warning: ""
            };
            document.getElementById("id_delay_keyword").addEventListener('change', () => {
                let seoTimeRefresh = document.getElementById("id_delay_keyword").value;
                if (seoTimeRefresh < 0) document.getElementById("id_delay_keyword").value = 0;
                if (seoTimeRefresh > 99) document.getElementById("id_delay_keyword").value = 99;
                this.setState({ seoTimeRefresh: document.getElementById("id_delay_keyword").value * 1000 });
            });
            document.getElementById("id_keyword").addEventListener('keyup', () => {
                clearTimeout(this.state.seoKeyWordInputTimeout);
                this.setState({
                    seoKeyWordInputTimeout: setTimeout(() => {
                        this.checkKeyword();
                        this.checkValidation();
                    }, this.state.seoTimeRefresh)
                });
            });
            document.getElementById("body-list").addEventListener('keyup', () => {
                clearTimeout(this.state.seoBodyInputTimeout);
                this.setState({
                    seoBodyInputTimeout: setTimeout(() => {
                        this.checkKeyword();
                        this.checkLength();
                        this.checkInternalLinks();
                        this.check_h1_in_content();
                        this.checkValidation();
                    }, this.state.seoTimeRefresh)
                });
            });

            // removal of all h1 buttons in rich text fields
            for (let stream_field of document.getElementsByClassName("stream-field")) {
                stream_field.addEventListener('click', (e) => {
                    if (document.querySelectorAll('button.Draftail-ToolbarButton[name="header-one"]').length != 0) {
                        for (let h1_button of document.querySelectorAll('button[name="header-one"]')) {
                            h1_button.style.display = "none";
                        }
                    }
                });
            }
            document.getElementById("id_title").addEventListener("input", () => {
                clearTimeout(this.state.seoTitleInputTimeout);
                this.setState({
                    seoTitleInputTimeout: setTimeout(() => {
                        this.checkTitle();
                        this.checkValidation();
                    }, this.state.seoTimeRefresh)
                });
            });
            document.getElementById("id_slug").addEventListener("input", () => {
                clearTimeout(this.state.seoSlugInputTimeout);
                this.setState({
                    seoSlugInputTimeout: setTimeout(() => {
                        this.checkSlug();
                        this.checkValidation();
                    }, this.state.seoTimeRefresh)
                });
            });
            this.checkValidation();
        }
        checkValidation() {
            var seoScore = this.state.keywordScore + this.state.lengthScore
                + this.state.titleScore + this.state.urlScore
                + this.state.internalLinksScore;
            if ((seoScore / 5) >= 0.8) {
                this.enablePublish();
            } else {
                this.disablePublish();
            }
        }
        getText() {
            let blocks = document.getElementsByClassName("notranslate public-DraftEditor-content");
            let text = "";
            for (let block of blocks) {
                // we make sure we don’t get deleted blocks’s content
                if (block.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode
                    .parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.style.display != "none") {
                    // we make sure we get only text and not GistCreateCode code
                    if (block.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode
                        .parentNode.parentNode.parentNode.parentNode.children[0].children[2].children[0].innerHTML != "Code") {
                        for (let line of block.firstChild.children) {
                            if (!line.classList.contains('Draftail-block--atomic')) {
                                if (line.classList.contains('public-DraftStyleDefault-ol')
                                    || line.classList.contains('public-DraftStyleDefault-ul')) {
                                    for (let child of line.children) {
                                        text += " " + child.firstChild.firstChild.firstChild.innerHTML;
                                    }
                                }
                                else {
                                    for (let group of line.firstChild.children) {
                                        if (!group.classList.contains("TooltipEntity")) {
                                            text += " " + group.firstChild.innerHTML;
                                        }
                                        // if there are links in the article, we get only the text of the link
                                        else {
                                            text += " " + group.children[1].firstChild.innerHTML;
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
            return text;
        }
        getHtml() {
            var rich_text_blocks = document.getElementsByClassName("notranslate public-DraftEditor-content");
            var raw_html_blocks = document.querySelectorAll('textarea[placeholder="RawHtml"]');
            var text = "";
            for (let block of rich_text_blocks) {
                // we make sure we don’t get deleted blocks’s content
                if (block.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode
                    .parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.style.display != "none") {
                    // we make sure we get only text and not GistCreateCode code
                    if (block.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode
                        .parentNode.parentNode.parentNode.children[0].children[2].children[0].innerHTML != "Code") {
                        text += block.firstChild.firstChild.firstChild.innerHTML;
                    }
                }
            }
            for (let block of raw_html_blocks) {
                if (block.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode
                    .parentNode.parentNode.style.display != "none") {
                    text += block.value;
                }
            }
            return text;
        }
        enablePublish() {
            let publishButtons = document.getElementsByName("action-publish");
            for (let button of publishButtons) {
                button.disabled = false;
                button.title = "";
            }
            let submitButtons = document.getElementsByName("action-submit");
            for (let button of submitButtons) {
                button.disabled = false;
                button.title = "";
            }
        }
        disablePublish() {
            let publishButtons = document.getElementsByName("action-publish");
            for (let button of publishButtons) {
                button.disabled = 'disabled';
                button.title = "Publishing is disabled as the SEO recommandations are not met, be sure "
                    + "to get at least 80% SEO validation to continue";
            }
            let submitButtons = document.getElementsByName("action-submit");
            for (let button of submitButtons) {
                button.disabled = 'disabled';
                button.title = "Publishing is disabled as the SEO recommandations are not met, be sure "
                    + "to get at least 80% SEO validation to continue";
            }
        }
        checkKeyword() {
            let body = [];
            body[0] = document.getElementById("id_keyword").value;
            body[1] = this.getText();
            fetch("/seo/keyword/", {
                method: "POST",
                credentials: 'include',
                body: JSON.stringify(body),
                headers: {
                    'X-CSRFToken': getCookie('csrftoken'),
                    'X-Requested-With': 'XMLHttpRequest',
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                },
                dataType: "json"
            }).then((response) => {
                return response.json();
            }).then((data) => {
                if (data['response'] == "NO_KEYWORD") {
                    this.setState({
                        keywordScore: 0,
                        keywordValidationPercentage: "NK",
                        keywordValidationColor: "gray",
                        keywordIndicatorTitle: "There is no keyword, please add a keyword via the seo pannel"
                    });
                }
                else if (data['response'] == "INVALID_KEYWORD" || data['response'] == "NO_TEXT") {
                    this.setState({
                        keywordScore: 0,
                        keywordValidationPercentage: (data['response'] == "NO_TEXT") ? "NT" : `${(Math.round(data['percentage'] * 10) / 10)} %`,
                        keywordValidationColor: (data['response'] == "NO_TEXT") ? "gray" : "red",
                        keywordIndicatorTitle: (data['response'] == "NO_TEXT") ? "There is no text, please add some text in the "
                            + "article" : "Your keyword is not validated because it is too frequent or too rare (2% or less or above 3%)"
                    });
                }
                else if (data['response'] == "VALID_KEYWORD") {
                    this.setState({
                        keywordScore: 1,
                        keywordValidationPercentage: `${(Math.round(data['percentage'] * 10) / 10).toString()} %`,
                        keywordValidationColor: "green",
                        keywordIndicatorTitle: "The keyword is present and the frequence matches the seo recommendations"
                    });
                }
            });
        }
        checkLength() {
            fetch("/seo/length/", {
                method: "POST",
                credentials: 'include',
                body: JSON.stringify(this.getText()),
                headers: {
                    'X-CSRFToken': getCookie('csrftoken'),
                    'X-Requested-With': 'XMLHttpRequest',
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                },
                dataType: "json"
            }).then(function (response) {
                return response.json();
            }).then((data) => {
                if (data["response"] || data["error"]) {
                    this.setState({ length: 0 });
                } else {
                    this.setState({ length: data["length"] });
                }
                if (data["code"] == 0) {
                    this.setState({ lengthValidationColor: "red", lengthScore: 0 });
                } else if (data["code"] == 1) {
                    this.setState({ lengthValidationColor: "orange", lengthScore: 0 });
                } else if (data["code"] == 2) {
                    this.setState({ lengthValidationColor: "green", lengthScore: 1 });
                }
            });
        }
        checkTitle() {
            var body = [];
            body[0] = document.getElementById("id_keyword").value;
            body[1] = document.getElementById("id_title").value;
            fetch("/seo/title/", {
                method: "POST",
                credentials: 'include',
                body: JSON.stringify(body),
                headers: {
                    'X-CSRFToken': getCookie('csrftoken'),
                    'X-Requested-With': 'XMLHttpRequest',
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                },
                dataType: "json"
            }).then(function (response) {
                return response.json();
            }).then((data) => {
                if (data["response"] == "NO_TITLE") {
                    this.setState({ titleWords: 0, titleCharacters: 0 });
                } else {
                    this.setState({ titleWords: data["word_count"], titleCharacters: data["character_count"] });
                }
                if (data["response"] == 'VALID_TITLE') {
                    this.setState({ titleValidationColor: "green", titleScore: 1 });
                } else {
                    this.setState({ titleValidationColor: "red", titleScore: 0 });
                }
            });
        }
        checkSlug() {
            var body = [];
            body[0] = document.getElementById("id_keyword").value;
            body[1] = document.getElementById("id_slug").value;
            fetch("/seo/slug/", {
                method: "POST",
                credentials: 'include',
                body: JSON.stringify(body),
                headers: {
                    'X-CSRFToken': getCookie('csrftoken'),
                    'X-Requested-With': 'XMLHttpRequest',
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                },
                dataType: "json"
            }).then(function (response) {
                return response.json();
            }).then((data) => {
                if (data["response"] == 'VALID_SLUG') {
                    this.setState({ slugValidationColor: "green", urlScore: 1 });
                } else {
                    this.setState({ slugValidationColor: "red", urlScore: 0 });
                }
            });
        }
        checkInternalLinks() {
            fetch("/seo/internal_links/", {
                method: "POST",
                credentials: 'include',
                body: JSON.stringify(this.getHtml()),
                headers: {
                    'X-CSRFToken': getCookie('csrftoken'),
                    'X-Requested-With': 'XMLHttpRequest',
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                },
                dataType: "json"
            }).then(function (response) {
                return response.json();
            }).then((data) => {
                if (data["response"] || data["error"]) {
                    this.setState({ internalLinks: 0 });
                } else {
                    this.setState({ internalLinks: data["internal_links"] });
                }
                if (data["code"] == 0) {
                    this.setState({ internalLinksValidationColor: "red", internalLinksScore: 0 });
                } else if (data["code"] == 1) {
                    this.setState({ internalLinksValidationColor: "orange", internalLinksScore: 0 });
                } else if (data["code"] == 2) {
                    this.setState({ internalLinksValidationColor: "green", internalLinksScore: 1 });
                }
            });
        }
        check_h1_in_content() {
            var rich_text_blocks = document.getElementsByClassName("DraftEditor-root");
            var text;
            for (let block of rich_text_blocks) {
                text += block.innerHTML;
            }
            var raw_html_blocks = document.querySelectorAll('textarea[placeholder="RawHtml')
            for (let block of raw_html_blocks) {
                text += block.value;
            }
            fetch("/seo/title_in_article/", {
                method: "POST",
                credentials: 'include',
                body: JSON.stringify(text),
                headers: {
                    'X-CSRFToken': getCookie('csrftoken'),
                    'X-Requested-With': 'XMLHttpRequest',
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                },
                dataType: "json"
            }).then(function (response) {
                return response.json();
            }).then((data) => {
                if (data['response'] == "H1_PRESENT") {
                    this.setState({ h1_in_content_warning: "there is a h1 in your content, please remove it" });
                } else {
                    this.setState({ h1_in_content_warning: "" });
                }
            });
        }
        render() {
            return [
                e('div', { class: "seo-title" },
                    [
                        e('img', { class: 'seo-logo', src: '/static/images/seo_logo.png', alt: "logo seo" },
                            null),
                        e('h2', null, "SEO results")
                    ]),
                e('div', { class: 'seo-toolbar' },
                    e('div', null,
                        [
                            e('span', {
                                class: `seo-indicator ${this.state.keywordValidationColor}`,
                                title: this.state.keywordIndicatorTitle
                            },
                                this.state.keywordValidationPercentage),
                            e('span',
                                { title: "The keyword must be present in the text but not too frequent (above 2% to 3% max)" },
                                "Keyword repartition")
                        ]),
                    e('div', null,
                        [
                            e('i',
                                { class: `icon ${this.state.lengthValidationColor}` },
                                null),
                            e('span',
                                { title: "The length of the article should be between 1400 and 1600 words" },
                                `Length content (${this.state.length} words)`)
                        ]),
                    e('div', null,
                        [
                            e('i',
                                { class: `icon ${this.state.titleValidationColor}` },
                                null),
                            e('span',
                                { title: "The keyword must be present in the title and title must be under 70 characters long" },
                                `Title article (${this.state.titleWords} words,${this.state.titleCharacters} characters)`)
                        ]),
                    e('div', null,
                        [
                            e('i',
                                { class: `icon ${this.state.slugValidationColor}` },
                                null),
                            e('span',
                                { title: "The keyword must be present in the slug" },
                                "Url article")
                        ]),
                    e('div', null,
                        [
                            e('i', { class: `icon ${this.state.internalLinksValidationColor}` },
                                null),
                            e('span', { title: "There must be internal links, the best is 5" },
                                `Internal Links (${this.state.internalLinks} links)`)
                        ]),
                    e('div', null,
                        [
                            e('span', { class: 'red' }, this.state.h1_in_content_warning)
                        ])
                )
            ];
        }
    }
    ReactDOM.render(
        e(SeoSideBar, null, null), sidePannel);
});