Предупредить пользователя перед тем, как покинуть веб-страницу с несохраненными изменениями


342

У меня есть несколько страниц с формами в моем приложении.

Как я могу защитить форму таким образом, чтобы, если кто-то уходит или закрывает вкладку браузера, ему было предложено подтвердить, что он действительно хочет оставить форму с несохраненными данными?



3
На этот вопрос здесь должен быть ответ, который вам
нужен


Ответы:


557

Короткий, неправильный ответ:

Вы можете сделать это, обработав beforeunloadсобытие и вернув ненулевую строку :

window.addEventListener("beforeunload", function (e) {
    var confirmationMessage = 'It looks like you have been editing something. '
                            + 'If you leave before saving, your changes will be lost.';

    (e || window.event).returnValue = confirmationMessage; //Gecko + IE
    return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
});

Проблема этого подхода заключается в том, что отправка формы также запускает событие unload . Это легко исправить, добавив флаг, которым вы отправляете форму:

var formSubmitting = false;
var setFormSubmitting = function() { formSubmitting = true; };

window.onload = function() {
    window.addEventListener("beforeunload", function (e) {
        if (formSubmitting) {
            return undefined;
        }

        var confirmationMessage = 'It looks like you have been editing something. '
                                + 'If you leave before saving, your changes will be lost.';

        (e || window.event).returnValue = confirmationMessage; //Gecko + IE
        return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
    });
};

Затем вызывая сеттер при отправке:

<form method="post" onsubmit="setFormSubmitting()">     
    <input type="submit" />
</form>

Но читайте дальше ...

Длинный правильный ответ:

Вы также не хотите показывать это сообщение, когда пользователь ничего не изменил в ваших формах . Одним из решений является использование beforeunloadсобытия в сочетании с «грязным» флагом, который вызывает запрос только в том случае, если он действительно актуален.

var isDirty = function() { return false; }

window.onload = function() {
    window.addEventListener("beforeunload", function (e) {
        if (formSubmitting || !isDirty()) {
            return undefined;
        }

        var confirmationMessage = 'It looks like you have been editing something. '
                                + 'If you leave before saving, your changes will be lost.';

        (e || window.event).returnValue = confirmationMessage; //Gecko + IE
        return confirmationMessage; //Gecko + Webkit, Safari, Chrome etc.
    });
};

Сейчас для реализации isDirtyметода существуют различные подходы.

Вы можете использовать jQuery и сериализацию форм , но у этого подхода есть некоторые недостатки. Сначала вам нужно изменить код, чтобы он работал в любой форме ( $("form").each()будет работать), но самая большая проблема заключается в том, что jQuery serialize()будет работать только с именованными, не отключенными элементами, поэтому изменение любого отключенного или безымянного элемента не вызовет грязный флаг. Для этого есть обходные пути , такие как создание элементов управления только для чтения вместо включения, сериализации и повторного отключения элементов управления.

Таким образом, события, кажется, путь. Вы можете попробовать прослушивать нажатия клавиш . Это событие имеет несколько проблем:

  • Не будет вызывать флажки, переключатели или другие элементы, которые изменяются с помощью мыши.
  • Сработает для нерелевантных нажатий клавиш, таких как Ctrlклавиша.
  • Не будет срабатывать при значениях, установленных через код JavaScript.
  • Не будет запускаться при вырезании или вставке текста через контекстные меню.
  • Не будет работать для виртуальных входов, таких как указатели даты или флажки / кнопки-переключатели, которые сохраняют свои значения в скрытом вводе через JavaScript.

changeСобытие также не вызывает на значениях , установленных в коде JavaScript , так и не будет работать для виртуальных входов.

Привязка inputсобытия ко всем inputs (и textareas и selects) на вашей странице не будет работать в старых браузерах и, как и все упомянутые выше решения по обработке событий, не поддерживает отмену. Когда пользователь изменяет текстовое поле, а затем отменяет его, или устанавливает и снимает флажок, форма все еще считается грязной.

А когда вы захотите реализовать больше поведения, например игнорировать определенные элементы, у вас будет еще больше работы.

Не изобретай велосипед

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

Если ваше приложение уже использует jQuery, вы также можете использовать проверенный, поддерживаемый код вместо собственного и использовать библиотеку третьей стороны для всего этого. JQuery вы уверены? Плагин прекрасно работает, смотрите их демонстрационную страницу . Это так просто, как это:

<script src="jquery.are-you-sure.js"></script>

<script>
  $(function() {
    $('#myForm').areYouSure(
      {
        message: 'It looks like you have been editing something. '
               + 'If you leave before saving, your changes will be lost.'
      }
    );
  });

</script>

Пользовательские сообщения не поддерживаются везде

Обратите внимание, что Firefox 4 не поддерживает пользовательские сообщения в этом диалоговом окне. По состоянию на апрель 2016 года развернут Chrome 51, в котором также удаляются пользовательские сообщения .

На этом сайте есть другие альтернативы, но я думаю, что такой диалог достаточно ясен:

Вы хотите покинуть этот сайт?

Внесенные вами изменения не могут быть сохранены.

Leave Stay


6
Обратите внимание, что использование пользовательских строк больше не работает в Chrome; chromestatus.com/feature/5349061406228480
Amann

6
Да, это описано в последнем разделе моего ответа.
CodeCaster

2
@ Крис, потому что Angular манипулирует DOM, чтобы изменить страницу, не отходя от текущего документа.
CodeCaster

15
Имейте в виду, что jQuery areYouSure был оставлен с 2014 года (57 открытых выпусков на Github ), он полон ошибок из моего опыта, может показаться, что он работает сначала, но после некоторых быстрых тестов я понял, что это не всегда работает. Не рекомендовал бы это вообще
Марк

4
@JacopoStanchi Я, к сожалению, не нашел ни одного. И я имею в виду, я выглядел довольно много - но это было еще в 10 января, поэтому кто-то, возможно, написал что-то, но я сомневаюсь в этом. Лучше всего реализовать это самостоятельно - это не то, что вы хотите услышать, так что извините. (Но, по крайней мере, вы не получите никаких сюрпризов, как я использовал jQuery areYouSure.) Кроме того, кто знает, может быть, вы даже можете сделать его открытым исходным кодом, и ваша реализация будет доступна другим;)
Марк

75

Проверьте событие JavaScript onbeforeunload . Это нестандартный JavaScript, представленный Microsoft, однако он работает в большинстве браузеров, и их документация onbeforeunload содержит больше информации и примеров.


3
Я думаю, что теперь это стало стандартом. >> Мероприятие было впервые представлено Microsoft в Internet Explorer 4 и стандартизировано в спецификации HTML5.
ви

34

через JQuery

$('#form').data('serialize',$('#form').serialize()); // On load save form current state

$(window).bind('beforeunload', function(e){
    if($('#form').serialize()!=$('#form').data('serialize'))return true;
    else e=null; // i.e; if form state change show warning box, else don't show it.
});

Вы можете использовать функцию Google JQuery Form Serialize, которая соберет все входные данные формы и сохранит их в массиве. Я думаю, этого объяснения достаточно :)


6
не используйте #form в качестве идентификатора формы, потому что окно создаст объект с именем form :)
mdikici

1
Проверьте здесь, если это не работает для вас: stackoverflow.com/questions/7080269/…
Leniel Maccaferri

16

Универсальное решение, не требующее настройки, которое автоматически обнаруживает все входные изменения, включая contenteditable элементы:

"use strict";
(() => {
const modified_inputs = new Set;
const defaultValue = "defaultValue";
// store default values
addEventListener("beforeinput", (evt) => {
    const target = evt.target;
    if (!(defaultValue in target || defaultValue in target.dataset)) {
        target.dataset[defaultValue] = ("" + (target.value || target.textContent)).trim();
    }
});
// detect input modifications
addEventListener("input", (evt) => {
    const target = evt.target;
    let original;
    if (defaultValue in target) {
        original = target[defaultValue];
    } else {
        original = target.dataset[defaultValue];
    }
    if (original !== ("" + (target.value || target.textContent)).trim()) {
        if (!modified_inputs.has(target)) {
            modified_inputs.add(target);
        }
    } else if (modified_inputs.has(target)) {
        modified_inputs.delete(target);
    }
});
// clear modified inputs upon form submission
addEventListener("submit", () => {
    modified_inputs.clear();
    // to prevent the warning from happening, it is advisable
    // that you clear your form controls back to their default
    // state after submission
});
// warn before closing if any inputs are modified
addEventListener("beforeunload", (evt) => {
    if (modified_inputs.size) {
        const unsaved_changes_warning = "Changes you made may not be saved.";
        evt.returnValue = unsaved_changes_warning;
        return unsaved_changes_warning;
    }
});
})();

1
Холодные бобы. Просто скопируйте и вставьте этот код на тестовую страницу, внесите изменения и нажмите «Обновить». Он отобразит собственное предупреждение браузера, предупреждающее вас о том, что у вас есть несохраненные изменения.
TheLegendaryCopyCoder

1
Это сработало для меня на Chrome 71.0.3578.98 (Официальная сборка) (64-битная
версия

2
Это работает очень хорошо, но если вы используете это с формой для отправки, вам нужно будет сначала очистить установленный модифицированную_вводу, прежде чем вы получите предупреждение о подтверждении изменений. Я добавил прослушиватель событий в мою форму для вызова при отправке и просто запустил функцию set clear, таким образом, модифицированное значение input очищается при запуске прослушивателя событий перед загрузкой и позволяет продолжать публикацию.
stepquick

1
Круто на самом деле копировать и вставлять. спасибо, что сэкономил мое время. Работа для меня в Firfox 75.0
Connectify_user

1
Люблю этот фрагмент
Rawburner

10

Построенный на основе отличной идеи Васима А. использовать сериализацию. Проблема заключалась в том, что предупреждение также отображалось при отправке формы. Это было исправлено здесь.

var isSubmitting = false

$(document).ready(function () {
    $('form').submit(function(){
        isSubmitting = true
    })

    $('form').data('initial-state', $('form').serialize());

    $(window).on('beforeunload', function() {
        if (!isSubmitting && $('form').serialize() != $('form').data('initial-state')){
            return 'You have unsaved changes which will not be saved.'
        }
    });
})

Он был протестирован в Chrome и IE 11.


Wasim A и Eerik Sven Puudist - Спасибо, я использовал id для формы и id для своей кнопки сохранения, так как все мои кнопки были типа submit <form action = "" id = "marksform" method = "POST"> <td> <input type = "submit" name = "submit_button" id = "save" value = "Save"> </ td> <td> <input type = "submit" name = "submit_button" value = "Next"> </ td> и несколько более всех типов Представлять Поэтому заменить .Submit (специфическими кнопкой мыши $ ( '# сохранить') нажмите кнопку (функция () {isSubmitting = True}) используйте '#marksform' вместо 'формы'.
Jayanta

7

Основываясь на предыдущих ответах и ​​собранных вместе из разных мест переполнения стека, вот решение, которое я придумал, которое обрабатывает случай, когда вы действительно хотите отправить свои изменения:

window.thisPage = window.thisPage || {};
window.thisPage.isDirty = false;

window.thisPage.closeEditorWarning = function (event) {
    if (window.thisPage.isDirty)
        return 'It looks like you have been editing something' +
               ' - if you leave before saving, then your changes will be lost.'
    else
        return undefined;
};

$("form").on('keyup', 'textarea', // You can use input[type=text] here as well.
             function () { 
                 window.thisPage.isDirty = true; 
             });

$("form").submit(function () {
    QC.thisPage.isDirty = false;
});
window.onbeforeunload = window.thisPage.closeEditorWarning;

Стоит отметить, что IE11, похоже, требует, чтобы closeEditorWarningфункция возвращалась, undefinedчтобы не показывать предупреждение.


Это решение не проверяет, изменяются ли какие-либо значения по сравнению с их исходным состоянием, поэтому становится недействительным в случае, если пользователь вводит текст и удаляет введенный текст, прежде чем пытаться уйти.
Бретт Вебер

Хороший вопрос, @BrettWeber. В этом случае вы можете изменить closeEditorWarning и иметь «if (window.thisPage.isDirty && uiHasChanged ())», где uiHasChanged - это функция, которая знает об исходном состоянии от сервера и проверяет все различия, но это решение было для случай, когда я не особо хотел выполнять всю эту дополнительную работу, и это дает ложные срабатывания только тогда, когда в текстовых областях были сделаны реальные нажатия клавиш, что, по моему мнению, было разумным компромиссом. :)
Джонатан

Не должно QC.thisPage.isDirty = false;быть window.thisPage.isDirty = false;? Таким образом, он проверит, отправляете ли вы форму, и не отобразит предупреждение. Казалось, работает для моих тестов. Кроме того, большинство форм имеют, inputа не textarea. Я использовал следующее, что работает довольно хорошо:$("form").on('keyup', 'textarea,input,select', function () {
Майк

7

Следующая однострочная работа сработала для меня.

window.onbeforeunload = s => modified ? "" : null;

Просто установите modifiedв истинно или ложно в зависимости от состояния приложения.


2
Некоторые предложения: Возвращайте пользовательское сообщение, а не пустую строку, так как некоторые браузеры будут отображать его (например, Edge), и возвращайте, undefinedа не nullкак IE, выведет 'null', если modifiedустановлено значение false.
Кевин Ли

1
как насчет мобильных устройств? Насколько я понимаю, IOS-устройства не работают с предзагрузкой.
Джейбро

4

Следующий код прекрасно работает. Вам нужно достичь изменений ввода элементов формы через атрибут id:

var somethingChanged=false;
            $('#managerForm input').change(function() { 
                somethingChanged = true; 
           }); 
            $(window).bind('beforeunload', function(e){
                if(somethingChanged)
                    return "You made some changes and it's not saved?";
                else 
                    e=null; // i.e; if form state change show warning box, else don't show it.
            });
        });

3
var unsaved = false;
$(":input").change(function () {         
    unsaved = true;
});

function unloadPage() {         
    if (unsaved) {             
        alert("You have unsaved changes on this page. Do you want to leave this page and discard your changes or stay on this page?");
    }
} 
window.onbeforeunload = unloadPage;

Я вставил его в отдельный файл и включил в <script src = "warnOnChange.js"> </ script>, но ничего не происходит, когда я изменяю текстовое поле и затем выполняю команду заголовка.
Фабрицио Бартоломуччи

2

Вы можете использовать serialize () для создания текстовой строки в кодировке URL-адреса путем сериализации значений формы и проверки, изменилась ли форма перед загрузкой.

$(document).ready(function(){
    var form = $('#some-form'),
        original = form.serialize()

    form.submit(function(){
        window.onbeforeunload = null
    })

    window.onbeforeunload = function(){
        if (form.serialize() != original)
            return 'Are you sure you want to leave?'
    }
})

Перейдите по этой ссылке https://coderwall.com/p/gny70a/alert-when-leaving-page-with-unsaved-form Автор сценария Владимир Сидоренко


1
Это сработало, но, как ответила разработчик кода, у этого метода есть некоторые недостатки, см. Это объяснение stackoverflow.com/a/7317311/10555981
Pradeep

Уже есть пара использованных ответов serialize(), и, действительно, у этого есть свои недостатки.
CodeCaster

Это должен быть правильный ответ, очень простой в использовании и работающий как положено.
Jodyshop

1

В добавление к идее @codecaster вы можете добавить это на каждую страницу с формой (в моем случае я использую ее глобально, так что только в формах будет это предупреждение) измените его функцию на

if ( formSubmitting || document.getElementsByTagName('form').length == 0) 

Также добавьте формы отправки, включая логин и ссылки для отмены, чтобы при нажатии кнопки «Отмена» или «Отправить» форма не вызывала предупреждение также на каждой странице, где есть форма ...

<a class="btn btn-danger btn-md" href="back/url" onclick="setFormSubmitting()">Cancel</a>

1

Вы можете проверить подробное объяснение здесь: http://techinvestigations.redexp.in/comparison-of-form-values-on-load-and-before-close/ сравнение значений формы при загрузке и -перед-близко

Основной код:

function formCompare(defaultValues, valuesOnClose) {

    // Create arrays of property names
    var aPropsFormLoad = Object.keys(defaultValues);
    var aPropsFormClose = Object.keys(valuesOnClose);

    // If number of properties is different,
    // objects are not equivalent
    if (aPropsFormLoad.length != aPropsFormClose.length) {
        return false;
    }

    for (var i = 0; i < aPropsFormLoad.length; i++) {
        var propName = aPropsFormLoad[i];

        // If values of same property are not equal,
        // objects are not equivalent
        if (defaultValues[aPropsFormLoad]+"" !== valuesOnClose[aPropsFormLoad]+"") {
            return false;
        }
    }

    // If we made it this far, objects
    // are considered equivalent
    return true;

}

//add polyfill for older browsers, as explained on the link above

//use the block below on load
    for(i=0; i < document.forms[0].elements.length; i++){
    console.log("The field name is: " + document.forms[0].elements[i].name +
        " and it’s value is: " + document.forms[0].elements[i].value );
    aPropsFormLoad[i] = document.forms[0].elements[i].value;
    }

//create a similar array on window unload event.

//and call the utility function
    if (!formCompare(aPropsOnLoad, aPropsOnClose))
    {
    //perform action: 
    //ask user for confirmation or
    //display message about changes made
    }


1

Решение от Eerik Sven Puudist ...

var isSubmitting = false;

$(document).ready(function () {
    $('form').submit(function(){
        isSubmitting = true
    })

    $('form').data('initial-state', $('form').serialize());

    $(window).on('beforeunload', function() {
        if (!isSubmitting && $('form').serialize() != $('form').data('initial-state')){
            return 'You have unsaved changes which will not be saved.'
        }
    });
})

... спонтанно выполнил эту работу для меня в сложной объектно-ориентированной обстановке без каких-либо изменений.

Единственное изменение, которое я применил, состояло в том, чтобы ссылаться на конкретную форму (только одну форму на файл), называемую «formForm» («form» -> «#formForm»):

<form ... id="formForm" name="formForm" ...>

Особенно хорошо сделано то, что кнопка отправки «оставлена ​​в покое».

Кроме того, он работает и для меня с последней версией Firefox (по состоянию на 7 февраля 2019 года).


1

Протестировал универсальное решение Eli Grey, сработало только после того, как я упростил код до

  'use strict';
  (() => {
    const modified_inputs = new Set();
    const defaultValue = 'defaultValue';
    // store default values
    addEventListener('beforeinput', evt => {
      const target = evt.target;
      if (!(defaultValue in target.dataset)) {
        target.dataset[defaultValue] = ('' + (target.value || target.textContent)).trim();
      }
    });

    // detect input modifications
    addEventListener('input', evt => {
      const target = evt.target;
      let original = target.dataset[defaultValue];

      let current = ('' + (target.value || target.textContent)).trim();

      if (original !== current) {
        if (!modified_inputs.has(target)) {
          modified_inputs.add(target);
        }
      } else if (modified_inputs.has(target)) {
        modified_inputs.delete(target);
      }
    });

    addEventListener(
      'saved',
      function(e) {
        modified_inputs.clear()
      },
      false
    );

    addEventListener('beforeunload', evt => {
      if (modified_inputs.size) {
        const unsaved_changes_warning = 'Changes you made may not be saved.';
        evt.returnValue = unsaved_changes_warning;
        return unsaved_changes_warning;
      }
    });

  })();

Модификации его удаляются использованием target[defaultValue]и только использованиемtarget.dataset[defaultValue] для хранения реального значения по умолчанию.

И я добавил «сохраненный» прослушиватель событий, где «сохраненное» событие будет вызвано вами самостоятельно при успешном сохранении.

Но это «универсальное» решение работает только в браузерах, но не работает в веб-обозрении приложения, например, wechat-браузерах.

Чтобы заставить его работать в браузерах wechat (частично), еще одно улучшение:

  'use strict';
  (() => {
    const modified_inputs = new Set();
    const defaultValue = 'defaultValue';
    // store default values
    addEventListener('beforeinput', evt => {
      const target = evt.target;
      if (!(defaultValue in target.dataset)) {
        target.dataset[defaultValue] = ('' + (target.value || target.textContent)).trim();
      }
    });

    // detect input modifications
    addEventListener('input', evt => {
      const target = evt.target;
      let original = target.dataset[defaultValue];

      let current = ('' + (target.value || target.textContent)).trim();

      if (original !== current) {
        if (!modified_inputs.has(target)) {
          modified_inputs.add(target);
        }
      } else if (modified_inputs.has(target)) {
        modified_inputs.delete(target);
      }

      if(modified_inputs.size){
        const event = new Event('needSave')
        window.dispatchEvent(event);
      }
    });

    addEventListener(
      'saved',
      function(e) {
        modified_inputs.clear()
      },
      false
    );

    addEventListener('beforeunload', evt => {
      if (modified_inputs.size) {
        const unsaved_changes_warning = 'Changes you made may not be saved.';
        evt.returnValue = unsaved_changes_warning;
        return unsaved_changes_warning;
      }
    });

    const ua = navigator.userAgent.toLowerCase();

    if(/MicroMessenger/i.test(ua)) {
      let pushed = false

      addEventListener('needSave', evt => {
        if(!pushed) {
          pushHistory();

          window.addEventListener("popstate", function(e) {
            if(modified_inputs.size) {
              var cfi = confirm('确定要离开当前页面嘛?' + JSON.stringify(e));
              if (cfi) {
                modified_inputs.clear()
                history.go(-1)
              }else{
                e.preventDefault();
                e.stopPropagation();
              }
            }
          }, false);
        }

        pushed = true
      });
    }

    function pushHistory() {
      var state = {
        title: document.title,
        url: "#flag"
      };
      window.history.pushState(state, document.title, "#flag");
    }
  })();

0

Я сделал это по-другому, поделившись здесь, чтобы кто-то мог получить помощь, протестирован только с Chrome.

Я хотел предупредить пользователя перед закрытием вкладки, только если есть какие-то изменения.

<input type="text" name="field" value="" class="onchange" />

var ischanged = false;

$('.onchange').change(function () {
    ischanged = true;
});

window.onbeforeunload = function (e) {
    if (ischanged) {
        return "Make sure to save all changes.";
    }        
};

Работает хорошо, но есть другая проблема: когда я отправляю форму, я получаю нежелательное предупреждение, я вижу много обходных путей, это происходит потому, что onbeforeunload срабатывает перед отправкой, поэтому мы не можем обработать его в событии onsubmit, как onbeforeunload = null, но Событие onclick кнопки отправки срабатывает перед обоими этими событиями, поэтому я обновил код

var isChanged = false;
var isSubmit = false;

window.onbeforeunload = function (e) {
    if (isChanged && (!isSubmit)) {
        return "Make sure to save all changes.";
    }        
};

$('#submitbutton').click(function () {
    isSubmit = true;
});

$('.onchange').change(function () {
    isChanged = true;
});

-5

Прежде всего, большинство браузеров имеет эту функцию по умолчанию. А зачем тебе это вообще? Почему бы не сохранить форму синхронизированной? Я имею в виду, сохранить его на любое изменение, не дожидаясь отправки от пользователя. Как это делают контакты Google. Конечно, если только все поля в форме являются обязательными. Пользователям не нравится, когда они заставляют что-то заполнять без возможности уйти, чтобы подумать, нужно ли им это. :)


Будете ли вы правы, но мои формы генерируются динамически, и поля не находятся под моим контролем, поэтому для меня невозможно отследить все поля и отправить запрос через ajax, если вы что-то имеете в виду, пожалуйста, поделитесь со мной.
Хан

Ладно, это может не понадобиться для ваших целей, но все же может быть реализовано. Если элементы формы находятся на экране, вы можете прикрепить свои собственные обработчики событий и получить любые данные, которые вы хотите. Затем используйте AJAX для сохранения данных в любом месте. Даже если формы имеют случайно сгенерированные идентификаторы или имена классов, вы можете использовать XPath, если можете предвидеть структуру формы. Это правильно XML / HTML? Таким образом, вы могли бы даже иметь JAD с людьми, ответственными за динамически генерируемую форму, и поговорить о том, как XSTL может помочь вам обоим в этом проекте. Просто мысль ...
SimonDever

1
save it on any change without waiting any submitting from userВы не всегда хотите это делать. Иногда пользователь хочет внести много изменений, а затем просмотреть и подтвердить, что он хочет сохранить его.
BadHorsie

1
Я не предлагал отправить форму. Данные могут быть сохранены в localStorage, в некотором кеше или даже представлены с каким-либо флагом черновика. Есть много способов для этого поведения. Это намного лучше, чем предупреждать пользователя о том, что он не заполнил форму, и если он не захочет закрыть окно, ему придется заполнить его снова в следующий раз. «Давай, мужик, не ленись, твой поезд / автобус / самолет / и т. Д. Не имеет значения, а также нам все равно, что ты забыл зарядное устройство со своего ноутбука, просто заполни эту чертову форму!»
YuS
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.