Здесь делается вжух 🪄

sebastian: В свежем осеннем воздухе, клубящемся среди закрытых окон и плотно заделанных швов в стенах, тонко, но уверенно разливался сладкий запах крови – ненавязчивая, но лидирующая трель флейты в симфоническом оркестре.
мистика, расы
autumn edition

autumn geis test

Информация о пользователе

Привет, Гость! Войдите или зарегистрируйтесь.


Вы здесь » autumn geis test » Тестовый форум » важная тема


важная тема

Сообщений 1 страница 6 из 6

1

[html]
<script src="https://code.jquery.com/jquery-3.7.1.min.js"></script>

<style>
* {
    box-sizing: border-box;
}

html,
body {
    margin: 0;
    padding: 0;
    background: transparent;
    color: #222;
    font-family: Arial, sans-serif;
}

body {
    width: 100%;
    overflow-x: hidden;
}
.profile-shop {
    width: 100%;
    color: #222;
}

.profile-shop__tabs {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: 6px;
    padding-top: 13px;
}

.profile-shop__tab {
    display: block;
    width: 98%;
    padding: 10px 20px;
    border: 0;
    border-radius: 7px;
    background: #ededed;
    color: #222;
    text-align: center;
    cursor: pointer;
    font: 500 13px Arial, sans-serif;
    transition:
        background-color .3s,
        color .3s;
}

.profile-shop__tab:hover,
.profile-shop__tab.is-active {
    background: #303030;
    color: #ededed;
}
.profile-shop__windows {
    position: relative;
    overflow: hidden;
}

.profile-shop__window {
    display: none;
    padding: 21px 0 10px;
    line-height: 1.5;
}

.profile-shop__window.is-active {
    display: block;
    animation: shopWindowShow .25s ease;
}

@keyframes shopWindowShow {
    from {
        opacity: 0;
        transform: translateY(15px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}
.profile-shop__title {
    margin: 0 0 15px;
    text-align: center;
    font-family: Georgia, serif;
    font-size: 22px;
    font-style: italic;
}

.profile-shop__notice {
    margin: 0 0 20px;
    padding: 15px 18px;
    border: 1px solid rgba(0, 0, 0, .13);
    border-radius: 7px;
    background: rgba(237, 237, 237, .7);
    text-align: center;
    font: 13px/1.6 Arial, sans-serif;
}

.profile-shop__notice p {
    margin: 0 0 10px;
}

.profile-shop__notice p:last-child {
    margin-bottom: 0;
}

.profile-shop__price-title {
    margin: 20px 0 15px;
    text-align: center;
    font-family: Georgia, serif;
    font-size: 17px;
    font-style: italic;
}

.profile-shop__separator {
    height: 1px;
    margin: 28px 0;
    border: 0;
    background: rgba(0, 0, 0, .16);
}
.shop-gallery {
    margin: 20px auto 25px;
}

.shop-item-wrap {
    position: relative;
    display: block;
    margin: 0;
    padding: 0;
    line-height: 0;
}

.shop-selectable {
    display: block;
    margin: 0;
    padding: 0;
    cursor: pointer;
    transition:
        opacity .2s ease,
        transform .2s ease,
        outline-color .2s ease;
}

.shop-selectable:hover {
    transform: translateY(-3px);
}

.shop-selectable.is-shop-selected {
    opacity: .58;
    outline: 3px solid #8b756b;
    outline-offset: 3px;
}

.shop-item-wrap::after {
    content: "✓";
    position: absolute;
    top: 7px;
    right: 7px;
    z-index: 3;
    display: none;
    align-items: center;
    justify-content: center;
    width: 25px;
    height: 25px;
    border-radius: 50%;
    background: #303030;
    color: #fff;
    font: bold 15px/1 Arial, sans-serif;
    pointer-events: none;
}

.shop-item-wrap.is-shop-selected::after {
    display: flex;
}
.shop-gallery[data-shop-category="плашка"],
.shop-gallery[data-shop-category="фон"] {
    display: grid;
    grid-template-columns: repeat(8, 70px);
    justify-content: center;
    align-items: start;
    gap: 10px;
    width: min(100%, 630px);
}

.shop-gallery[data-shop-category="плашка"] > img,
.shop-gallery[data-shop-category="плашка"] > .shop-item-wrap,
.shop-gallery[data-shop-category="фон"] > img,
.shop-gallery[data-shop-category="фон"] > .shop-item-wrap {
    width: 70px;
    height: 200px;
}

.shop-gallery[data-shop-category="плашка"] img,
.shop-gallery[data-shop-category="фон"] img {
    width: 70px !important;
    min-width: 70px !important;
    max-width: 70px !important;
    height: 200px !important;
    min-height: 200px !important;
    max-height: 200px !important;
    border: 0;
    border-radius: 9px;
    object-fit: cover;
    object-position: center;
    box-shadow: 0 2px 5px rgba(0, 0, 0, .25);
}
.profile-shop__custom {
    margin: 18px 0 24px;
    padding: 15px;
    border: 1px solid rgba(0, 0, 0, .13);
    border-radius: 7px;
    background: rgba(255, 255, 255, .55);
}

.profile-shop__custom-title {
    margin: 0 0 10px;
    text-align: center;
    font: 600 12px/1.5 Arial, sans-serif;
}

.profile-shop__custom-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 34px 34px;
    gap: 6px;
    margin-top: 7px;
}

.profile-shop__custom-row:first-child {
    margin-top: 0;
}

.profile-shop__custom-input {
    width: 100%;
    min-width: 0;
    height: 34px;
    padding: 7px 10px;
    border: 1px solid rgba(0, 0, 0, .2);
    border-radius: 5px;
    outline: none;
    background: rgba(255, 255, 255, .9);
    color: #222;
    font: 12px Arial, sans-serif;
}

.profile-shop__custom-input:focus {
    border-color: rgba(48, 48, 48, .6);
}

.profile-shop__custom-control {
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid rgba(0, 0, 0, .2);
    border-radius: 5px;
    background: #ededed;
    color: #222;
    cursor: pointer;
    font: 20px/1 Arial, sans-serif;
}

.profile-shop__custom-control:hover {
    background: #303030;
    color: #fff;
}

.profile-shop__custom-row:first-child [data-shop-custom-remove] {
    visibility: hidden;
    pointer-events: none;
}
.shop-gallery[data-shop-category="артефакт"] {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    justify-content: center;
    gap: 14px;
}

.shop-gallery[data-shop-category="артефакт"] > img,
.shop-gallery[data-shop-category="артефакт"] > .shop-item-wrap {
    width: auto;
    max-width: 180px;
    height: auto;
}

.shop-gallery[data-shop-category="артефакт"] img {
    width: auto !important;
    max-width: 180px !important;
    height: auto !important;
    max-height: 220px !important;
    border: 0;
    object-fit: contain;
}
.profile-shop__order {
    margin-top: 25px;
    padding: 18px;
    border: 1px solid rgba(0, 0, 0, .15);
    border-radius: 7px;
    background: rgba(237, 237, 237, .75);
}

.profile-shop__order-head,
.profile-shop__order-footer {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 15px;
}

.profile-shop__balance {
    display: flex;
    flex-wrap: wrap;
    gap: 8px 22px;
    margin: 0 0 12px;
    padding: 10px 12px;
    border: 1px solid rgba(0, 0, 0, .1);
    border-radius: 5px;
    background: rgba(255, 255, 255, .6);
    font-size: 12px;
    line-height: 1.5;
}

.profile-shop__balance span {
    flex: 1 1 210px;
}

.profile-shop__balance b {
    font-size: 13px;
}

.profile-shop__balance.is-negative [data-shop-profile-total] {
    color: #a52a2a;
}

.profile-shop__order-head {
    margin-bottom: 12px;
}

.profile-shop__order-head strong {
    font-family: Georgia, serif;
    font-size: 18px;
    font-style: italic;
    font-weight: normal;
}

.profile-shop__output {
    display: block;
    width: 100%;
    min-height: 170px;
    margin: 0 0 12px;
    padding: 12px;
    resize: vertical;
    border: 1px solid rgba(0, 0, 0, .17);
    border-radius: 5px;
    outline: none;
    background: rgba(255, 255, 255, .85);
    color: #222;
    font: 12px/1.6 Arial, sans-serif;
}

.profile-shop__order-footer {
    flex-wrap: wrap;
    font-size: 12px;
}

.profile-shop__buttons {
    display: flex;
    flex-wrap: wrap;
    gap: 7px;
}

.profile-shop__button {
    padding: 8px 13px;
    border: 1px solid rgba(0, 0, 0, .2);
    border-radius: 5px;
    background: rgba(255, 255, 255, .8);
    color: #222;
    cursor: pointer;
    font: 12px Arial, sans-serif;
    transition:
        background-color .2s,
        opacity .2s;
}

.profile-shop__button:hover:not(:disabled) {
    background: #fff;
}

.profile-shop__button:disabled {
    cursor: default;
    opacity: .4;
}

.profile-shop__status {
    min-height: 18px;
    margin-top: 10px;
    color: #555;
    text-align: center;
    font-size: 11px;
}
@media (max-width: 850px) {
    .shop-gallery[data-shop-category="плашка"],
    .shop-gallery[data-shop-category="фон"] {
        grid-template-columns: repeat(8, 70px);
        width: min(100%, 470px);
    }
}

@media (max-width: 630px) {
    .profile-shop__tabs {
        grid-template-columns: repeat(2, 1fr);
    }

    .shop-gallery[data-shop-category="плашка"],
    .shop-gallery[data-shop-category="фон"] {
        grid-template-columns: repeat(4, 70px);
        width: min(100%, 310px);
    }

    .profile-shop__order-head,
    .profile-shop__order-footer {
        align-items: stretch;
        flex-direction: column;
    }

    .profile-shop__buttons {
        width: 100%;
    }

    .profile-shop__button {
        flex: 1 1 auto;
    }
}

@media (max-width: 420px) {
    .shop-gallery[data-shop-category="плашка"],
    .shop-gallery[data-shop-category="фон"] {
        grid-template-columns: repeat(3, 70px);
        width: min(100%, 230px);
    }
}

.quote-table-wrap {
    text-align: center;
    margin-bottom: 18px;
}

.quote-table {
    display: inline-table;
    width: 100%;
    max-width: 450px;
    text-align: left;
    border-collapse: separate;
    border-spacing: 0 10px;
    background: rgba(255, 255, 255, .45);
    border-radius: 8px;
}

.quote-table__label {
    width: 28px;
    padding: 6px 4px;
    vertical-align: middle;
    text-align: center;
    color: #555;
    border-right: 1px solid rgba(0, 0, 0, .1);
}

.quote-table__label span {
    display: inline-block;
    writing-mode: vertical-rl;
    transform: rotate(180deg);
    white-space: nowrap;
    font: italic 700 12px / 1 var(--cursivef);
    letter-spacing: .5px;
    color: #303030;
}

.quote-table td {
    padding: 4px 10px;
}

.quote-color-pair {
    display: flex;
    flex-wrap: wrap;
    gap: 10px 14px;
}

.quote-color-pair__group {
    display: flex;
    align-items: center;
    gap: 6px;
}

.quote-color-pair__tag {
    font: 11px Arial, sans-serif;
    color: #555;
}

.quote-color-row {
    display: flex;
    align-items: center;
    gap: 8px;
}

.quote-color-picker {
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid rgba(0, 0, 0, .25);
    border-radius: 6px;
    cursor: pointer;
    background: none;
}

.quote-color-hex {
    width: 90px;
    height: 34px;
    padding: 0 8px;
    border: 1px solid rgba(0, 0, 0, .2);
    border-radius: 5px;
    outline: none;
    background: rgba(255, 255, 255, .9);
    color: #222;
    font: 12px Arial, sans-serif;
    text-align: center;
}

.quote-text-input--compact {
    min-height: 34px;
    padding: 6px 10px;
    font-size: 11px;
}

.quote-mini-toggle {
    display: inline-block;
    margin-top: 6px;
    padding: 5px 10px;
    border: 1px solid rgba(0, 0, 0, .2);
    border-radius: 4px;
    background: rgba(255, 255, 255, .8);
    color: #222;
    cursor: pointer;
    font: 400 12px / 1 var(--sec-font);
}

.quote-mini-toggle.is-active {
    background: #303030;
    color: #fff;
}

.quote-combos {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 10px;
    margin: 0 0 20px;
}

.quote-combo {
    width: 90px;
    padding: 8px;
    border: 1px solid rgba(0, 0, 0, .18);
    border-radius: 6px;
    background: rgba(255, 255, 255, .6);
    cursor: pointer;
    text-align: center;
    font: 11px Arial, sans-serif;
    transition: outline-color .2s;
}

.quote-combo-swatch {
    height: 30px;
    margin-bottom: 6px;
    border-radius: 4px;
}

.quote-combo.is-active {
    outline: 2px solid #303030;
    outline-offset: 2px;
}

.quote-combo-price {
    display: block;
    margin-top: 2px;
    color: #555;
}

.quote-text-input {
    display: block;
    min-height: 60px;
    padding: 10px 12px;
    border: 1px solid rgba(0, 0, 0, .2);
    border-radius: 6px;
    outline: none;
    background: rgba(255, 255, 255, .9);
    color: #222;
    font: 12px/1.5 Arial, sans-serif;
    text-align: left;
}

.quote-text-toggle {
    display: block;
    margin: 0 auto;
    padding: 8px 16px;
    border: 1px solid rgba(0, 0, 0, .2);
    border-radius: 5px;
    background: rgba(255, 255, 255, .8);
    color: #222;
    cursor: pointer;
    font: 12px Arial, sans-serif;
}

.quote-text-toggle.is-active {
    background: #303030;
    color: #fff;
}
</style>

<div class="profile-shop" id="profile-shop">

    <div class="profile-shop__tabs">
        <button class="profile-shop__tab is-active" type="button" data-shop-tab="bookmarks">
            закладки
        </button>

        <button class="profile-shop__tab" type="button" data-shop-tab="backgrounds">
            фоны
        </button>

        <button class="profile-shop__tab" type="button" data-shop-tab="artifacts">
            артефакты
        </button>

        <button class="profile-shop__tab" type="button" data-shop-tab="quote">
            плашка
        </button>
    </div>

    <div class="profile-shop__windows">
        <section class="profile-shop__window is-active" data-shop-window="bookmarks">
            <h2 class="profile-shop__title">
                закладки для профиля
            </h2>

            <div class="profile-shop__notice">
                Закладка в профиль из нашей коллекции стоит
                <strong>300 денег</strong>, закладка на ваш вкус —
                <strong>550 денег</strong>.

                Для обработки заявки оставьте ссылку на сообщение
                в банке, где была оформлена покупка, а также ссылку
                на желаемую закладку.
            </div>

            <div
                class="profile-shop__custom"
                data-shop-custom-group
                data-shop-category="закладка на ваш вкус"
                data-shop-price="550"
            >
                <div class="profile-shop__custom-title">
                    своя закладка — 550 денег
                </div>

                <div data-shop-custom-rows>
                    <div class="profile-shop__custom-row" data-shop-custom-row>
                        <input
                            class="profile-shop__custom-input"
                            type="text"
                            data-shop-custom-input
                            placeholder="вставьте ссылку на картинку"
                        >
                        <button class="profile-shop__custom-control" type="button" data-shop-custom-add title="Добавить ещё поле">+</button>
                        <button class="profile-shop__custom-control" type="button" data-shop-custom-remove title="Удалить поле">×</button>
                    </div>
                </div>
            </div>

            <div
                class="shop-gallery"
                data-shop-category="плашка"
                data-shop-price="300"
            >
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/780175.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/866908.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/442819.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/286367.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/224561.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/988042.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/169463.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/18370.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/266460.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/840850.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/379600.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/415865.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/529814.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/826613.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/185620.png" alt="Закладка">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/45725.png" alt="Закладка">
            </div>
        </section>

        <section class="profile-shop__window" data-shop-window="backgrounds">
            <h2 class="profile-shop__title">
                фоны для личной странички
            </h2>

            <div class="profile-shop__notice">
                <p>
                    Фоны личной страницы из нашей коллекции стоят
                    <strong>250 денег</strong>, из ваших исходников —
                    <strong>350 денег</strong>.
                </p>

                <p>
                    После покупки оформите заявку и приложите ссылку
                    на выбранный фон.
                </p>

                <p>
                    <strong><em>Примечание:</em></strong>
                    при открытии личной страницы фон будет виден
                    в полном размере. Перед покупкой рекомендуется
                    открыть изображение в новой вкладке.
                </p>
            </div>

            <div
                class="profile-shop__custom"
                data-shop-custom-group
                data-shop-category="фон из ваших исходников"
                data-shop-price="350"
            >
                <div class="profile-shop__custom-title">
                    свой фон — 350 денег
                </div>

                <div data-shop-custom-rows>
                    <div class="profile-shop__custom-row" data-shop-custom-row>
                        <input
                            class="profile-shop__custom-input"
                            type="text"
                            data-shop-custom-input
                            placeholder="вставьте ссылку на картинку"
                        >
                        <button class="profile-shop__custom-control" type="button" data-shop-custom-add title="Добавить ещё поле">+</button>
                        <button class="profile-shop__custom-control" type="button" data-shop-custom-remove title="Удалить поле">×</button>
                    </div>
                </div>
            </div>

            <div
                class="shop-gallery"
                data-shop-category="фон"
                data-shop-price="250"
            >
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/302052.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/291726.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/985217.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/12909.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/646367.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/309066.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/554289.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/450484.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/845421.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/361653.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/720949.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/283145.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/331964.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/898671.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/250341.png" alt="Фон">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/526811.png" alt="Фон">
            </div>
        </section>
        <section class="profile-shop__window" data-shop-window="artifacts">
            <h2 class="profile-shop__title">
                одноразовые артефакты
            </h2>

            <div class="profile-shop__notice">
                Сгорает после использования. В одном квесте можно
                использовать <strong>до трёх</strong> разовых
                предметов. Применяются как до, так и после броска.
            </div>

            <div class="profile-shop__price-title">
                цена: 300 денег
            </div>

            <div
                class="shop-gallery"
                data-shop-category="артефакт"
                data-shop-price="300"
            >
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/821120.png" alt="Артефакт">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/271525.png" alt="Артефакт">
                <img src="https://upforme.ru/uploads/001c/bd/d1/64/39096.png" alt="Артефакт">
                <img src="https://upforme.ru/uploads/001c/bd/d1/64/856065.png" alt="Артефакт">
            </div>

            <div class="profile-shop__price-title">
                цена: 400 денег
            </div>

            <div
                class="shop-gallery"
                data-shop-category="артефакт"
                data-shop-price="400"
            >
                <img src="https://upforme.ru/uploads/001c/bd/d1/64/199670.png" alt="Артефакт">
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/54236.png" alt="Артефакт">
                <img src="https://upforme.ru/uploads/001c/bd/d1/64/795532.png" alt="Артефакт">
                <img src="https://upforme.ru/uploads/001c/bd/d1/4/667715.png" alt="Артефакт">
            </div>

            <hr class="profile-shop__separator">

            <h2 class="profile-shop__title">
                многоразовые артефакты
            </h2>

            <div class="profile-shop__notice">
                Действует <strong>в течение одного квеста</strong>,
                после чего сгорает. При себе можно иметь
                <strong>только один</strong>. Менять артефакт
                во время игры нельзя.
            </div>

            <div class="profile-shop__price-title">
                цена: 500 денег
            </div>

            <div
                class="shop-gallery"
                data-shop-category="артефакт"
                data-shop-price="500"
            >
                <img src="https://upforme.ru/uploads/001c/bd/d1/8/255932.png" alt="Артефакт">
                <img src="https://upforme.ru/uploads/001c/bd/d1/4/746461.png" alt="Артефакт">
                <img src="https://upforme.ru/uploads/001c/bd/d1/64/171210.png" alt="Артефакт">
                <img src="https://upforme.ru/uploads/001c/bd/d1/64/168079.png" alt="Артефакт">
            </div>
        </section>

        <section class="profile-shop__window" data-shop-window="quote">
            <h2 class="profile-shop__title">
                плашка
            </h2>

            <div class="profile-shop__notice">
                Всё ниже — бесплатный предпросмотр, результат сразу виден
                в карточке автора темы слева. Купить можно готовую комбинацию
                цвета (<strong>350</strong>) и/или свой текст (<strong>200</strong>).
                Лз сохраняется в заявку бесплатно.
            </div>

            <div class="quote-table-wrap">
            <table class="quote-table">
                <tr>
                    <td class="quote-table__label"><span>лз</span></td>
                    <td>
                        <div
                            class="quote-text-input quote-text-input--compact"
                            contenteditable="true"
                            data-lz-input
                            data-placeholder="HTML-код лз"
                        ></div>
                        <button class="quote-mini-toggle" type="button" data-lz-toggle>
                            сохранить в заявку
                        </button>
                    </td>
                </tr>
                <tr>
                    <td class="quote-table__label"><span>плашка</span></td>
                    <td>
                        <div
                            class="quote-text-input quote-text-input--compact"
                            contenteditable="true"
                            data-quote-text-input
                            data-placeholder="свой текст для предпросмотра"
                        ></div>
                        <button class="quote-mini-toggle" type="button" data-quote-text-toggle>
                            заказать — 200 денег
                        </button>
                    </td>
                </tr>
                <tr>
                    <td class="quote-table__label"><span>цвет</span></td>
                    <td>
                        <div class="quote-color-pair">
                            <span class="quote-color-pair__group">
                                <span class="quote-color-pair__tag">фон</span>
                                <input class="quote-color-picker" type="color" data-quote-picker-bg value="#2b2b33">
                                <input class="quote-color-hex" type="text" data-quote-hex-bg placeholder="#2b2b33" maxlength="7">
                            </span>
                            <span class="quote-color-pair__group">
                                <span class="quote-color-pair__tag">текст</span>
                                <input class="quote-color-picker" type="color" data-quote-picker-color value="#d8c9a3">
                                <input class="quote-color-hex" type="text" data-quote-hex-color placeholder="#d8c9a3" maxlength="7">
                            </span>
                        </div>
                    </td>
                </tr>
            </table>
            </div>

            <div class="profile-shop__price-title">
                готовые комбинации — 350 денег
            </div>

            <div class="quote-combos">
                <div class="quote-combo" data-quote-combo="combo1">
                    <div class="quote-combo-swatch" style="background:#2b2b33"></div>
                    Полночь
                    <span class="quote-combo-price">350</span>
                </div>
                <div class="quote-combo" data-quote-combo="combo2">
                    <div class="quote-combo-swatch" style="background:#3a2a2f"></div>
                    Пепел розы
                    <span class="quote-combo-price">350</span>
                </div>
                <div class="quote-combo" data-quote-combo="combo3">
                    <div class="quote-combo-swatch" style="background:#2c332b"></div>
                    Мох
                    <span class="quote-combo-price">350</span>
                </div>
                <div class="quote-combo" data-quote-combo="combo4">
                    <div class="quote-combo-swatch" style="background:#3a2a1c"></div>
                    Ржавчина
                    <span class="quote-combo-price">350</span>
                </div>
                <div class="quote-combo" data-quote-combo="combo5">
                    <div class="quote-combo-swatch" style="background:#2c2f36"></div>
                    Туман
                    <span class="quote-combo-price">350</span>
                </div>
            </div>
        </section>
    </div>
    <div class="profile-shop__order">
        <div class="profile-shop__order-head">
            <strong>ваша покупка</strong>

            <span>
                выбрано:
                <b data-shop-count>0</b>
            </span>
        </div>

        <textarea
            class="profile-shop__output"
            data-shop-output
            readonly
            placeholder="нажмите на картинку или вставьте свою ссылку"
        ></textarea>

        <div class="profile-shop__balance" data-shop-balance>
            <span>
                банк:
                <b data-shop-bank>загрузка…</b>
                денег
            </span>

            <span>
                общая сумма на профиле:
                <b data-shop-profile-total>—</b>
            </span>
        </div>

        <div class="profile-shop__order-footer">
            <span>
                общая сумма покупки:
                <b data-shop-total>0</b>
                денег
            </span>

            <div class="profile-shop__buttons">
                <button
                    class="profile-shop__button"
                    type="button"
                    data-shop-copy
                    disabled
                >
                    скопировать
                </button>

                <button
                    class="profile-shop__button"
                    type="button"
                    data-shop-clear
                    disabled
                >
                    очистить
                </button>
            </div>
        </div>

        <div class="profile-shop__status" data-shop-status></div>
    </div>
</div>

<script>
(function ($) {
    $(function () {
        var CHANNEL = 'broken-geis-profile-shop-v4';
        var QUOTE_FIELD_ID = 5;

        var COMBOS = [
            { id: 'combo1', title: 'Полночь', bg: '#2b2b33', text: '#d8c9a3', defaultQuote: 'тишина здесь гуще, чем где-либо ещё' },
            { id: 'combo2', title: 'Пепел розы', bg: '#3a2a2f', text: '#e3b7b0', defaultQuote: 'то, что сгорело, ещё помнит свою форму' },
            { id: 'combo3', title: 'Мох', bg: '#2c332b', text: '#c7d2b8', defaultQuote: 'здесь время растёт медленнее' },
            { id: 'combo4', title: 'Ржавчина', bg: '#3a2a1c', text: '#d9a468', defaultQuote: 'старое железо хранит все клятвы' },
            { id: 'combo5', title: 'Туман', bg: '#2c2f36', text: '#b8c4cf', defaultQuote: 'дорога есть, пока её не видно' }
        ];

        var $shop = $('#profile-shop');
        var selected = {};
        var bank = null;
        var bankLoaded = false;

        var quoteState = {
            comboId: null,
            customText: null,
            defaultText: '',
            wantsCustomText: false,
            previewBg: null,
            previewColor: null
        };

        var lzState = {
            html: null,
            defaultHtml: '',
            wantsSave: false
        };

        var $output = $shop.find('[data-shop-output]');
        var $count = $shop.find('[data-shop-count]');
        var $total = $shop.find('[data-shop-total]');
        var $bank = $shop.find('[data-shop-bank]');
        var $profileTotal = $shop.find('[data-shop-profile-total]');
        var $balance = $shop.find('[data-shop-balance]');
        var $copy = $shop.find('[data-shop-copy]');
        var $clear = $shop.find('[data-shop-clear]');
        var $status = $shop.find('[data-shop-status]');

        $shop.on('click', '[data-shop-tab]', function () {
            var tab = $(this).attr('data-shop-tab');

            $shop.find('[data-shop-tab]').removeClass('is-active');
            $(this).addClass('is-active');
            $shop.find('[data-shop-window]').removeClass('is-active');
            $shop.find('[data-shop-window="' + tab + '"]').addClass('is-active');
        });

        $shop.find('.shop-gallery').each(function () {
            var $gallery = $(this);
            var category = $gallery.attr('data-shop-category');
            var price = +$gallery.attr('data-shop-price');

            $gallery.find('img').each(function () {
                $(this)
                    .attr('data-shop-category', category)
                    .attr('data-shop-price', price)
                    .addClass('shop-selectable')
                    .wrap('<span class="shop-item-wrap"></span>');
            });
        });

        function getCombo(id) {
            var i;
            for (i = 0; i < COMBOS.length; i++) {
                if (COMBOS[i].id === id) {
                    return COMBOS[i];
                }
            }
            return null;
        }

        function getItems() {
            var items = Object.keys(selected).map(function (key) {
                return selected[key];
            });

            $shop.find('[data-shop-custom-group]').each(function () {
                var category = $(this).attr('data-shop-category');
                var price = +$(this).attr('data-shop-price');

                $(this).find('[data-shop-custom-input]').each(function () {
                    var url = $.trim($(this).val());
                    if (url) {
                        items.push({ url: url, category: category, price: price });
                    }
                });
            });

            if (quoteState.comboId) {
                var combo = getCombo(quoteState.comboId);
                if (combo) {
                    items.push({
                        url: 'комбинация «' + combo.title + '» (фон ' + combo.bg + ', цвет текста ' + combo.text +
                            ', текст плашки: ' + combo.defaultQuote + ')',
                        category: 'готовая комбинация плашки',
                        price: 350
                    });
                }
            }

            if (quoteState.wantsCustomText && quoteState.customText) {
                items.push({
                    url: 'текст: «' + quoteState.customText + '»',
                    category: 'свой текст на плашке',
                    price: 200
                });
            }

            if (lzState.wantsSave && lzState.html) {
                items.push({
                    url: 'лз: ' + lzState.html,
                    category: 'личное звание (лз)',
                    price: 0
                });
            }

            return items;
        }

        function getTotal(items) {
            var total = 0;
            $.each(items, function (_, item) {
                total += item.price;
            });
            return total;
        }

        function buildMessage(items, total) {
            if (!items.length) return '';

            var lines = [];
            var prices = [];

            $.each(items, function (i, item) {
                lines.push((i + 1) + '. ' + item.url + ' | ' + item.category);
                prices.push(item.price);
            });

            lines.push('');
            lines.push('Общая сумма покупки: ' + prices.join(' + ') + ' = ' + total + ' денег');

            if (bankLoaded && bank !== null) {
                lines.push('');
                lines.push('Банк: ' + bank + ' денег');
                lines.push('Общая сумма на профиле: ' + bank + ' - ' + total + ' = ' + (bank - total) + ' денег');
            }

            return lines.join('\n');
        }

        function render() {
            var items = getItems();
            var total = getTotal(items);
            var hasItems = items.length > 0;

            $count.text(items.length);
            $total.text(total);
            $output.val(buildMessage(items, total));
            $copy.prop('disabled', !hasItems);
            $clear.prop('disabled', !hasItems);

            if (!bankLoaded) {
                $bank.text('загрузка…');
                $profileTotal.text('—');
            } else if (bank === null) {
                $bank.text('не найден');
                $profileTotal.text('—');
            } else {
                $bank.text(bank);
                $profileTotal.text(bank + ' - ' + total + ' = ' + (bank - total) + ' денег');
            }

            $balance.toggleClass('is-negative', bank !== null && bank - total < 0);
        }

        function buildBookmarkHtml(imageUrl) {
            return '<div class="perspage1"><a href="#" onclick="return false;">' +
                '<img src="' + imageUrl + '" title="личная страница"></a></div>';
        }

        function sendPreviewUpdate(partial) {
            var payload = $.extend({}, partial);
            window.parent.postMessage({
                channel: CHANNEL,
                type: 'profile-shop-preview-update',
                value: payload
            }, '*');
        }

        $shop.on('click', '.shop-selectable', function (e) {
            e.preventDefault();

            var $image = $(this);
            var url = $image.attr('src');
            var category = $image.attr('data-shop-category');
            var key = category + '|' + url;
            var $wrap = $image.parent();
            var wasSelected = !!selected[key];

            if (category === 'плашка' && !wasSelected) {
                Object.keys(selected).forEach(function (k) {
                    if (selected[k].category === 'плашка' && k !== key) {
                        delete selected[k];
                    }
                });
                $shop.find('.shop-gallery[data-shop-category="плашка"] .shop-selectable').each(function () {
                    if (this !== $image[0]) {
                        $(this).add($(this).parent()).removeClass('is-shop-selected');
                    }
                });
            }

            if (wasSelected) {
                delete selected[key];
                $image.add($wrap).removeClass('is-shop-selected');
            } else {
                selected[key] = {
                    url: url,
                    category: category,
                    price: +$image.attr('data-shop-price')
                };
                $image.add($wrap).addClass('is-shop-selected');
            }

            if (category === 'плашка') {
                sendPreviewUpdate({ bookmarkHtml: wasSelected ? null : buildBookmarkHtml(url) });
            }

            render();
        });

        $shop.on('click', '[data-shop-custom-add]', function () {
            var $row = $(this).closest('[data-shop-custom-row]').clone();
            $row.find('input').val('');
            $(this).closest('[data-shop-custom-rows]').append($row);
            $row.find('input').focus();
            render();
        });

        $shop.on('click', '[data-shop-custom-remove]', function () {
            $(this).closest('[data-shop-custom-row]').remove();
            render();
        });

        $shop.on('input', '[data-shop-custom-input]', render);

        $copy.on('click', function () {
            var text = $output.val();

            function fallbackCopy() {
                $output[0].select();
                try {
                    document.execCommand('copy');
                    $status.text('Сообщение скопировано.');
                } catch (err) {
                    $status.text('Не удалось скопировать — выделите текст вручную.');
                }
                setTimeout(function () { $status.text(''); }, 2000);
            }

            if (navigator.clipboard && navigator.clipboard.writeText) {
                navigator.clipboard.writeText(text).then(function () {
                    $status.text('Сообщение скопировано.');
                    setTimeout(function () { $status.text(''); }, 2000);
                }, fallbackCopy);
            } else {
                fallbackCopy();
            }
        });

        $clear.on('click', function () {
            selected = {};
            $shop.find('.is-shop-selected').removeClass('is-shop-selected');

            $shop.find('[data-shop-custom-group]').each(function () {
                var $rows = $(this).find('[data-shop-custom-row]');
                $rows.first().find('input').val('');
                $rows.slice(1).remove();
            });

            quoteState.comboId = null;
            quoteState.wantsCustomText = false;
            $shop.find('[data-quote-combo]').removeClass('is-active');
            $shop.find('[data-quote-text-toggle]')
                .removeClass('is-active')
                .text('заказать — 200 денег');

            lzState.wantsSave = false;
            $shop.find('[data-lz-toggle]')
                .removeClass('is-active')
                .text('сохранить лз в заявку');

            render();
        });

        var HEX_RE = /^#[0-9a-fA-F]{6}$/;

        function applyCustomBg(hex) {
            if (!HEX_RE.test(hex)) return;
            quoteState.previewBg = hex;
            $shop.find('[data-quote-combo]').removeClass('is-active');
            quoteState.comboId = null;
            $shop.find('[data-quote-picker-bg]').val(hex);
            $shop.find('[data-quote-hex-bg]').val(hex);
            sendPreviewUpdate({ quoteBg: hex });
            render();
        }

        function applyCustomColor(hex) {
            if (!HEX_RE.test(hex)) return;
            quoteState.previewColor = hex;
            $shop.find('[data-quote-combo]').removeClass('is-active');
            quoteState.comboId = null;
            $shop.find('[data-quote-picker-color]').val(hex);
            $shop.find('[data-quote-hex-color]').val(hex);
            sendPreviewUpdate({ quoteColor: hex });
            render();
        }

        $shop.on('input change', '[data-quote-picker-bg]', function () {
            applyCustomBg($(this).val());
        });

        $shop.on('input', '[data-quote-hex-bg]', function () {
            var hex = $.trim($(this).val());
            if (hex && hex.charAt(0) !== '#') hex = '#' + hex;
            if (HEX_RE.test(hex)) applyCustomBg(hex);
        });

        $shop.on('input change', '[data-quote-picker-color]', function () {
            applyCustomColor($(this).val());
        });

        $shop.on('input', '[data-quote-hex-color]', function () {
            var hex = $.trim($(this).val());
            if (hex && hex.charAt(0) !== '#') hex = '#' + hex;
            if (HEX_RE.test(hex)) applyCustomColor(hex);
        });

        $shop.on('click', '[data-quote-combo]', function () {
            var id = $(this).attr('data-quote-combo');
            var combo = getCombo(id);
            if (!combo) return;

            var wasActive = $(this).hasClass('is-active');

            $shop.find('[data-quote-combo]').removeClass('is-active');

            if (wasActive) {
                quoteState.comboId = null;
                quoteState.previewBg = null;
                quoteState.previewColor = null;
                sendPreviewUpdate({ quoteBg: null, quoteColor: null, quoteText: null });
            } else {
                $(this).addClass('is-active');
                quoteState.comboId = id;
                quoteState.previewBg = combo.bg;
                quoteState.previewColor = combo.text;
                $shop.find('[data-quote-picker-bg]').val(combo.bg);
                $shop.find('[data-quote-hex-bg]').val(combo.bg);
                $shop.find('[data-quote-picker-color]').val(combo.text);
                $shop.find('[data-quote-hex-color]').val(combo.text);
                sendPreviewUpdate({
                    quoteBg: quoteState.previewBg,
                    quoteColor: quoteState.previewColor,
                    quoteText: combo.defaultQuote
                });
            }

            render();
        });

        function sanitizeToTextWithBr(rawHtml) {
            var html = String(rawHtml);
            html = html.replace(/<div><br\s*\/?><\/div>/gi, '<br>');
            html = html.replace(/<\/div>\s*<div>/gi, '<br>');
            html = html.replace(/<\/?div[^>]*>/gi, '');
            html = html.replace(/<br\s*\/?>/gi, '<br>');
            html = html.replace(/<(?!br\s*\/?>)[^>]+>/gi, '');
            return html;
        }

        $shop.on('keydown', '[data-quote-text-input]', function (e) {
            if (e.key === 'Enter' || e.keyCode === 13) {
                e.preventDefault();
                document.execCommand('insertLineBreak');
            }
        });

        $shop.on('input', '[data-quote-text-input]', function () {
            var clean = sanitizeToTextWithBr($(this).html());
            quoteState.customText = clean;
            sendPreviewUpdate({ quoteText: clean });
        });

        $shop.on('click', '[data-quote-text-toggle]', function () {
            quoteState.wantsCustomText = !quoteState.wantsCustomText;
            $(this).toggleClass('is-active', quoteState.wantsCustomText);
            $(this).text(quoteState.wantsCustomText
                ? 'добавлено ✓'
                : 'заказать — 200 денег');
            render();
        });

        var $lzInput = $shop.find('[data-lz-input]');

        function sanitizeLzHtml(rawHtml) {
            var html = String(rawHtml);
            html = html.replace(/<script[\s\S]*?<\/script>/gi, '');
            html = html.replace(/<iframe[\s\S]*?<\/iframe>/gi, '');
            html = html.replace(/ on\w+\s*=\s*"[^"]*"/gi, '');
            html = html.replace(/ on\w+\s*=\s*'[^']*'/gi, '');
            html = html.replace(/javascript\s*:/gi, '');
            return html;
        }

        function applyLzValue(rawText) {
            var clean = sanitizeLzHtml(rawText);
            lzState.html = clean;
            sendPreviewUpdate({ fld1Html: clean });
        }

        $shop.on('input', '[data-lz-input]', function () {
            applyLzValue($(this).text());
        });

        $shop.on('paste', '[data-lz-input]', function (e) {
            var clipboard = (e.originalEvent || e).clipboardData || window.clipboardData;
            if (!clipboard) return;
            e.preventDefault();
            var text = clipboard.getData('text/plain') || clipboard.getData('text');
            document.execCommand('insertText', false, text);
        });

        $shop.on('click', '[data-lz-toggle]', function () {
            lzState.wantsSave = !lzState.wantsSave;
            $(this).toggleClass('is-active', lzState.wantsSave);
            $(this).text(lzState.wantsSave
                ? 'сохранено ✓'
                : 'сохранить в заявку');
            render();
        });

        window.addEventListener('message', function (e) {
            if (!e.data || e.data.channel !== CHANNEL) return;

            if (e.data.type === 'profile-shop-bank-value') {
                bankLoaded = true;
                bank = parseInt(e.data.value, 10);
                if (isNaN(bank)) bank = null;
                render();
                return;
            }

            if (e.data.type === 'profile-shop-viewer-value' && e.data.ready) {
                viewerReady = true;
                var profile = e.data.value || {};
                quoteState.defaultText = profile.quote || '';
            }
        });

        var bankRequestTimer = null;

        function requestBank() {
            window.parent.postMessage({
                channel: CHANNEL,
                type: 'profile-shop-bank-request'
            }, '*');
        }

        function startBankRequest() {
            requestBank();

            bankRequestTimer = setInterval(function () {
                if (bankLoaded) {
                    clearInterval(bankRequestTimer);
                    bankRequestTimer = null;
                    return;
                }

                requestBank();
            }, 1000);
        }

        var viewerRequestTimer = null;
        var viewerReady = false;

        function requestViewerProfile() {
            window.parent.postMessage({
                channel: CHANNEL,
                type: 'profile-shop-viewer-request'
            }, '*');
        }

        function startViewerRequest() {
            requestViewerProfile();

            viewerRequestTimer = setInterval(function () {
                if (viewerReady) {
                    clearInterval(viewerRequestTimer);
                    viewerRequestTimer = null;
                    return;
                }

                requestViewerProfile();
            }, 1000);
        }

        render();
        startBankRequest();
        startViewerRequest();
    });
})(jQuery);
</script>
[/html] hide-autor

0

2

всем прив

0

3

Код:
<!-- для магазина личной страницы -->
<script>
(function ($) {
    'use strict';

    if (window.self !== window.top) {
        /* не выполняем логику примерочной внутри собственных служебных iframe */
        return;
    }

    var CHANNEL = 'broken-geis-profile-shop-v4';
    var QUOTE_FIELD_ID = 5; /* потом сменить на 6 */
    var BOOKMARK_FIELD_ID = 4;
    var CHAR_FIELD_ID = 1;

    var viewerProfile = null;
    var viewerProfileReady = false;
    var viewerLoadStarted = false;
    var shopIframeEl = null;

    var previewState = {
        bookmarkHtml: null,
        fld1Html: null,
        quoteText: null,
        quoteBg: null,
        quoteColor: null
    };

    function getBank() {
        if (typeof UserFld2 === 'undefined') {
            return null;
        }
        var bank = parseInt(UserFld2, 10);
        return isNaN(bank) ? null : bank;
    }

    function sendBank(target) {
        var data = {
            channel: CHANNEL,
            type: 'profile-shop-bank-value',
            value: getBank()
        };
        if (target) {
            target.postMessage(data, '*');
            return;
        }
        $('iframe').each(function () {
            if (this.contentWindow) {
                this.contentWindow.postMessage(data, '*');
            }
        });
    }

    function getOwnUserId() {
        var href = null;
        $('a[href*="action=out"]').each(function () {
            if (!href) {
                href = $(this).attr('href');
            }
        });
        if (!href) {
            return null;
        }
        var m = href.match(/[?&]id=(\d+)/);
        return m ? m[1] : null;
    }

    function fetchHiddenPage(url, onReady) {
        var $iframe = $('<iframe>', { src: url, 'aria-hidden': 'true' }).css({
            position: 'absolute',
            left: '-9999px',
            top: '-9999px',
            width: '1px',
            height: '1px',
            visibility: 'hidden'
        });

        $iframe.on('load', function () {
            var doc = null;
            try {
                doc = this.contentWindow.document;
            } catch (err) {
                console.log('[примерочная] нет доступа к documentу iframe (' + url + '):', err);
                $iframe.remove();
                onReady(null);
                return;
            }
            onReady(doc);
            $iframe.remove();
        });

        $('body').append($iframe);
    }

    function parseMainProfile(doc, result) {
        var $doc = $(doc);

        var $avatarImg = $doc.find('#pa-avatar img').first();
        result.avatar = $avatarImg.length ? ($avatarImg.attr('src') || null) : null;

        var $name = $doc.find('#profile-name strong').first();
        result.nickname = $name.length ? $.trim($name.text()) : null;

        var $fld1 = $doc.find('#pa-fld' + CHAR_FIELD_ID).first();
        if ($fld1.length) {
            /* на странице профиля лз идёт с подписью "лз:" — в карточке рядом с постом её нет */
            var $clone = $fld1.clone();
            $clone.find('span').first().remove();
            result.fld1 = $.trim($clone.html());
        } else {
            result.fld1 = null;
        }
    }

    function parseFieldsForm(doc, result) {
        var $doc = $(doc);

        var $bookmarkInput = $doc.find('#fld' + BOOKMARK_FIELD_ID).first();
        result.fld4 = $bookmarkInput.length ? ($bookmarkInput.val() || '') : null;

        var $quoteInput = $doc.find('#fld' + QUOTE_FIELD_ID).first();
        result.quote = $quoteInput.length ? ($quoteInput.val() || '') : null;
    }

    function loadViewerProfile() {
        if (viewerLoadStarted) {
            return;
        }
        viewerLoadStarted = true;

        var uid = getOwnUserId();
        if (!uid) {
            console.log('[примерочная] не нашли свой id (ссылка "Выход" не найдена)');
            viewerLoadStarted = false;
            return;
        }

        var result = {
            userId: uid,
            avatar: null,
            nickname: null,
            fld1: null,
            fld4: null,
            quote: null
        };

        fetchHiddenPage('/profile.php?id=' + uid, function (doc) {
            if (doc) {
                parseMainProfile(doc, result);
            }

            fetchHiddenPage('/profile.php?section=fields&id=' + uid, function (doc2) {
                if (doc2) {
                    parseFieldsForm(doc2, result);
                }

                viewerProfile = result;
                viewerProfileReady = true;

                console.log('[примерочная] профиль зрителя загружен:', viewerProfile);

                applyCardSwap();
            });
        });
    }

    function resolveShopIframe(source) {
        if (shopIframeEl && shopIframeEl.contentWindow === source) {
            return shopIframeEl;
        }
        var found = null;
        $('iframe').each(function () {
            if (this.contentWindow === source) {
                found = this;
            }
        });
        if (found) {
            shopIframeEl = found;
            console.log('[примерочная] нашли iframe магазина, пост:', $(found).closest('.post').attr('id'));
        }
        return found;
    }

    function getShopPostCard() {
        if (!shopIframeEl) {
            return null;
        }
        var $post = $(shopIframeEl).closest('.post');
        if (!$post.length) {
            return null;
        }
        var $card = $post.find('.post-author').first();
        return $card.length ? $card : null;
    }

    function applyCardSwap(attempt) {
        attempt = attempt || 1;

        if (!viewerProfileReady || !viewerProfile) {
            console.log('[примерочная] applyCardSwap: профиль ещё не готов, отмена');
            return;
        }

        var $card = getShopPostCard();

        if (!$card) {
            if (attempt >= 8) {
                console.log('[примерочная] applyCardSwap: так и не нашли iframe магазина/карточку после ' + attempt + ' попыток');
                return;
            }
            setTimeout(function () {
                applyCardSwap(attempt + 1);
            }, 400);
            return;
        }

        console.log('[примерочная] applyCardSwap: карточка найдена (попытка ' + attempt + '), применяю', previewState);

        if (viewerProfile.avatar) {
            var $avatarImg = $card.find('.pa-avatar img').first();
            if ($avatarImg.length) {
                $avatarImg.attr('src', viewerProfile.avatar);
                $avatarImg.attr('alt', viewerProfile.nickname || '');
            } else {
                console.log('[примерочная] .pa-avatar img не найден внутри карточки');
            }
        }

        if (viewerProfile.nickname) {
            var $authorLink = $card.find('.pa-author a').first();
            if ($authorLink.length) {
                $authorLink.text(viewerProfile.nickname);
            } else {
                console.log('[примерочная] .pa-author a не найден внутри карточки');
            }
        }

        var bookmarkHtml = previewState.bookmarkHtml || viewerProfile.fld4;
        if (bookmarkHtml !== null && bookmarkHtml !== undefined) {
            var $fld4 = $card.find('.pa-fld' + BOOKMARK_FIELD_ID).first();
            if ($fld4.length) {
                $fld4.html(bookmarkHtml);
            } else {
                console.log('[примерочная] .pa-fld' + BOOKMARK_FIELD_ID + ' не найден внутри карточки');
            }
        }

        if (viewerProfile.fld1 !== null || previewState.fld1Html !== null) {
            var fld1Html = (previewState.fld1Html !== null && previewState.fld1Html !== undefined)
                ? previewState.fld1Html
                : viewerProfile.fld1;
            var $fld1 = $card.find('.pa-fld' + CHAR_FIELD_ID).first();
            console.log('[примерочная] лз к применению:', fld1Html, '| найден элемент:', $fld1.length);
            if ($fld1.length) {
                $fld1.html(fld1Html);
            } else {
                console.log('[примерочная] .pa-fld' + CHAR_FIELD_ID + ' не найден внутри карточки');
            }
        }

        var quoteText = (previewState.quoteText !== null && previewState.quoteText !== undefined)
            ? previewState.quoteText
            : viewerProfile.quote;
        var $quote = $card.find('.pa-fld' + QUOTE_FIELD_ID).first();
        if ($quote.length) {
            if (quoteText !== null && quoteText !== undefined) {
                $quote.html(quoteText);
            }
            /* пустая строка снимает инлайн-стиль и возвращает дефолтный вид темы */
            $quote.css('background-color', previewState.quoteBg || '');
            $quote.css('color', previewState.quoteColor || '');
        } else {
            console.log('[примерочная] .pa-fld' + QUOTE_FIELD_ID + ' не найден внутри карточки');
        }
    }

    $(window).on('message', function (e) {
        var event = e.originalEvent;
        if (!event.data || event.data.channel !== CHANNEL) {
            return;
        }

        resolveShopIframe(event.source);

        if (event.data.type === 'profile-shop-bank-request') {
            sendBank(event.source);
            return;
        }

        if (event.data.type === 'profile-shop-viewer-request') {
            event.source.postMessage({
                channel: CHANNEL,
                type: 'profile-shop-viewer-value',
                ready: viewerProfileReady,
                value: viewerProfile
            }, '*');
            return;
        }

        if (event.data.type === 'profile-shop-preview-update') {
            var payload = event.data.value || {};
            console.log('[примерочная] получено обновление превью:', payload);

            if (typeof payload.bookmarkHtml !== 'undefined') {
                previewState.bookmarkHtml = payload.bookmarkHtml;
            }
            if (typeof payload.fld1Html !== 'undefined') {
                previewState.fld1Html = payload.fld1Html;
            }
            if (typeof payload.quoteText !== 'undefined') {
                previewState.quoteText = payload.quoteText;
            }
            if (typeof payload.quoteBg !== 'undefined') {
                previewState.quoteBg = payload.quoteBg;
            }
            if (typeof payload.quoteColor !== 'undefined') {
                previewState.quoteColor = payload.quoteColor;
            }

            applyCardSwap();
        }
    });

    $(function () {
        sendBank();
        setTimeout(sendBank, 500);

        loadViewerProfile();
    });
})(jQuery);
</script>

0

4

[html]<!-- новостной блок -->
<style>
@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@1,700&family=Caveat:wght@600&display=swap');
.n1-wrap{position:relative;width:500px;max-width:100%;height:600px;margin:0 auto;background:url('https://upforme.ru/uploads/001c/c4/75/2/195231.png') no-repeat center/cover;font-family:Georgia,serif;color:#b7bcc4;box-sizing:border-box;overflow:hidden;}
.n1-wrap *{box-sizing:border-box;}

.n1-flow{position:absolute;top:16px;left:18px;width:331px;}
.n1-head{display:flex;align-items:center;gap:10px;margin-bottom:6px;}
.n1-logo{width:60px;height:60px;border-radius:7px;object-fit:cover;flex-shrink:0;}
.n1-title{font:500 65px/1 var(--newsf);color:var(--acc-color);letter-spacing:1px;}
.n1-subtitle{font:11px/1 var(--sec-font);letter-spacing:.7px;text-transform:uppercase;color:#d9dde2;margin-bottom:10px;}
.n1-nav{width:329px;display:grid;grid-template-columns:repeat(3,1fr);gap:6px;margin-bottom:8px;}
.n1-nav a{display:block;text-align:center;font:500 12px/1 var(--sec-font);padding:3px 7px;background:var(--acc-color);color:#000!important;text-decoration:none;border-radius:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}
.n1-nav a:hover{background:#f2a944;}
.n1-text{width:331px;height:230px;overflow-y:auto;padding-right:8px;}
.n1-text p{font:12px/1.6 var(--sec-font);text-align:justify;margin:0 0 10px;color:#c3c7cd;}

.n1-tags{position:absolute;top:456px;left:18px;font:italic 600 13px/1 var(--cursivef);text-shadow:1px 1px 3px #000000a1;color:#fff;}
.n1-tags a{position:relative;color:#e8952e;text-decoration:none;margin-right:10px;}
.n1-tags a:hover{color:var(--acc-color);}
.n1-tags a:after{content:attr(data-title);position:absolute;bottom:22px;left:50%;transform:translateX(-50%);width:150px;padding:5px;border-radius:10px;background:#222;color:#fff;font:11px/150% var(--sec-font);text-align:center;text-shadow:none;opacity:0;pointer-events:none;transition:.2s;z-index:99999;}
.n1-tags a:hover:after{opacity:1;}

.n1-quote{position:absolute;top:479px;left:16px;width:310px;text-align:justify;padding:14px 16px;font:600 11px/130% var(--def-font);color:#c3c7cd;}
.n1-quote b{font:italic 600 14px/1 var(--cursivef);color:#fff;}
.n1-quote b a{color:inherit;text-decoration:underline;}
.n1-quote b a:hover{color:var(--acc-color);}
</style>

<div class="n1-wrap">

  <div class="n1-flow">
    <div class="n1-head">
      <img class="n1-logo" src="https://64.media.tumblr.com/de4bcd66c02bb49cadba0e8b0ad5f9c7/c5e5d1394783bf2a-8d/s540x810/246c122a2ade1331dcb3cc0434d14f1ce550d0a9.gif" alt="">
      <div class="n1-title">News 1</div>
    </div>

    <div class="n1-subtitle">сюда текст новости какой-то</div>

    <div class="n1-nav">
      <a href="#">ссылка</a>
      <a href="#">ссылка</a>
      <a href="#">ссылка</a>
    </div>

    <div class="n1-text">
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur fringilla purus et faucibus vehicula. Nunc ut sollicitudin tellus. Suspendisse potenti. Vestibulum id augue lectus. Proin iaculis viverra quam, nec bibendum odio tincidunt eu. Duis malesuada rhoncus nisi, eu dictum arcu volutpat hendrerit. Phasellus molestie orci et est pulvinar dapibus. Duis efficitur at eros ut vulputate.</p>
      <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur fringilla purus et faucibus vehicula. Nunc ut sollicitudin tellus. Suspendisse potenti. Vestibulum id augue lectus. Proin iaculis viverra quam, nec bibendum odio tincidunt eu. Duis malesuada rhoncus nisi, eu dictum arcu volutpat hendrerit.</p>
    </div>
  </div>

  <div class="n1-tags">
    <a href="#" data-title="активист">активист</a>
    <a href="#" data-title="активист">активист</a>
    <a href="#" data-title="постописец">постописец</a>
    <a href="#" data-title="постописец">постописец</a>
  </div>

  <div class="n1-quote">
    <b>пост недели <a href="https://brokengeis.rusff.me/profile.php?id=1">nickname</a>:</b> well-versed in the modern period drama with a twist, director yorgos lanthimos (dogtooth, the favourite) had a simple edict for the costume design of poor things.
  </div>

</div>
[/html]

0

5

[html]
<style>
.guestbook{width:800px;margin:0 auto;padding:24px;box-sizing:border-box;position:relative;background:url(https://forumstatic.ru/files/001c/c4/75/72353.png) center/cover no-repeat;border-radius:35px;overflow:hidden}
.gb-header{display:flex;justify-content:flex-end;margin-bottom:8px}
.gb-title{color:#fff;font:500 75px/1 var(--newsf);}

.gb-body{display:flex;justify-content:flex-end;align-items:flex-start;gap:25px;}

.gb-admins{display:flex;flex-direction:column;gap:8px;width:100px;flex-wrap:nowrap;justify-content:center;}
.gb-admin{text-transform:lowercase;display:flex;align-items:center;justify-content:center;padding:0 13px;font:600 11px/160% 'Noto Sans Mono',sans-serif;height:30px;border-radius:15px;background:#000;color:#fff!important;position:relative;}
.gb-admin:after{content:attr(data-title);position:absolute;top:38px;left:50%;transform:translateX(-50%);width:150px;padding:5px;border-radius:10px;background:#222;color:#fff;font:11px/160% 'Noto Sans Mono',monospace;text-align:center;opacity:0;pointer-events:none;transition:.2s;z-index:99999;}
.gb-admin:hover:after{opacity:1}

.gb-top{display:flex;gap:20px}

.gb-left{width:180px;height:258px;padding:0;background:rgba(35,35,35,.65);backdrop-filter:blur(5px);border-radius:18px;box-sizing:border-box;overflow:hidden;}
.gb-right{width:427px;height:258px;padding:0;background:rgba(35,35,35,.65);backdrop-filter:blur(5px);border-radius:18px;box-sizing:border-box;overflow:hidden;}

.gb-bottom{width:427px;margin:18px 0 0 auto}
.gb-copy{width:427px;height:34px;border:0;border-radius:10px;background:#222;color:#fff;cursor:pointer;margin-bottom:8px}
.gb-code{width:427px;height:51px;border:0;border-radius:12px;padding:15px;box-sizing:border-box;resize:none}

.gb-left-text{width:159px;height:220px;margin:10px 0 0 10px;overflow:auto;color:#ececec;font: 300 13px / 120% "Cormorant Infant", serif;white-space:normal;word-break:break-word;}
.gb-right-text{width:395px;padding-right:6px;height:223px;margin:13px 0 0 18px;overflow:auto;color:#ececec;font: 300 13px / 120% "Cormorant Infant", serif;text-align:justify;white-space:normal;word-break:break-word;}
</style>

<div class="guestbook">

<div class="gb-header">
  <div class="gb-title">Gostevaya</div>
</div>

<div class="gb-body">

  <div class="gb-admins">
    <a href="https://brokengeis.rusff.me/profile.php?id=4" class="gb-admin" data-title="дизайнер и графин">хускар</a>
    <a href="https://brokengeis.rusff.me/profile.php?id=8" class="gb-admin" data-title="питомец амс">вивиан</a>
    <a href="https://brokengeis.rusff.me/profile.php?id=6" class="gb-admin" data-title="спец по матчасти">томас</a>
    <a href="https://brokengeis.rusff.me/profile.php?id=64" class="gb-admin" data-title="квестоплет">кардан</a>
    <a href="https://brokengeis.rusff.me/profile.php?id=7" class="gb-admin" data-title="техподдержка">амелия</a>
    <a href="https://brokengeis.rusff.me/profile.php?id=194" class="gb-admin" data-title="аниматор">райли</a>
  </div>

  <div class="gb-top">
    <div class="gb-left">
      <div class="gb-left-text">
        <b>админский произвол:</b>
        <br>adam brody
        <br>cameron chapman
        <br>daisy edgar-jones
        <br>lewis pullman
        <br>max verstappen
        <br>samara weaving
        <br>sarah pidgeon
        <br>victoria pedretti
        <br>

        <br>
        <b>до 31.08 (включительно):</b>
        ben barnes — <a href="https://brokengeis.rusff.me/profile.php?id=266">nathaniel quill</a><br>
        eva green — <a href="https://brokengeis.rusff.me/profile.php?id=273">enya</a><br>

        <br>
        <b>до 01.09 (включительно):</b>
        anton shastoon — <a href="https://brokengeis.rusff.me/profile.php?id=270">blue brynmor</a><br>
        herman tømmeraas — <a href="https://brokengeis.rusff.me/profile.php?id=270">blue brynmor</a><br>
        jack cameron kay — <a href="https://brokengeis.rusff.me/profile.php?id=267">eli goffman</a><br>

        <br>
        <b>до 06.09 (включительно):</b>
        james norton — <a href="https://brokengeis.rusff.me/profile.php?id=183">религиозный психоз</a><br>
      </div>
    </div>

    <div class="gb-right">
      <div class="gb-right-text">
        добро пожаловать на ФРПГ в жанре мистика (с вплетением рас и кельтской мифологии). проходите, располагайтесь, переобувайтесь в тапочки, выбирайте любимые лица и создавайте уникальные истории.
        <br><br>
        внешности для зарегистрированных держим 3 дня (плюс два продления по 2 дня), а гостям, друзьям и на подумать — сутки.
        <br><br>
        если коротко о нас, то мы располагаемся на британском архипелаге и занимаем всю территорию ирландии и британии. здесь пытаются ужиться между собой вампиры, оборотни, банши, ведьмы, призраки, фейри, мэрроу и обычные люди (в будущем планируем пополнение рас). проблемы экономического кризиса и престолонаследия давно уже не волнуют всех этих существ, в отличие от выживания под взором разгневанных богов.
        <br><br>
        божества, что раньше покровительствовали всем этим расам, обернулись против них и поставили под вопрос само существование своих подопечных. теперь им предстоит следить за своим поведением, зарабатывать очки милости или получать дебаффы гнева богов. помните, что любое решение будет иметь последствие.
        <br><br>
        подробнее ознакомиться с матчастью форума, узнать нас поближе и просто понять, что вообще здесь происходит, можно в специальных темах по навигации в подфорумах или шапке форума.<br>
      </div>
    </div>

  </div>
</div>

<div class="gb-bottom">
</div>
</div>
[/html]
[hideprofile]

[html]
<div class="gbform-widget" data-role="gbform-root" data-ready="false">

  <div class="gbform-fields">
    <input type="text" class="gbform-input" data-role="gbform-name" placeholder="имя внешности" maxlength="80">
    <input type="text" class="gbform-input" data-role="gbform-nick" placeholder="ваш ник" maxlength="80">
    <input type="text" class="gbform-input" data-role="gbform-link" placeholder="ссылка на профиль" maxlength="200">
  </div>

  <div class="gbform-actions">
    <button type="button" data-action="gbform-generate">сформировать код</button>
    <button type="button" data-action="gbform-copy">копировать</button>
  </div>

  <div class="gbform-output" contenteditable="true" data-role="gbform-output"></div>

  <div class="gbform-status" data-role="gbform-status"></div>

</div>

<script>
(function () {
  'use strict';

  function byRole(root, name) {
    return root ? root.querySelector('[data-role="' + name + '"]') : null;
  }

  function esc(s) {
    return String(s).replace(/[&<>"]/g, function (c) {
      return c === '&' ? '&amp;' : c === '<' ? '&lt;' : c === '>' ? '&gt;' : '&quot;';
    });
  }

  function findBtn(el, root) {
    while (el && el !== root) {
      if (el.getAttribute && el.getAttribute('data-action')) { return el; }
      el = el.parentNode;
    }
    return null;
  }

  function status(root, text) {
    var s = byRole(root, 'gbform-status');
    if (s) { s.textContent = text || ''; }
  }

  function generate(root) {
    var name = byRole(root, 'gbform-name'), nick = byRole(root, 'gbform-nick'),
        link = byRole(root, 'gbform-link'), out = byRole(root, 'gbform-output');
    if (!name || !nick || !link || !out) { return; }

    var n = name.value.replace(/^\s+|\s+$/g, ''),
        k = nick.value.replace(/^\s+|\s+$/g, ''),
        l = link.value.replace(/^\s+|\s+$/g, '');

    if (!n || !k) {
      root.setAttribute('data-generated', 'false');
      status(root, 'заполните имя внешности и ник');
      return;
    }

    var nickPart = l ? ('<a href="' + esc(l) + '">' + esc(k) + '</a>') : esc(k);
    out.textContent = esc(n) + ' — ' + nickPart + '<br>';
    root.setAttribute('data-generated', 'true');
    status(root, 'готово, можно копировать');
  }

  function copy(root) {
    var out = byRole(root, 'gbform-output');
    if (!out || root.getAttribute('data-generated') !== 'true' || !out.textContent) {
      status(root, 'сначала нажмите «сформировать код»');
      return;
    }
    var text = out.textContent, doc = root.ownerDocument, win = doc.defaultView;

    function legacy() {
      try {
        var range = doc.createRange();
        range.selectNodeContents(out);
        var sel = win.getSelection();
        sel.removeAllRanges();
        sel.addRange(range);
        var ok = doc.execCommand('copy');
        sel.removeAllRanges();
        status(root, ok ? 'скопировано' : 'не удалось скопировать автоматически');
      } catch (e) {
        status(root, 'не удалось скопировать автоматически');
      }
    }

    if (win.navigator && win.navigator.clipboard && win.navigator.clipboard.writeText) {
      win.navigator.clipboard.writeText(text).then(function () { status(root, 'скопировано'); }, legacy);
    } else {
      legacy();
    }
  }

  function onClick(ev) {
    var root = ev.currentTarget, btn = findBtn(ev.target, root);
    if (!btn) { return; }
    var action = btn.getAttribute('data-action');
    if (action === 'gbform-generate') { generate(root); }
    else if (action === 'gbform-copy') { copy(root); }
  }

  function initOne(root) {
    if (!root || root.getAttribute('data-ready') === 'true') { return; }
    if (!byRole(root, 'gbform-name') || !byRole(root, 'gbform-nick') || !byRole(root, 'gbform-link') || !byRole(root, 'gbform-output')) { return; }
    root.setAttribute('data-ready', 'true');
    root.setAttribute('data-generated', 'false');
    root.addEventListener('click', onClick, false);
  }

  function init() {
    var roots = document.querySelectorAll('[data-role="gbform-root"]');
    for (var i = 0; i < roots.length; i++) { initOne(roots[i]); }
  }

  document.addEventListener('DOMContentLoaded', init, false);
  setTimeout(init, 300);
  setTimeout(init, 800);
  setTimeout(init, 1500);
  init();

}());
</script>
[/html]

0

6

[html]<!-- генератор новостного блока -->
<style>
.n1gen{max-width:900px;margin:0 auto;font-family:Arial,Helvetica,sans-serif;color:#1c1c1c;}
.n1gen *{box-sizing:border-box;}
.n1gen-grid{display:flex;flex-wrap:wrap;gap:16px;}
.n1gen-col{flex:1 1 400px;min-width:320px;}
.n1gen-panel{border:1px solid #333;border-radius:8px;padding:14px;margin-bottom:14px;background:#f7f7f7;}
.n1gen-panel legend{font-weight:bold;font-size:12px;padding:0 6px;color:#a05a00;}
.n1gen-field{margin-bottom:12px;}
.n1gen-field:last-child{margin-bottom:0;}
.n1gen-label{display:block;font-size:11px;font-weight:bold;color:#555;margin-bottom:4px;text-transform:uppercase;}
.n1gen input[type="text"]{width:100%;padding:6px 8px;border:1px solid #999;border-radius:4px;font-size:13px;font-family:inherit;}
.n1gen-editable{width:100%;min-height:90px;border:1px solid #999;border-radius:4px;padding:8px;font-size:13px;background:#fff;line-height:1.5;overflow-y:auto;}
.n1gen-editable:focus{outline:2px solid #e8952e;}
.n1gen-hint{font-size:11px;color:#777;margin-top:4px;}
.n1gen-toolbar{display:flex;gap:4px;margin-bottom:6px;}
.n1gen-tbtn{width:28px;height:26px;border:1px solid #999;border-radius:4px;background:#fff;cursor:pointer;font-size:12px;}
.n1gen-tbtn:hover{border-color:#e8952e;color:#e8952e;}
.n1gen-row{display:flex;gap:6px;margin-bottom:6px;align-items:center;}
.n1gen-row input{flex:1;}
.n1gen-rowdel{width:26px;height:26px;flex-shrink:0;border:1px solid #a33;border-radius:4px;background:#fee;color:#a33;cursor:pointer;}
.n1gen-addbtn{width:100%;padding:6px;border:1px dashed #e8952e;border-radius:4px;background:#fff;color:#a05a00;cursor:pointer;font-size:12px;}
.n1gen-pairlabel{font-size:11px;color:#777;margin:8px 0 4px;}
.n1gen-outhead{display:flex;justify-content:space-between;align-items:center;margin-bottom:6px;}
.n1gen-copybtn{padding:8px 16px;border:0;border-radius:4px;background:#e8952e;color:#1a1200;font-weight:bold;cursor:pointer;font-size:13px;}
.n1gen-copybtn.n1gen-copied{background:#4caf6b;color:#063;}
.n1gen-out{width:100%;max-height:100px;overflow:auto;background:#111;color:#9fdca0;border:1px solid #333;border-radius:6px;padding:10px;font-family:monospace;font-size:11px;white-space:pre-wrap;word-break:break-all;}
</style>

<div class="n1gen" data-role="n1gen-root">
  <div class="n1gen-grid">

    <div class="n1gen-col">

      <div class="n1gen-field">
        <label class="n1gen-label">Ссылка на лого / аватар</label>
        <input type="text" data-role="logo" value="гифка админа">
      </div>

      <div class="n1gen-field">
        <label class="n1gen-label">Заголовок</label>
        <input type="text" data-role="title" value="News 1">
      </div>

      <div class="n1gen-field">
        <label class="n1gen-label">Подзаголовок</label>
        <input type="text" data-role="subtitle" value="сюда текст новости какой-то">
      </div>

      <fieldset class="n1gen-panel">
        <legend>Ссылки навигации</legend>
        <div data-role="nav-list"></div>
        <button type="button" class="n1gen-addbtn" data-action="add-nav">+ добавить ссылку</button>
      </fieldset>

      <div class="n1gen-field">
        <label class="n1gen-label">Текст новости</label>
        <div class="n1gen-toolbar">
          <button type="button" class="n1gen-tbtn" data-action="fmt-b" data-target="text" style="font-weight:bold;">Ж</button>
          <button type="button" class="n1gen-tbtn" data-action="fmt-i" data-target="text" style="font-style:italic;">К</button>
          <button type="button" class="n1gen-tbtn" data-action="fmt-s" data-target="text" style="text-decoration:line-through;">З</button>
          <button type="button" class="n1gen-tbtn" data-action="fmt-link" data-target="text">🔗</button>
          <button type="button" class="n1gen-tbtn" data-action="fmt-img" data-target="text">🖼</button>
        </div>
        <div class="n1gen-editable" contenteditable="true" data-role="text"></div>
        <div class="n1gen-hint">Выдели текст и нажми кнопку. Пустая строка = новый абзац, одиночный Enter = &lt;br&gt;.</div>
      </div>

    </div>

    <div class="n1gen-col">

      <fieldset class="n1gen-panel">
        <legend>Активисты и постописцы</legend>
        <div class="n1gen-hint">2 активиста + 2 постописца. Сначала ник, потом ссылка на профиль. Тег добавится сам.</div>

        <div class="n1gen-pairlabel">Активист 1</div>
        <div class="n1gen-row">
          <input type="text" placeholder="ник" data-role="act1-nick">
          <input type="text" placeholder="ссылка на профиль" data-role="act1-link" value="#">
        </div>

        <div class="n1gen-pairlabel">Активист 2</div>
        <div class="n1gen-row">
          <input type="text" placeholder="ник" data-role="act2-nick">
          <input type="text" placeholder="ссылка на профиль" data-role="act2-link" value="#">
        </div>

        <div class="n1gen-pairlabel">Постописец 1</div>
        <div class="n1gen-row">
          <input type="text" placeholder="ник" data-role="post1-nick">
          <input type="text" placeholder="ссылка на профиль" data-role="post1-link" value="#">
        </div>

        <div class="n1gen-pairlabel">Постописец 2</div>
        <div class="n1gen-row">
          <input type="text" placeholder="ник" data-role="post2-nick">
          <input type="text" placeholder="ссылка на профиль" data-role="post2-link" value="#">
        </div>
      </fieldset>

      <fieldset class="n1gen-panel">
        <legend>Пост недели / цитата</legend>
        <div class="n1gen-field">
          <label class="n1gen-label">Ник (текст ссылки)</label>
          <input type="text" data-role="quote-nick" value="nickname">
        </div>
        <div class="n1gen-field">
          <label class="n1gen-label">Ссылка на профиль</label>
          <input type="text" data-role="quote-link" value="#">
        </div>
        <div class="n1gen-field">
          <label class="n1gen-label">Текст цитаты</label>
          <div class="n1gen-toolbar">
            <button type="button" class="n1gen-tbtn" data-action="fmt-b" data-target="quote" style="font-weight:bold;">Ж</button>
            <button type="button" class="n1gen-tbtn" data-action="fmt-i" data-target="quote" style="font-style:italic;">К</button>
            <button type="button" class="n1gen-tbtn" data-action="fmt-s" data-target="quote" style="text-decoration:line-through;">З</button>
            <button type="button" class="n1gen-tbtn" data-action="fmt-link" data-target="quote">🔗</button>
          </div>
          <div class="n1gen-editable" style="min-height:60px;" contenteditable="true" data-role="quote"></div>
        </div>
      </fieldset>

      <div class="n1gen-panel">
        <div class="n1gen-outhead">
          <label class="n1gen-label" style="margin:0;">Готовый код</label>
          <button type="button" class="n1gen-copybtn" data-action="copy">Скопировать код</button>
        </div>
        <div class="n1gen-out" contenteditable="true" data-role="output"></div>
      </div>

    </div>

  </div>
</div>

<script>
(function(){
'use strict';

function bbOpen(){ return String.fromCharCode(91); }
function bbClose(){ return String.fromCharCode(93); }

function escAttr(s){
  return String(s).replace(/&/g,'&amp;').replace(/"/g,'&quot;');
}

function nodeVal(root, role){
  var el = root.querySelector('[data-role="' + role + '"]');
  return el ? el.value : '';
}

function editableHtml(root, role){
  var el = root.querySelector('[data-role="' + role + '"]');
  return el ? el.innerHTML : '';
}

function trimBr(html){
  html = html.replace(/^(?:<br\s*\/?>|\s)+/i, '');
  html = html.replace(/(?:<br\s*\/?>|\s)+$/i, '');
  return html;
}

function textToParas(html){
  html = trimBr(html);
  if (html === '') return '';
  var parts = html.split(/(?:<br\s*\/?>\s*){2,}/i);
  var out = '';
  for (var i = 0; i < parts.length; i++){
    var p = trimBr(parts[i]);
    if (p === '') continue;
    out += '<p>' + p + '</p>\n    ';
  }
  return out.replace(/\s+$/, '');
}

function insertBrAtCursor(el){
  var sel = window.getSelection();
  if (!sel.rangeCount) return;
  var range = sel.getRangeAt(0);
  if (!el.contains(range.commonAncestorContainer)) return;
  range.deleteContents();
  var br = document.createElement('br');
  range.insertNode(br);
  range.setStartAfter(br);
  range.setEndAfter(br);
  range.collapse(true);
  sel.removeAllRanges();
  sel.addRange(range);
}

function wrapSelection(el, tagName){
  el.focus();
  var sel = window.getSelection();
  if (!sel.rangeCount) return;
  var range = sel.getRangeAt(0);
  if (!el.contains(range.commonAncestorContainer)) return;
  if (range.collapsed) return;
  var wrapper = document.createElement(tagName);
  try{
    range.surroundContents(wrapper);
  }catch(e){
    var content = range.extractContents();
    wrapper.appendChild(content);
    range.insertNode(wrapper);
  }
  sel.removeAllRanges();
}

function insertLink(el){
  el.focus();
  var sel = window.getSelection();
  if (!sel.rangeCount) return;
  var range = sel.getRangeAt(0);
  if (!el.contains(range.commonAncestorContainer)) return;
  var url = window.prompt('Введите URL ссылки:', 'https://');
  if (url === null) return;
  var a = document.createElement('a');
  a.setAttribute('href', url);
  if (range.collapsed){
    a.textContent = 'текст ссылки';
    range.insertNode(a);
  } else {
    try{
      range.surroundContents(a);
    }catch(e){
      var content = range.extractContents();
      a.appendChild(content);
      range.insertNode(a);
    }
  }
  sel.removeAllRanges();
}

function insertImage(el){
  el.focus();
  var sel = window.getSelection();
  if (!sel.rangeCount) return;
  var range = sel.getRangeAt(0);
  if (!el.contains(range.commonAncestorContainer)) return;
  var url = window.prompt('Введите URL изображения:', 'https://');
  if (url === null) return;
  var img = document.createElement('img');
  img.setAttribute('src', url);
  img.setAttribute('alt', '');
  range.deleteContents();
  range.insertNode(img);
  sel.removeAllRanges();
}

function personTag(root, nickRole, linkRole, label){
  var nick = nodeVal(root, nickRole);
  var link = nodeVal(root, linkRole) || '#';
  if (!nick) return '';
  return '    <a href="' + link + '" data-title="' + escAttr(nick) + '">' + label + '</a>\n';
}

function buildNavCode(root){
  var rows = root.querySelectorAll('[data-role="nav-list"] [data-navrow]');
  var out = '';
  for (var i = 0; i < rows.length; i++){
    var t = rows[i].querySelector('[data-role="nav-text"]').value || 'ссылка';
    var h = rows[i].querySelector('[data-role="nav-href"]').value || '#';
    out += '      <a href="' + h + '">' + t + '</a>\n';
  }
  return out;
}

var BG_URL = 'https://upforme.ru/uploads/001c/c4/75/2/195231.png';

function render(root){
  var logo = nodeVal(root, 'logo');
  var title = nodeVal(root, 'title');
  var subtitle = nodeVal(root, 'subtitle');
  var quoteNick = nodeVal(root, 'quote-nick');
  var quoteLink = nodeVal(root, 'quote-link') || '#';
  var quoteHtml = trimBr(editableHtml(root, 'quote'));
  var textParas = textToParas(editableHtml(root, 'text'));
  var navCode = buildNavCode(root);
  var tagsCode =
    personTag(root, 'act1-nick', 'act1-link', 'активист') +
    personTag(root, 'act2-nick', 'act2-link', 'активист') +
    personTag(root, 'post1-nick', 'post1-link', 'постописец') +
    personTag(root, 'post2-nick', 'post2-link', 'постописец');

  var q = bbOpen(), c = bbClose();

  var code =
q + 'html' + c + '<!-- новостной блок -->\n' +
'<style>\n' +
"@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@1,700&family=Caveat:wght@600&display=swap');\n" +
".n1-wrap{position:relative;width:500px;max-width:100%;height:600px;margin:0 auto;background:url('" + BG_URL + "') no-repeat center/cover;font-family:Georgia,serif;color:#b7bcc4;box-sizing:border-box;overflow:hidden;}\n" +
".n1-wrap *{box-sizing:border-box;}\n" +
".n1-flow{position:absolute;top:16px;left:18px;width:331px;}\n" +
".n1-head{display:flex;align-items:center;gap:10px;margin-bottom:6px;}\n" +
".n1-logo{width:60px;height:60px;border-radius:7px;object-fit:cover;flex-shrink:0;}\n" +
".n1-title{font:500 65px/1 var(--newsf);color:var(--acc-color);letter-spacing:1px;}\n" +
".n1-subtitle{font:11px/1 var(--sec-font);letter-spacing:.7px;text-transform:uppercase;color:#d9dde2;margin-bottom:10px;}\n" +
".n1-nav{width:329px;height:44px;align-content:start;overflow:hidden;display:grid;grid-template-columns:repeat(3,1fr);gap:6px;margin-bottom:8px;}\n" +
".n1-nav a{display:block;text-align:center;font:500 12px/1 var(--sec-font);padding:3px 7px;background:var(--acc-color);color:#000!important;text-decoration:none;border-radius:12px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;}\n" +
".n1-nav a:hover{background:#f2a944;}\n" +
".n1-text{width:331px;height:230px;overflow-y:auto;padding-right:8px;}\n" +
".n1-text p{font:12px/1.6 var(--sec-font);text-align:justify;margin:0 0 10px;color:#c3c7cd;}\n" +
".n1-tags{position:absolute;top:456px;left:18px;font:italic 600 13px/1 var(--cursivef);text-shadow:1px 1px 3px #000000a1;color:#fff;}\n" +
".n1-tags a{position:relative;color:#e8952e;text-decoration:none;margin-right:10px;}\n" +
".n1-tags a:hover{color:var(--acc-color);}\n" +
".n1-tags a:after{content:attr(data-title);position:absolute;bottom:22px;left:50%;transform:translateX(-50%);width:150px;padding:5px;border-radius:10px;background:#222;color:#fff;font:11px/150% var(--sec-font);text-align:center;text-shadow:none;opacity:0;pointer-events:none;transition:.2s;z-index:99999;}\n" +
".n1-tags a:hover:after{opacity:1;}\n" +
".n1-quote{position:absolute;top:479px;left:16px;width:310px;text-align:justify;padding:14px 16px;font:600 11px/130% var(--def-font);color:#c3c7cd;}\n" +
".n1-quote b{font:italic 600 14px/1 var(--cursivef);color:#fff;}\n" +
".n1-quote b a{color:inherit;text-decoration:underline;}\n" +
".n1-quote b a:hover{color:var(--acc-color);}\n" +
'</style>\n\n' +
'<div class="n1-wrap">\n\n' +
'  <div class="n1-flow">\n' +
'    <div class="n1-head">\n' +
'      <img class="n1-logo" src="' + logo + '" alt="">\n' +
'      <div class="n1-title">' + title + '</div>\n' +
'    </div>\n\n' +
'    <div class="n1-subtitle">' + subtitle + '</div>\n\n' +
'    <div class="n1-nav">\n' + navCode +
'    </div>\n\n' +
'    <div class="n1-text">\n      ' + textParas + '\n    </div>\n' +
'  </div>\n\n' +
'  <div class="n1-tags">\n' + tagsCode +
'  </div>\n\n' +
'  <div class="n1-quote">\n' +
'    <b>пост недели <a href="' + quoteLink + '">' + quoteNick + '</a>:</b> ' + quoteHtml + '\n' +
'  </div>\n\n' +
'</div>\n' +
q + '/html' + c;

  var out = root.querySelector('[data-role="output"]');
  if (out) out.textContent = code;
}

var navRowCount = 0;

function addNavRow(root){
  navRowCount++;
  var list = root.querySelector('[data-role="nav-list"]');
  var row = document.createElement('div');
  row.className = 'n1gen-row';
  row.setAttribute('data-navrow', navRowCount);
  row.innerHTML =
    '<input type="text" placeholder="текст ссылки" data-role="nav-text" value="ссылка">' +
    '<input type="text" placeholder="URL" data-role="nav-href" value="#">' +
    '<button type="button" class="n1gen-rowdel" data-action="del-nav">×</button>';
  list.appendChild(row);
  bindRowInputs(root, row);
  render(root);
}

function bindRowInputs(root, scope){
  var inputs = scope.querySelectorAll('input');
  for (var i = 0; i < inputs.length; i++){
    inputs[i].addEventListener('input', function(){ render(root); });
  }
  var delBtn = scope.querySelector('[data-action="del-nav"]');
  if (delBtn){
    delBtn.onclick = function(){
      scope.parentNode.removeChild(scope);
      render(root);
    };
  }
}

function copyCode(root){
  var out = root.querySelector('[data-role="output"]');
  var btn = root.querySelector('[data-action="copy"]');
  var text = out.textContent;

  function done(){
    btn.textContent = 'Скопировано!';
    btn.className = 'n1gen-copybtn n1gen-copied';
    window.setTimeout(function(){
      btn.textContent = 'Скопировать код';
      btn.className = 'n1gen-copybtn';
    }, 1500);
  }

  function fallback(){
    try{
      var range = document.createRange();
      range.selectNodeContents(out);
      var sel = window.getSelection();
      sel.removeAllRanges();
      sel.addRange(range);
      document.execCommand('copy');
      sel.removeAllRanges();
      done();
    }catch(e){
      window.alert('Не удалось скопировать автоматически — выдели код вручную и скопируй (Ctrl+C).');
    }
  }

  if (navigator.clipboard && navigator.clipboard.writeText){
    navigator.clipboard.writeText(text).then(done, fallback);
  } else {
    fallback();
  }
}

function initGen(){
  var root = document.querySelector('[data-role="n1gen-root"]');
  if (!root) return;
  if (root.getAttribute('data-ready') === 'true') return;

  // текстовые поля
  var inputs = root.querySelectorAll('input[type="text"]');
  for (var i = 0; i < inputs.length; i++){
    if (!inputs[i].closest('[data-navrow]')){
      inputs[i].addEventListener('input', function(){ render(root); });
    }
  }

  // редактируемые блоки (текст новости, цитата)
  var editables = root.querySelectorAll('.n1gen-editable[data-role]');
  for (var j = 0; j < editables.length; j++){
    (function(el){
      el.addEventListener('input', function(){ render(root); });
      el.addEventListener('keydown', function(e){
        var key = e.which || e.keyCode;
        if (key === 13){
          e.preventDefault();
          insertBrAtCursor(el);
          render(root);
        }
      });
    })(editables[j]);
  }

  // кнопки форматирования
  var fmtButtons = root.querySelectorAll('[data-action^="fmt-"]');
  for (var k = 0; k < fmtButtons.length; k++){
    (function(btn){
      btn.onclick = function(){
        var target = btn.getAttribute('data-target');
        var el = root.querySelector('.n1gen-editable[data-role="' + target + '"]');
        if (!el) return;
        var action = btn.getAttribute('data-action');
        if (action === 'fmt-b') wrapSelection(el, 'b');
        else if (action === 'fmt-i') wrapSelection(el, 'i');
        else if (action === 'fmt-s') wrapSelection(el, 's');
        else if (action === 'fmt-link') insertLink(el);
        else if (action === 'fmt-img') insertImage(el);
        render(root);
      };
    })(fmtButtons[k]);
  }

  // добавление строки навигации
  var addNavBtn = root.querySelector('[data-action="add-nav"]');
  if (addNavBtn){
    addNavBtn.onclick = function(){ addNavRow(root); };
  }

  // копирование
  var copyBtn = root.querySelector('[data-action="copy"]');
  if (copyBtn){
    copyBtn.onclick = function(){ copyCode(root); };
  }

  // стартовые 3 строки навигации
  addNavRow(root);
  addNavRow(root);
  addNavRow(root);

  render(root);
  root.setAttribute('data-ready', 'true');
}

if (document.readyState === 'loading'){
  document.addEventListener('DOMContentLoaded', initGen);
} else {
  initGen();
}
window.setTimeout(initGen, 300);
window.setTimeout(initGen, 800);
window.setTimeout(initGen, 1500);

})();
</script>
[/html]

0


Вы здесь » autumn geis test » Тестовый форум » важная тема


Рейтинг форумов | Создать форум бесплатно