Mistake Not

Объявление

Постов за июнь: 337 | Эпизодов закрыто: 4
crossover | 18+
Активисты
Blade Blade
Dan Heng Dan Heng
Fingon Fingon
Wei Wuxian Wei Wuxian

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

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


Вы здесь » Mistake Not » Falling Outside the Normal Moral Constraints » script


script

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

1

Теги: Test

Подпись автора

Подпись - это небольшая приписка, прилагаемая к вашим сообщениям. Это может быть все, что вам нравится. Например

1
96

0

2

Код:
<!-- SPRING by @vatueil -->
<script>
/*
 * christmas ornaments
 * version: v1.1.0
 * copyright (c) 2026 @vatueil for executecross.ru
 * need <div id="springOrnaments"></div> in html bottom
 */


class UniqueSelector {
  static getElementSelector(element) {
    if (element._uniqueSelectorCache) {
      return element._uniqueSelectorCache;
    }
    
    if (element.id) {
      const selector = `#${CSS.escape(element.id)}`;
      element._uniqueSelectorCache = selector;
      return selector;
    }
    
    if (element.tagName === 'BODY') {
      element._uniqueSelectorCache = 'body';
      return 'body';
    }
    
    let selector = element.tagName.toLowerCase();
    
    if (element.className && typeof element.className === 'string') {
      const className = element.className.trim();
      if (className) {
        const classList = className.split(/\s+/).filter(cls => 
          cls && cls !== 'highlight' && cls !== 'decoratable' && cls !== 'ornaments-container'
        );
        if (classList.length > 0) {
          selector += '.' + classList.map(c => CSS.escape(c)).join('.');
        }
      }
    }
    
    const parent = element.parentNode;
    if (parent && parent.children && parent.children.length > 1) {
      let index = 1;
      for (let i = 0; i < parent.children.length; i++) {
        if (parent.children[i] === element) {
          selector += `:nth-child(${index})`;
          break;
        }
        index++;
      }
    }
    
    element._uniqueSelectorCache = selector;
    return selector;
  }

  static getUniqueSelector(element) {
    let selector = this.getElementSelector(element);
    let elements;
    
    try {
      elements = document.querySelectorAll(selector);
    } catch (e) {
      return selector;
    }
    
    if (elements.length === 1) {
      return selector;
    }

    let bestSelector = selector;
    let bestCount = elements.length;

    const queryCache = new Map();
    const queryElements = (sel) => {
      if (queryCache.has(sel)) {
        return queryCache.get(sel);
      }
      try {
        const result = document.querySelectorAll(sel);
        queryCache.set(sel, result);
        return result;
      } catch {
        return [];
      }
    };

    let parent = element.parentElement;
    while (parent) {
      if (parent.id) {
        const parentSelector = `#${CSS.escape(parent.id)} ${selector}`;
        elements = queryElements(parentSelector);
        if (elements.length === 1) {
          UniqueSelector.clearCache(parent);
          return parentSelector;
        }
        if (elements.length < bestCount) {
          bestSelector = parentSelector;
          bestCount = elements.length;
        }
      }
      parent = parent.parentElement;
    }

    const usedParentClasses = new Set();
    parent = element.parentElement;
    
    while (parent && parent.tagName !== 'BODY') {
      const classList = Array.from(parent.classList).filter(c => 
        !usedParentClasses.has(c) && 
        c !== 'highlight' && 
        c !== 'decoratable' && 
        c !== 'ornaments-container'
      );
      
      for (const cls of classList) {
        const parentSelector = `.${CSS.escape(cls)} ${selector}`;
        elements = queryElements(parentSelector);
        if (elements.length === 1) {
          UniqueSelector.clearCache(parent);
          return parentSelector;
        }
        if (elements.length < bestCount) {
          bestSelector = parentSelector;
          bestCount = elements.length;
        }
        usedParentClasses.add(cls);
      }
      parent = parent.parentElement;
    }

    parent = element.parentElement;
    let selectors = [selector];
    
    while (parent && parent.tagName !== 'BODY') {
      const parentSelector = this.getElementSelector(parent);
      const newSelectors = [];
      
      for (const sel of selectors) {
        const newSelector = `${parentSelector} ${sel}`;
        newSelectors.push(newSelector);
        elements = queryElements(newSelector);
        if (elements.length === 1) {
          UniqueSelector.clearCache(parent);
          return newSelector;
        }
        if (elements.length < bestCount) {
          bestSelector = newSelector;
          bestCount = elements.length;
        }
      }
      
      selectors = newSelectors;
      parent = parent.parentElement;
    }

    return bestSelector;
  }

  static clearCache(element) {
    delete element._uniqueSelectorCache;
  }
}

class ForumDecorator {
  constructor(config) {
    this.config = {
      decoratableSelectors: ['.pa-author, .pa-posts, .pa-fld3, .pa-fld2', '#profile-name, #pa-fld3 strong, #pa-fld2 strong, #pa-posts a', '#pun-navlinks li:not(#navlogout)', '.crumbs a', '.pa-fld1 .lz-name', '#post-form h2 span', '.permalink, .postlink a', '.tclcon h3 a, .tcl h3 a, .pf > a, .lastpost-link, .avatar-image', '.button', '#pun-status .container', ".statscon span", "h1 span", ".announce-btn, #pun-announcement .character-wrapper"],
      maxOrnaments: 2025,
      ornamentCategories: [
        {
          name: 'Цветы',
          images: [
            "https://forumstatic.ru/files/001b/f1/8a/42536.webp?v=1",
            "https://forumstatic.ru/files/001b/f1/8a/32614.webp?v=1",
            "https://forumstatic.ru/files/001b/f1/8a/63068.png?v=1",
            "https://forumstatic.ru/files/001b/f1/8a/39327.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/45666.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/14658.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/96576.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/66356.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/88283.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/36804.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/56082.webp?v=1",
          ]
        },
        {
          name: 'Зелень',
          images: [
            "https://forumstatic.ru/files/001b/f1/8a/30630.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/25010.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/66763.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/54117.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/46868.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/14096.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/67503.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/92313.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/34272.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/72885.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/55623.webp?v=1"

          ]
        },
        {
          name: 'Бабочки',
          images: [
            "https://forumstatic.ru/files/001c/7f/71/86329.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/76207.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/63953.webp?v=1",
            "https://forumstatic.ru/files/001b/f1/8a/15694.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/28830.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/89872.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/22835.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/68567.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/91548.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/53938.webp?v=1",
            "https://forumstatic.ru/files/001c/7f/71/79345.webp?v=1",
          ]
        }
      ],
      storageKey: 'forum_decor_spring',
      ...config
    };
    
    this.ornaments = [];
    this.draggingFromCollection = null;
    this.isPanelCollapsed = this.loadPanelState();
    this.currentTab = 0;
    
    this.decoratableElements = null;
    this.decoratableCacheTime = 0;
    this.cacheTTL = 1000;
    
    this.save = this.debounce(this.save.bind(this), 500);
    
    this.preloadedImages = new Set();
    
    this.init();
  }
  
  init() {
    this.createContainer();
    this.markDecoratableElements();
    this.createCollectionPanel();
    this.preloadImages();
    this.loadSavedDecorations();
    this.setupGlobalEvents();
  }
  
  createContainer() {
    this.container = document.getElementById('springOrnaments') || 
      document.body.appendChild(document.createElement('div'));
    this.container.id = 'springOrnaments';
  }
  
  markDecoratableElements() {
    const fragment = document.createDocumentFragment();
    
    this.config.decoratableSelectors.forEach(selector => {
      try {
        const elements = document.querySelectorAll(selector);
        elements.forEach(el => {
          el.classList.add('decoratable');
          if (!el.querySelector('.ornaments-container')) {
            const container = document.createElement('div');
            container.className = 'ornaments-container spring';
            el.appendChild(container);
          }
        });
      } catch (e) {
        console.warn('Неверный селектор:', selector, e);
      }
    });
  }
  
  getDecoratableElements() {
    const now = Date.now();
    if (!this.decoratableElements || (now - this.decoratableCacheTime) > this.cacheTTL) {
      this.decoratableElements = [];
      this.config.decoratableSelectors.forEach(selector => {
        try {
          const elements = document.querySelectorAll(selector);
          this.decoratableElements.push(...elements);
        } catch (e) {
          console.warn('Ошибка в селекторе:', selector);
        }
      });
      this.decoratableCacheTime = now;
    }
    return this.decoratableElements;
  }
  
  debounce(func, wait) {
    let timeout;
    return function executedFunction(...args) {
      const later = () => {
        clearTimeout(timeout);
        func(...args);
      };
      clearTimeout(timeout);
      timeout = setTimeout(later, wait);
    };
  }
  
  preloadImages() {
    const allImages = this.getAllImages();
    allImages.forEach(src => {
      if (!this.preloadedImages.has(src)) {
        const img = new Image();
        img.src = src;
        this.preloadedImages.add(src);
      }
    });
  }
  
  createCollectionPanel() {
    this.panel = document.createElement('div');
    this.panel.className = `ornament-collection ${this.isPanelCollapsed ? 'collapsed' : ''}`;
    
    const tabsHTML = this.config.ornamentCategories.map((cat, index) => 
      `<div class="collection-tab ${index === 0 ? 'active' : ''}" data-tab="${index}">${cat.name}</div>`
    ).join('');
    
    this.panel.innerHTML = `
      <div class="collection-toggle">
        <i class="fi fi-ss-flower-tulip"></i>
      </div>
      <div class="collection-tabs">${tabsHTML}</div>
      ${this.config.ornamentCategories.map((_, index) => 
        `<div class="collection-items ${index === 0 ? '' : 'hidden'}" data-tab-content="${index}"></div>`
      ).join('')}
      <div class="collection-footer">
        <p><strong>Shift + Click</strong> чтобы убрать украшение</p>
        <button class="button btn-sm" onclick="window.decor.resetAll()">
          Сбросить все
        </button>
      </div>
    `;
    
    document.body.appendChild(this.panel);
    
    this.panel.addEventListener('click', (e) => {
      const tab = e.target.closest('.collection-tab');
      if (tab) {
        const tabIndex = parseInt(tab.dataset.tab);
        this.switchTab(tabIndex);
      }
    });
    
    requestAnimationFrame(() => {
      this.updatePanel();
    });

    this.panel.querySelector('.collection-toggle').addEventListener('click', () => {
      this.panel.classList.toggle('collapsed');
      this.isPanelCollapsed = this.panel.classList.contains('collapsed');
      this.savePanelState();
    });
  }

  loadPanelState() {
    return localStorage.getItem(`${this.config.storageKey}_panelState`) === 'false' || localStorage.getItem(`${this.config.storageKey}_panelState`) === null ? false : true;
  }

  savePanelState() {
    try {
      localStorage.setItem(
        `${this.config.storageKey}_panelState`, 
        JSON.stringify(this.isPanelCollapsed)
      );
    } catch (e) {
      console.warn('Не удалось сохранить состояние панели:', e);
    }
  }  
  
  switchTab(tabIndex) {
    if (this.currentTab === tabIndex) return;
    
    this.currentTab = tabIndex;
    
    this.panel.querySelectorAll('.collection-tab').forEach(tab => {
      tab.classList.toggle('active', parseInt(tab.dataset.tab) === tabIndex);
    });
    
    this.panel.querySelectorAll('.collection-items').forEach(content => {
      content.classList.toggle('hidden', parseInt(content.dataset.tabContent) !== tabIndex);
    });
    
    requestAnimationFrame(() => {
      this.updateCurrentTab();
    });
  }
  
  updatePanel() {
    requestAnimationFrame(() => {
      this.updateCurrentTab();
    });
  }
  
  updateCurrentTab() {
    const currentCategory = this.config.ornamentCategories[this.currentTab];
    const itemsContainer = this.panel.querySelector(`.collection-items[data-tab-content="${this.currentTab}"]`);
    if (!itemsContainer) return;
    
    const fragment = document.createDocumentFragment();
    
    currentCategory.images.forEach((src, i) => {
      const globalIndex = this.getGlobalIndex(this.currentTab, i);
      const count = this.ornaments.filter(o => o.typeIndex === globalIndex).length;
      const isAvailable = count < 3;
      
      const btn = document.createElement('div');
      btn.className = 'collection-ornament spring';
      btn.dataset.category = this.currentTab;
      btn.dataset.index = i;
      btn.draggable = isAvailable;
      
      if (!isAvailable) {
        btn.style.opacity = '0.4';
        btn.style.cursor = 'not-allowed';
      } else {
        btn.addEventListener('dragstart', (e) => this.startDragFromCollection(e, src, globalIndex));
        btn.addEventListener('dragend', () => this.endDragFromCollection());
      }
      
      this.createOrnamentElement(btn, src);
      fragment.appendChild(btn);
    });
    
    itemsContainer.innerHTML = '';
    itemsContainer.appendChild(fragment);
  }
  
  getGlobalIndex(categoryIndex, itemIndex) {
    let globalIndex = 0;
    for (let i = 0; i < categoryIndex; i++) {
      globalIndex += this.config.ornamentCategories[i].images.length;
    }
    return globalIndex + itemIndex;
  }
  
  getCategoryInfo(globalIndex) {
    let accumulated = 0;
    for (let i = 0; i < this.config.ornamentCategories.length; i++) {
      const category = this.config.ornamentCategories[i];
      if (globalIndex < accumulated + category.images.length) {
        return {
          categoryIndex: i,
          categoryName: category.name,
          image: category.images[globalIndex - accumulated],
          localIndex: globalIndex - accumulated
        };
      }
      accumulated += category.images.length;
    }
    return null;
  }
  
  getAllImages() {
    return this.config.ornamentCategories.reduce((all, cat) => 
      all.concat(cat.images), []);
  }
  
  createOrnamentElement(container, src) {
    const img = document.createElement('img');
    img.src = src;
    img.loading = 'lazy';
    img.style.width = '100%';
    img.style.height = '100%';
    img.style.objectFit = 'contain';
    img.draggable = false;
    
    container.appendChild(img);
    return img;
  }
  
  startDragFromCollection(e, src, typeIndex) {
    if (this.ornaments.length >= this.config.maxOrnaments) {
      $.jGrowl('<i class="fi fi-ss-flower-daffodil"></i> Лимит украшений достигнут');
      e.preventDefault();
      return;
    }
    
    this.draggingFromCollection = { src, typeIndex };
    e.dataTransfer.setData('text/plain', 'from_collection');
    e.dataTransfer.effectAllowed = 'copy';
    
    if (!this.dragGhost) {
      this.dragGhost = document.createElement('div');
      this.dragGhost.className = 'forum-ornament spring';
      this.dragGhost.style.position = 'fixed';
      this.dragGhost.style.left = '-100px';
      this.dragGhost.style.pointerEvents = 'none';
      this.dragGhost.style.zIndex = '99';
      document.body.appendChild(this.dragGhost);
    }
    
    this.dragGhost.innerHTML = '';
    this.createOrnamentElement(this.dragGhost, src);
    e.dataTransfer.setDragImage(this.dragGhost, 20, 20);
    
    const elements = this.getDecoratableElements();
    for (const el of elements) {
      el.classList.add('highlight');
    }
  }
  
  endDragFromCollection() {
    this.draggingFromCollection = null;
    
    const elements = this.getDecoratableElements();
    for (const el of elements) {
      el.classList.remove('highlight');
    }
  }
  
  createOrnament(src, typeIndex, x, y) {
    if (this.ornaments.length >= this.config.maxOrnaments) {
      $.jGrowl('<i class="fi fi-ss-flower-daffodil"></i> Лимит украшений!');
      return null;
    }
    
    const id = `o_${Date.now()}_${Math.random().toString(36).slice(2, 9)}`;
    const o = document.createElement('div');
    o.className = 'forum-ornament spring';
    o.dataset.id = id;
    o.dataset.typeIndex = typeIndex;
    
    this.createOrnamentElement(o, src);
    o.style.left = `${x}px`;
    o.style.top = `${y}px`;
    o.draggable = true;
    
    o.addEventListener('dragstart', (e) => this.dragStart(e, o));
    o.addEventListener('dragend', (e) => this.dragEnd(e, o));
    
    this.container.appendChild(o);
    
    const data = {
      element: o, 
      typeIndex, 
      attachedTo: null, 
      pos: null, 
      selector: null,
      id
    };
    
    this.ornaments.push(data);
    
    requestAnimationFrame(() => {
      this.updatePanel();
    });
    
    return data;
  }
  
  handleDrop(e) {
    e.preventDefault();
    e.stopPropagation();
    
    if (!this.draggingFromCollection) return;
    
    const { src, typeIndex } = this.draggingFromCollection;
    const target = document.elementFromPoint(e.clientX, e.clientY);
    const decorEl = this.findDecoratable(target, e.clientX, e.clientY);
    
    if (decorEl) {
      decorEl.classList.remove('highlight');
      const selector = UniqueSelector.getUniqueSelector(decorEl);
      
      if (!selector || selector === '') {
        $.jGrowl('Ошибка сохранения позиции');
        this.endDragFromCollection();
        return;
      }
      
      try {
        const elements = document.querySelectorAll(selector);
        if (elements.length === 0 || (elements.length > 1 && !Array.from(elements).includes(decorEl))) {
          $.jGrowl('Ошибка уникальности селектора');
          this.endDragFromCollection();
          return;
        }
        
        this.attachNewOrnament(src, typeIndex, decorEl, selector, e.clientX, e.clientY);
      } catch(err) {
        $.jGrowl('Неизвестная ошибка');
        this.endDragFromCollection();
        return;
      }
    } else {
      const x = Math.max(10, Math.min(e.clientX - 20, window.innerWidth - 50));
      const y = Math.max(10, Math.min(e.clientY - 20, window.innerHeight - 50));
      this.createOrnament(src, typeIndex, x, y);
      $.jGrowl(`<i class="fi fi-ss-flower"></i> Украшение добавлено`);
    }
    
    this.endDragFromCollection();
  }
  
  attachNewOrnament(src, typeIndex, el, selector, x, y) {
    const data = this.createOrnament(src, typeIndex, -100, -100);
    if (!data) return;
    
    this.attachOrnament(data.element, el, selector, x, y);
  }
  
  findDecoratable(el, clientX, clientY) {
    const decoratable = el.closest('.decoratable');
    if (!decoratable) return null;
    
    const rect = decoratable.getBoundingClientRect();
    const ballCenterX = clientX;
    const ballCenterY = clientY;
    
    const isBelow = ballCenterY >= rect.top - 37 && ballCenterY <= rect.bottom + 20;
    const isHorizontallyAligned = ballCenterX >= rect.left && ballCenterX <= rect.right;
    
    return isBelow && isHorizontallyAligned ? decoratable : null;
  }
  
  dragStart(e, o) {
    o.classList.add('dragging');
    e.dataTransfer.setData('text', o.dataset.id);
    
    const elements = this.getDecoratableElements();
    for (const el of elements) {
      el.classList.add('highlight');
    }
  }
  
  dragEnd(e, o) {
    o.classList.remove('dragging');
    
    const elements = this.getDecoratableElements();
    for (const el of elements) {
      el.classList.remove('highlight');
    }
    
    const target = document.elementFromPoint(e.clientX, e.clientY);
    const decorEl = this.findDecoratable(target, e.clientX, e.clientY);
    
    if (decorEl) {
      const selector = UniqueSelector.getUniqueSelector(decorEl);
      if (selector && selector !== '') {
        this.attachOrnament(o, decorEl, selector, e.clientX, e.clientY);
      } else {
        this.freeOrnament(o, e.clientX, e.clientY);
      }
    } else {
      this.freeOrnament(o, e.clientX, e.clientY);
    }
  }
  
  freeOrnament(o, clientX, clientY) {
    o.style.left = `${Math.max(10, Math.min(clientX - 20, window.innerWidth - 50))}px`;
    o.style.top = `${Math.max(10, Math.min(clientY - 20, window.innerHeight - 50))}px`;
    o.classList.remove('attached');
    
    if (o.parentElement !== this.container) {
      this.container.appendChild(o);
    }
    
    const data = this.ornaments.find(d => d.element === o);
    if (data) {
      data.attachedTo = null;
      data.selector = null;
      data.pos = null;
      this.save();
    }
  }
  
  attachOrnament(o, el, selector, clientX, clientY) {
    let container = el.querySelector('.ornaments-container');
    if (!container) {
      container = document.createElement('div');
      container.className = 'ornaments-container spring';
      el.appendChild(container);
    }
    
    if (o.parentElement) {
      o.parentElement.removeChild(o);
    }
    
    const rect = el.getBoundingClientRect();
    const relativeX = Math.max(0, Math.min(clientX - rect.left - 20, rect.width - 40));
    const fixedTop = '0';
    const fixedBottom = 'calc(100% - 0.15em - 24px)';
    
    o.style.position = 'absolute';
    o.style.left = `${relativeX}px`;
    o.style.top = fixedTop;
    o.style.transformOrigin = 'bottom center';
    o.classList.add('attached');
    
    container.appendChild(o);
    
    const data = this.ornaments.find(d => d.element === o);
    if (data) {
      data.attachedTo = selector;
      data.selector = selector;
      data.pos = {
        top: fixedTop,
        left: relativeX,
        width: rect.width,
        page: window.location.pathname + window.location.search
      };
    }
    
    this.save();
    $.jGrowl('<i class="fi fi-ss-flower"></i> Украсили!');
  }
  
  resolveElementSelector(selector) {
    if (!selector || selector === '') return null;
    
    try {
      const elements = document.querySelectorAll(selector);
      
      if (elements.length === 0) {
        const parts = selector.split(' ');
        for (let i = 1; i < parts.length; i++) {
          const partial = parts.slice(-i).join(' ');
          const partialElements = document.querySelectorAll(partial);
          if (partialElements.length === 1) return partialElements[0];
        }
        return null;
      }
      
      return elements[0];
    } catch (e) {
      console.warn('Ошибка разрешения селектора:', selector, e);
      return null;
    }
  }
  
  removeOrnament(o) {
    const index = this.ornaments.findIndex(d => d.element === o);
    if (index > -1) {
      this.ornaments.splice(index, 1);
      o.remove();
      
      requestAnimationFrame(() => {
        this.updatePanel();
        this.save();
      });
    }
  }
  
  loadSavedDecorations() {
    try {
      const currentPage = window.location.pathname + window.location.search;
      const savedData = localStorage.getItem(`${this.config.storageKey}_${currentPage}`);
      
      if (!savedData) return;
      
      const data = JSON.parse(savedData);
      if (!data.ornaments || !Array.isArray(data.ornaments)) return;
      
      const allImages = this.getAllImages();
      const validOrnaments = [];
      const fragment = document.createDocumentFragment();
      const freeOrnaments = [];
      
      data.ornaments.forEach(item => {
        if (item.typeIndex === undefined || !allImages[item.typeIndex]) {
          return;
        }
        
        const src = allImages[item.typeIndex];
        const o = document.createElement('div');
        o.className = 'forum-ornament spring';
        this.createOrnamentElement(o, src);
        o.draggable = true;
        
        o.addEventListener('dragstart', (e) => this.dragStart(e, o));
        o.addEventListener('dragend', (e) => this.dragEnd(e, o));
        
        const ornamentData = {
          element: o,
          typeIndex: item.typeIndex,
          attachedTo: item.attachedTo,
          selector: item.selector || item.attachedTo,
          pos: item.pos
        };
        
        validOrnaments.push(ornamentData);
        
        if (item.attachedTo) {
          const targetEl = this.resolveElementSelector(item.attachedTo);
          if (targetEl) {
            let container = targetEl.querySelector('.ornaments-container');
            if (!container) {
              container = document.createElement('div');
              container.className = 'ornaments-container spring';
              targetEl.appendChild(container);
            }
            
            container.appendChild(o);
            o.style.position = 'absolute';
            o.style.left = `${item.pos?.left || 0}px`;
            o.style.top = item.pos?.top || '0';
            o.classList.add('attached');
          } else {
            freeOrnaments.push({ o, item });
          }
        } else {
          freeOrnaments.push({ o, item });
        }
      });
      
      freeOrnaments.forEach(({ o, item }) => {
        if (item.pos && item.pos.x && item.pos.y) {
          o.style.left = `${item.pos.x}px`;
          o.style.top = `${item.pos.y}px`;
        } else {
          o.style.left = `${50 + Math.random() * (window.innerWidth - 150)}px`;
          o.style.top = `${50 + Math.random() * (window.innerHeight - 150)}px`;
        }
        fragment.appendChild(o);
      });
      
      this.container.appendChild(fragment);
      this.ornaments = validOrnaments;
      
      requestAnimationFrame(() => {
        this.updatePanel();
      });
      
      console.log('Загружено украшений:', this.ornaments.length);
    } catch(e) {
      console.warn('Ошибка загрузки украшений:', e);
    }
  }
  
  save() {
    try {
      const currentPage = window.location.pathname + window.location.search;
      const validOrnaments = this.ornaments.filter(o => {
        if (o.attachedTo) {
          try {
            return document.querySelector(o.attachedTo) !== null;
          } catch {
            return false;
          }
        }
        return true;
      });
      
      const pageData = {
        ornaments: validOrnaments.map(o => ({
          typeIndex: o.typeIndex,
          attachedTo: o.attachedTo,
          selector: o.selector,
          pos: o.pos
        })),
        timestamp: Date.now(),
        version: '1.0'
      };
      
      const currentData = JSON.stringify(pageData);
      const savedData = localStorage.getItem(`${this.config.storageKey}_${currentPage}`);
      
      if (savedData !== currentData) {
        localStorage.setItem(`${this.config.storageKey}_${currentPage}`, currentData);
      }
    } catch(e) {
      console.warn('Ошибка сохранения:', e);
    }
  }
  
  setupGlobalEvents() {
    document.addEventListener('dragover', (e) => {
      e.preventDefault();
      e.dataTransfer.dropEffect = 'copy';
    }, { passive: false });
    
    document.addEventListener('drop', (e) => this.handleDrop(e), { passive: false });
    
    document.addEventListener('click', (e) => {
      if (e.shiftKey && e.target.closest('.forum-ornament')) {
        const ornament = e.target.closest('.forum-ornament');
        this.removeOrnament(ornament);
        $.jGrowl('<i class="fi fi-ss-trash-xmark"></i> Украшение удалено');
        e.stopPropagation();
      }
    });
    
    const observer = new MutationObserver(() => {
      this.decoratableElements = null;
      this.decoratableCacheTime = 0;
    });
    
    observer.observe(document.body, {
      childList: true,
      subtree: true,
      attributes: true,
      attributeFilter: ['class', 'id']
    });
  }
  
  resetAll() {
    if (confirm('Убрать все украшения?')) {
      this.ornaments.forEach(o => o.element.remove());
      this.ornaments = [];
      
      const currentPage = window.location.pathname + window.location.search;
      localStorage.removeItem(`${this.config.storageKey}_${currentPage}`);
      
      requestAnimationFrame(() => {
        this.updatePanel();
        $.jGrowl('<i class="fi fi-ss-flower"></i> Все украшения убраны');
      });
    }
  }
  
  destroy() {
    if (this.dragGhost) {
      this.dragGhost.remove();
    }
  }
}

if (window.matchMedia("(min-device-width: 768px)").matches) {
  document.addEventListener('DOMContentLoaded', () => {
    window.decor = new ForumDecorator();
  });
}
</script>
<!-- SPRING end -->
Подпись автора

Подпись - это небольшая приписка, прилагаемая к вашим сообщениям. Это может быть все, что вам нравится. Например

1
96

0


Вы здесь » Mistake Not » Falling Outside the Normal Moral Constraints » script


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