В четверг, 7 мая, около 16 часов (MSK) регистратор заморозил домен «cyclowiki.org» без уведомления владельцев. Сайт недоступен из большинства стран. Правление изучает возможности решения проблемы.

MediaWiki:Gadget-AutoSave.js

Материал из Циклопедии
Перейти к навигации Перейти к поиску

Замечание: Возможно, после публикации вам придётся очистить кэш своего браузера, чтобы увидеть изменения.

  • Firefox / Safari: Удерживая клавишу Shift, нажмите на панели инструментов Обновить либо нажмите Ctrl+F5 или Ctrl+R (⌘+R на Mac)
  • Google Chrome: Нажмите Ctrl+Shift+R (⌘+Shift+R на Mac)
  • Internet Explorer / Edge: Удерживая Ctrl, нажмите Обновить либо нажмите Ctrl+F5
  • Opera: Нажмите Ctrl+F5.
// =================================================
// ===     «Автосохранение вики-черновиков»      ===
// ===       автор: Урахара из Циклопедии        ===
// ===          лицензия: CC BY-SA 4.0           ===
// =================================================

// Работает даже на старых версиях MediaWiki (гарантированно — начиная с 1.19)
// Рабочей базой служил движок Mediawiki 1.39.1
// Не рассчитан для браузера Internet Explorer 10 и ниже
// Автосохранение срабатывает через 3 сек. после того, как вы перестали печатать
// При выходе со страницы сохранение происходит мгновенно (без задержки)
// Ручное сохранение (кнопка «Сохранить черновик» или Ctrl+S) тоже мгновенное
// Индикатор показывает «Редактирование…» пока вы печатаете, и иное по делу
// Работает даже без интернета (если страница уже загружена)
// Не нагружает сервер, обработка на стороне устройства пользователя
// Имеет защиту от ошибок — если что-то пойдёт не так, сломается только сам скрипт
// Сохраняется текст и несозданных новых статей (вбейте то же имя при воссоздании)
// При очистке кэша браузера всё равно сохраняет текст 
// Текст исчезнет, если почистите историю браузера
// Текст исчезнет, если зайдёте в режим инкогнито и закроете окно
// Черновики старше 7 дней автоматически удаляются
// Браузер может удалить данные, если заполнится лимит сайта на хранилище


// ============ ЗАКОММЕНТИРОВАННЫЙ КОД ============

( function () {
    'use strict';

    // Проверяем, находимся ли мы на странице редактирования
    if ( mw.config.get( 'wgAction' ) !== 'edit' && mw.config.get( 'wgAction' ) !== 'submit' ) {
        return; // Не на странице редактирования - выходим
    }

    // Получаем название текущей страницы
    var pageName = mw.config.get( 'wgPageName' ) || 'newpage';
    var storageKey = 'wiki_draft_' + pageName;
    var $textbox = $( '#wpTextbox1' );
    
    if ( !$textbox.length ) {
        console.log( 'AutoSave: Поле редактирования не найдено' );
        return;
    }

    // Настройки
    var config = {
        saveDelay: 3000,
        maxDraftAge: 7
    };

    // Состояние
    var saveTimeout = null;
    var lastSavedContent = '';

    // Функция сохранения черновика
    function saveDraft( silent ) {
        var currentContent = $textbox.val();
        
        if ( !currentContent || currentContent.trim() === '' ) {
            console.log( 'AutoSave: Пустой контент, не сохраняем' );
            return false;
        }
        
        if ( currentContent === lastSavedContent ) {
            console.log( 'AutoSave: Контент не изменился' );
            return false;
        }

        try {
            var draftData = {
                content: currentContent,
                timestamp: Date.now(),
                page: pageName
            };
            
            localStorage.setItem( storageKey, JSON.stringify( draftData ) );
            lastSavedContent = currentContent;
            
            console.log( 'AutoSave: Черновик сохранён', {
                размер: currentContent.length,
                время: new Date().toLocaleString()
            } );
            
            if ( !silent ) {
                showNotification( 'Черновик сохранён', 'success' );
            }
            
            updateIndicator( 'saved' );
            return true;
        } catch ( e ) {
            console.error( 'AutoSave: Ошибка сохранения', e );
            showNotification( 'Ошибка сохранения черновика', 'error' );
            return false;
        }
    }

    // Функция уведомлений (простая, без зависимостей)
    function showNotification( message, type ) {
        if ( typeof mw.notify === 'function' ) {
            mw.notify( message, { 
                type: type, 
                autoHide: true, 
                tag: 'autosave-notify' 
            } );
        }
    }

    // Функция восстановления (без jQuery UI диалогов!)
    function restoreDraft() {
        try {
            var saved = localStorage.getItem( storageKey );
            if ( !saved ) {
                console.log( 'AutoSave: Черновик не найден' );
                return;
            }

            var draftData = JSON.parse( saved );
            var currentContent = $textbox.val();
            
            // Проверка страницы
            if ( draftData.page !== pageName ) {
                console.log( 'AutoSave: Черновик для другой страницы' );
                return;
            }

            // Проверка возраста
            var now = Date.now();
            var daysOld = ( now - draftData.timestamp ) / ( 1000 * 60 * 60 * 24 );
            
            console.log( 'AutoSave: Возраст черновика', daysOld.toFixed(1) + ' дней' );
            
            if ( daysOld > config.maxDraftAge ) {
                console.log( 'AutoSave: Черновик устарел, удаляем' );
                localStorage.removeItem( storageKey );
                return;
            }

            // Проверка совпадения
            if ( draftData.content === currentContent ) {
                console.log( 'AutoSave: Контент совпадает' );
                lastSavedContent = currentContent;
                return;
            }

            // Используем простой confirm вместо jQuery UI dialog
            if ( confirm( 'Найден несохранённый черновик от ' + 
                  new Date( draftData.timestamp ).toLocaleString() + 
                  '. Восстановить?' ) ) {
                
                $textbox.val( draftData.content );
                lastSavedContent = draftData.content;
                
                // Подсветка
                $textbox.css( 'border', '2px solid #00af89' );
                setTimeout( function() {
                    $textbox.css( 'border', '' );
                }, 2000 );
                
                showNotification( 'Черновик восстановлен', 'success' );
                console.log( 'AutoSave: Черновик восстановлен' );
            }

        } catch ( e ) {
            console.error( 'AutoSave: Ошибка восстановления', e );
        }
    }

    // Кнопка сохранения
    function addSaveButton() {
        if ( $( '#autosave-save-btn' ).length ) return;

        var $saveBtn = $( '<button>' )
            .attr( 'id', 'autosave-save-btn' )
            .text( 'Сохранить черновик' )
            .addClass( 'mw-ui-button mw-ui-progressive' )
            .css( 'margin-left', '10px' )
            .click( function() {
                var btn = this;
                if ( saveDraft( false ) ) {
                    $( btn ).text( 'Сохранено!' );
                    setTimeout( function() {
                        $( btn ).text( 'Сохранить черновик' );
                    }, 2000 );
                }
            } );

        $( '#wpSave' ).after( $saveBtn );

        // Ctrl+S
        $( document ).on( 'keydown', function( e ) {
            if ( e.ctrlKey && e.key === 's' ) {
                e.preventDefault();
                $( '#autosave-save-btn' ).click();
            }
        } );
    }

    // Индикатор
    function addStatusIndicator() {
        if ( $( '#autosave-indicator' ).length ) return;

        var $indicator = $( '<div>' )
            .attr( 'id', 'autosave-indicator' )
            .css( {
                'font-size': '0.9em',
                'margin': '5px 0',
                'padding': '4px 8px',
                'background': '#f8f9fa',
                'border-radius': '2px'
            } )
            .text( 'Черновик не сохранён' );

        $textbox.after( $indicator );
    }

    // Обновление индикатора
    function updateIndicator( status ) {
        var $indicator = $( '#autosave-indicator' );
        if ( !$indicator.length ) return;

        var messages = {
            'saved': { text: 'Черновик сохранён', color: '#00af89' },
            'editing': { text: 'Редактирование...', color: '#36c' },
            'error': { text: 'Ошибка сохранения', color: '#d33' },
            'idle': { text: 'Черновик не сохранён', color: '#72777d' }
        };

        var msg = messages[ status ] || messages.idle;
        $indicator.text( msg.text ).css( 'color', msg.color );
    }

    // Очистка при сохранении
    function clearOnSave() {
        $( '#wpSave' ).on( 'click', function() {
            localStorage.removeItem( storageKey );
            updateIndicator( 'idle' );
            console.log( 'AutoSave: Черновик очищен после сохранения' );
        } );
    }

    // Автосохранение при вводе
    $textbox.on( 'input', function() {
        if ( saveTimeout ) {
            clearTimeout( saveTimeout );
        }
        updateIndicator( 'editing' );
        saveTimeout = setTimeout( function() {
            saveDraft( true ); // silent mode
        }, config.saveDelay );
    } );

    // При выходе
    $( window ).on( 'beforeunload', function() {
        var currentContent = $textbox.val();
        if ( currentContent !== lastSavedContent && currentContent.trim() !== '' ) {
            saveDraft( true );
        }
    } );

    // Инициализация
    $( document ).ready( function() {
        console.log( 'AutoSave: Инициализация для страницы', pageName );
        
        setTimeout( function() {
            addSaveButton();
            addStatusIndicator();
            restoreDraft();
            clearOnSave();
        }, 500 );
        
        console.log( 'AutoSave: Готов к работе' );
        
        // Проверка localStorage
        try {
            localStorage.setItem( 'autosave_test', 'test' );
            localStorage.removeItem( 'autosave_test' );
            console.log( 'AutoSave: localStorage работает' );
        } catch ( e ) {
            console.error( 'AutoSave: localStorage недоступен', e );
        }
    } );

} )();