Форум Flasher.ru

Форум Flasher.ru (http://www.flasher.ru/forum/index.php)
-   ActionScript 3.0 (http://www.flasher.ru/forum/forumdisplay.php?f=83)
-   -   Кто и как хранит данные в конфигах? (http://www.flasher.ru/forum/showthread.php?t=172753)

carrotoff 16.12.2011 11:43

Кто и как хранит данные в конфигах?
 
Интересно ваше мнение. Здесь много сторонников MVC подхода. Если рассматривать Model, как Data контейнер, то в каком формате вы храните там данные: xml, object, json или же под каждое поле создаете переменные (ветвите модели). Ну вопрос, конечно, актуален и не для MVC.

Как хранятся у вас сложные структуры данных? Ну первый пример, приходящий на ум, - это товары в магазине, которые могут фильтроваться по рубрикам, сортироваться по цене, ну и другие критерии. Как обновляете (удаляете/добавляете) такие структуры?

Ну сразу оговорюсь про себя. С сервером мы общаемся посредством xml, потому сложные структуры так и хранятся, ну а простые, как обычные свойства классов.

В общем, поделитесь своим опытом.

P.S. Тему немного странно назвал, ну я еще сплю :)

Genzo 16.12.2011 11:52

Лично я использую VO под это дело, если брать именно информационные классы, такие как : Version или ApplicationInfo : [version, creator, date]... , то можно хардкодить константы и делать их статическими.

carrotoff 16.12.2011 11:56

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

-De- 16.12.2011 12:01

xml и json - для сохранения данных в текстовом формате. Object - для хранения пар строка-переменная (где какая именно строка - сложно предугадать). Если известна структура, то лепить структуру (POD классы).
Ну вот как-то так, по капитански)

Если нужны выборки с условиями, то тут встает вопрос - какие выборки

carrotoff 16.12.2011 12:20

Цитата:

то тут встает вопрос - какие выборки
Ну продолжении темы товаров магазина: имеем загруженный xml с сервера со списком всех товаров. А показывать в клиенте их нужно по рубрикам.

Код:

<items>
  <item>
      <name>Товар1</name>
      <rubric_id>1</rubric_id>
  </item>
  <item>
      <name>Товар1</name>
      <rubric_id>2</rubric_id>
  </item>
</items>

Ну как-то так. Допустим, нужно отобразить товары с rubric_id = 1, Вот такие структуры у меня хранятся, как и пришли с сервера, в xml, ибо удобно. Но интересно - может существует более тру подход.

GBee 16.12.2011 12:44

У нас тяжелый клиент. Все храним в БД в "нормальном" виде, разбитые по полям. Есть поля, которые нужны серверу / серверу и мне / только мне. То что нужно только клиенту - хранится как JSON. Практически у каждой сущности есть поле settings. А кидаемся хмльками. Списочные данные выкачиваются при старте флэшки. Прилетает несолько хмл-простыней, парсятся в массивы/хеши с нормальными объектами.

Локали и конфиг флэшки просто хмльки рядом с флэшкой лежат.

Добавлено через 3 минуты
Цитата:

Но интересно - может существует более тру подход.
Не знаю насколько тру, но терпеть не могу динамические объекты. Распарсил бы в объекты пихнул в массив и фильтровал по rubricId.

carrotoff 16.12.2011 13:02

А в чем фишка парсить xml в массив объектов? Если xml фактически есть объект с удобным поиском

Aquahawk 16.12.2011 13:09

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

GBee 16.12.2011 13:18

У меня у объектов от 5 до 40 параметров, вспоминать как их назвал серверный прогер меня не радует. Кроме того, там есть временные параметры для ускорения работы, генерящиеся из других параметров. Для простых приложений я тоже скорее всего буду использовать, а может не буду - не люблю.

Завтра на сервере скажут, что магазин увеличил кол-во товара, и теперь рубрики станут подрубриками, а рубриками будет что-то другое, и теперь проперть будет называться sub_rubric_id. Сколько классов вам надо бдет изменить? Мне один.

wvxvw 16.12.2011 13:28

На прошлой работе пробовал написать генератор из XML в AS. Но его очень трудно было бы сделать универсальным, да и XML - совсем не лучшее место для хранения настроек. Еще делал генератор из SQL в AS - не особо удобно пользоваться, особенно, если SQL плохо знаешь :) Была мысль написать генератор AMF данных, но возникла неопределенность с форматом ввода :) Но если когда-нибудь руки дойдут и придумаю как именно сделать ввод удобным - последнее, наверное, было бы самым хорошим вариантом.

carrotoff 16.12.2011 13:36

Цитата:

Завтра на сервере скажут, что магазин увеличил кол-во товара, и теперь рубрики станут подрубриками, а рубриками будет что-то другое, и теперь проперть будет называться sub_rubric_id. Сколько классов вам надо бдет изменить? Мне один.
Можно, я на ты:)
Вань, это вопрос преобразования данных. Я их также могу пропускать через некоторый посредник, преобразовывая в удобоваримый тип. И этот удобоваримый тип также может быть XML, а не массив объектов. Вот.

GBee 16.12.2011 13:39

хмл в хмл, мсье знает толк в извращениях. :о))

carrotoff 16.12.2011 13:43

Ну я такой подход не применяю, ибо это не есть хорошо:) Это скорее некое абстрактное суждение) Я написал это применительно к вопросу, в чем преимущество массива объектов перед xml.

Ну даже не так. Мне не нужно применять поход с посредником в принципе, ибо данные с сервера приходят в нормальном виде.
И я спрашиваю, какой смысл мне перегонять данные из xml в массив объектов, ежели я получу ровно ту же самую структуру. Вот.

Genzo 16.12.2011 13:47

Вот пример как из xml делать VO, ну просто так для галочки сюда выложу :
Код AS3:

package ***.model.vo
{
    public class RecordInfoVO implements IValueObject
    {       
        private var _contentName:String;
        private var _contentUrlkey:String;
                private var _contentUri:String;
        private var _contentPerformer:String;
 
        private var _recordId:uint;
        private var _recordUsername:String;
        private var _recordRate:uint;
        private var _recordFlvUrl:String;
        private var _recordPreviewUrl:String;
        private var _recordHasVideo:Boolean;
 
        private var _recordDuration:Number;
 
        private static var _loaded:Boolean = false;
        public static function load():void
        {
            if(_loaded != true) _loaded = true;
        }
 
        public static function get loaded():Boolean
        {
            return _loaded;
        }
 
        public function RecordInfoVO()
        {
        }
 
        public function parseXML(data:XML):void
        {
            this._contentName = data.content.name;
            this._contentUrlkey = data.content.urlkey;
            this._contentPerformer = data.content.performer;
            this._recordId = data.record.id;
            this._recordUsername = data.record.username;
            this._recordRate = data.record.rate;
            this._recordFlvUrl = data.record.flvurl;
            this._recordPreviewUrl = data.record.previewurl;
            this._contentUri = data.content.uri;
            this._recordHasVideo = Boolean(data.record.hasvideo);
        }
 
        public function get contentName():String
        {
            return this._contentName;
        }
 
        public function get contentUrlkey():String
        {
            return this._contentUrlkey;
        }
 
        public function get contentPerformer():String
        {
            return this._contentPerformer;
        }
 
        public function get recordId():uint
        {
            return this._recordId;
        }
 
        public function get recordUsername():String
        {
            return this._recordUsername;
        }
 
        public function get recordRate():uint
        {
            return this._recordRate;
        }
 
        public function get recordFlvUrl():String
        {
            return this._recordFlvUrl;
        }
 
        public function get recordPreviewUrl():String
        {
            return this._recordPreviewUrl;
        }
 
        public function get recordHasVideo():Boolean
        {
            return this._recordHasVideo;
        }
 
        public function get recordDuration():Number
        {
            return _recordDuration;
        }
 
        public function set recordDuration(value:Number):void
        {
            _recordDuration = value;
        }
 
                public function get contentUri():String
                {
                        return _contentUri;
                }
 
 
    }
}

Код AS3:

package ***.model.vo
{
    public interface IValueObject
    {
        function parseXML(data:XML):void
    }
}


GBee 16.12.2011 13:47

wvxvw, Genzo
Я использую простую систему. Все объекты, которые заполняются из хмл, имеют конструктор вида function FromXMLObject(xml:XML=null) и там параметры раскидываются куда надо.

когда прилетает хмл есть утилитная функция
Код AS3:

public static function xmlListToObjectArray(list:XMLList, cls:Class):Array
        {
                var arr:Array = new Array();
                var len:int = list.length();
                for(var i:int = 0; i<len; i++)
                {
                        var obj:Object = new cls(list[i]);
                        arr.push(obj);
                }
                return arr;
        }

Когда-то раньше пробовал совсем универсально сделать, чтобы в хмл прилетало название класса, и совсем автоматом генерить, но потом получилось куча исключений, и решил писать руками.

Genzo 16.12.2011 13:48

Код AS3:

private function onSucces(e:Event):void
        {
            this._loader.removeEventListener(Event.COMPLETE, onSucces);
            this._loader.removeEventListener(IOErrorEvent.IO_ERROR, onError);
            this._loader.removeEventListener(SecurityErrorEvent.SECURITY_ERROR, onError);           
            try{
                var xml:XML = XML(this._loader.data);
                var voClass:Class = getDefinitionByName("***.model.vo." + (xml.localName() as String)) as Class;
                var data:IValueObject = new voClass();
                data.parseXML(xml);
                this._loader.close();
                this._loader = null;
                this.dispatchEvent(new XMLLoaderEvent(XMLLoaderEvent.DATA_LOADED , data))
            }catch(e:*){
                this.dispatchEvent(new XMLLoaderEvent(XMLLoaderEvent.DATA_ERROR));
            }
 
        }


GBee 16.12.2011 13:50

От варианта Genzo отказался пару лет назад, почему не помню, но сейчас он мне кажется более адекватным, чем мой. Хм.

carrotoff 16.12.2011 13:54

Цитата:

когда прилетает хмл есть утилитная функция
Код AS3:

public static function xmlListToObjectArray(list:XMLList, cls:Class):Array
        {
                var arr:Array = new Array();
                var len:int = list.length();
                for(var i:int = 0; i<len; i++)
                {
                        var obj:Object = new cls(list[i]);
                        arr.push(obj);
                }
                return arr;
        }


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

Aquahawk 16.12.2011 13:56

решил открыть свой велосипед
https://bitbucket.org/Aquahawk/configmanager/

Тесты там ни к чёрту, доки нет, часть туду в коде, но я этим пользуюсь. Можно считать что это pre alpha

Добавлено через 38 секунд
гитигнор не настроен, билд скрипты не вынесены отдельно

carrotoff 16.12.2011 13:58

Цитата:

От варианта Genzo отказался пару лет назад, почему не помню, но сейчас он мне кажется более адекватным, чем мой. Хм.
Мне тоже нравится:)

Nooob 16.12.2011 17:40

лучше так
Код AS3:

package ***.model.vo
{
    public class RecordInfoVO
    {       
//private var
        public static function parseXML(data:XML):RecordInfoVO
        {
            var vo:RecordInfoVO = new RecordInfoVO();
            vo._contentName = data.content.name;
            vo._contentUrlkey = data.content.urlkey;
            vo._contentPerformer = data.content.performer;
            vo._recordId = data.record.id;
            vo._recordUsername = data.record.username;
            vo._recordRate = data.record.rate;
            vo._recordFlvUrl = data.record.flvurl;
            vo._recordPreviewUrl = data.record.previewurl;
            vo._contentUri = data.content.uri;
            vo._recordHasVideo = Boolean(data.record.hasvideo);
            return vo;
        }
 
        public function RecordInfoVO()
        {
        }
//public get function
    }
}

Код AS3:

var recordInfoVO:RecordInfoVO = RecordInfoVO.parseXML(data);


wvxvw 16.12.2011 17:53

У XML есть несколько существенных недостатков:
<foo bar="100"/> - bar число или строка, если число, то какой разрядности? SQL таким не страдает.
<foo><bar/></foo><foo><bar/></foo> - первый bar и второй - это один и тот же объект, или нет? AMF этим не страдает. В SQL можно договориться, что ключи - это ссылки, а остальные значения - всегда не-ссылки.
Для AIR, наверное, самым правельным было бы использовать встроенный SQL, для веб - я бы наверное все-таки написал какой-нибудь конвертор на том же AIR, который бы нужные данные экспортировал в AMF и потом бы я их просто встраивал / загружал - что по ситуации подходит.

GBee 16.12.2011 18:23

Если использовать выше указанные классы, то первый недостаток - таковым не является.
Если писать нечто универсальное, то да.

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

dimarik 16.12.2011 23:08

Цитата:

Сообщение от -De- (Сообщение 1051875)
(POD классы).

Цитата:

Сообщение от Wikipedia, the free encyclopedia
Computers

Ping of death, a type of attack on a computer
Plain Old Data structure, a term for all data types from the C programming language in C++
Plain Old Documentation, a platform-independent documentation tool for the computer language Perl
Point of Delivery, a module or group of network, computer, storage, and application components that work together to deliver a network service
Power-on diagnostics, also known as the power-on self-test, the start up sequence of computers before the pperating system is loaded
Python Open Document

Какой именно POD подразумевался?

Добавлено через 9 минут
Цитата:

Сообщение от carrotoff (Сообщение 1051892)
Можно, я на ты:)
Вань, это вопрос преобразования данных. Я их также могу пропускать через некоторый посредник, преобразовывая в удобоваримый тип. И этот удобоваримый тип также может быть XML, а не массив объектов. Вот.

Подтверждение того, что клиент сильно зависит от внешней среды (протокола). При этом можно бесконечно создавать десериализаторы (посредники, в тексте), если среда неоднородна. А приложение — это комплекс клиентской и серверной стороны. Договаривайтесь о протоколах.

Добавлено через 12 минут
Цитата:

Сообщение от Genzo (Сообщение 1051897)
Код AS3:

private function onSucces(e:Event):void
        {
            this._loader.removeEventListener(Event.COMPLETE, onSucces);
            ...
            super.dispatchEvent(new XMLLoaderEvent(XMLLoaderEvent.DATA_LOADED , data));   
        }


super, если не переопределили.

Добавлено через 21 минуту
Цитата:

Сообщение от Genzo (Сообщение 1051897)
Код AS3:

private function onSucces(e:Event):void
        {
                this._loader.close();
                this._loader = null
 
        }


Это зачем? close() вообще не понятно для чего. У вас там потоковая загрузка? Long-poll?
null зачем? Вероятно, лоадер будет использоваться еще не один раз. Каждый раз создавать заново лоадер? Это антиоптимизация.

expl 16.12.2011 23:36

Обычно поступаем так:

Есть конфиг клиента - это просто список поле-значение в xml, причем если через флеш-варс передали такое же поле как в конфиге - оно перетирается (это позволяет использовать конфиг лежащий на сервере, поправляя данные в HTML-ке чтобы грузиться локально - ну это если лень локальный конфиг создавать полностью). В конфиге обычно храниться информация что откуда брать - то, что зависит от места запуска флешки, не более.

Остальные данные от сервера делятся на статические и динамические.
Динамические - это то, что клиент получает, отправляя запросы
Статические - это так называемые "справочники" - там начиная от списка товаров с параметрами, заканчивая геометрией "фермы" и общими с сервером константами
На клиенте представлены набором классов, которые парсят их в типизированные объекты, типа
Код AS3:

public class ShopDictionary
{
    public function parseXML(xml:xml):void {...}
    public function get items():Vector<Item> {}
}
// или
public class VegetablesDictionary
{
  public function parseXML(xml:XML)
  public function getByType(type:int):Vegetable;
}

Статика грузится вся сразу, даже делаются некоторые проверки валидности, чтобы авторы справочников не косячили, например в одном справочнике не ссылались на итемы, отсутствующие в другом.

Цитата:

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

dimarik 16.12.2011 23:37

Цитата:

Сообщение от Nooob (Сообщение 1051961)
лучше так
Код AS3:

package ***.model.vo
{
    public class RecordInfoVO
    {       
        public static function parseXML(data:XML):RecordInfoVO
        {
            var vo:RecordInfoVO = new RecordInfoVO();
            vo._contentName = data.content.name;
            ...
            return vo;
        }

Код AS3:

var recordInfoVO:RecordInfoVO = RecordInfoVO.parseXML(data);


А можно и заранее сделать рыбу-модель и разбирать значения в нее, уже готовую. В вашем случае нет никакого оповещения ("приватки" же задаете) об изменении модели.

Добавлено через 4 минуты
Цитата:

Сообщение от expl (Сообщение 1052029)
Обычно поступаем так:

Тупые серверы всегда так поступают. Статика вполне может быть скомпилена в swf c необходимой структурой.
А чуть подразгонятся, то и динамика туда же.

expl 16.12.2011 23:46

Код AS3:

Статика вполне может быть скомпилена в swf c необходимой структурой.

Да? Т.е. вы не одобряете необходимость парсинга? или формат?
Еще есть проблема: "как эту swf серверу парсить - он же те же данные использует?"

Но было бы интересно узнать подробности про способ и преимущества загона в swf

dimarik 16.12.2011 23:48

Совершенно не одобряю этой суеты, вы правильно подметили. Мне на работе это обрыдло.

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

expl 16.12.2011 23:57

Все-таки хотелось бы узнать:
- что идет на входе в swf
- как с этой swf работает сервер (или она генерится сервером? тогда по каким данным?)
- что получает клиент из этой swf и как с ней работает

dimarik 17.12.2011 00:00

- Простите, не понял;
- Генерирует сервер по протоколу;
- Клиент получает объект (character, item, что-то еще, что я упустил).

Альтернативе (3Д, которая) не откажешь в гибкости.

expl 17.12.2011 00:12

с одной стороны получается
- xml
- парсинг на сервере
- парсинг на клиенте
с другой
- протокол (в каком формате, кстати?)
- парсинг на сервере
- генерация на сервере
По трудозатратам примерно тоже самое (однако со вторым случаем придется повозиться с генератором swc/swf/as3).
Но, как я понимаю есть один большой плюс: работа с протоколом сосредоточена в одном месте - на сервере, т.е. осутствуют проблемы синхроницации протоколе, сложнее ошибиться на клиенте.
А если вспомнить про динамику - то сложнее ошибиться и при отправке/обработке запроса

Но, ИМХО для проектов средней сложности дешевле работать с парсингом, меньше граблей, на которые можно напороться, нет?

Цитата:

- Простите, не понял;
Я сначала подумал, что сервер парсит swf-ку :wacko:, а она еще отку-да-то генерится, но раз это не так, то вопрос не имеет смысла.

dimarik 17.12.2011 00:22

Цитата:

Сообщение от expl (Сообщение 1052039)
Но, как я понимаю есть один большой плюс: работа с протоколом сосредоточена в одном месте - на сервере, т.е. осутствуют проблемы синхроницации протоколе, сложнее ошибиться на клиенте.

Добавлю от себя, плюс в том, что моя нехилая головная боль таким образом перекладывается на серверных программистов. А я сижу и пишу доки по типизированию объектов.

Для небольших проектов, вероятно, велосипед может оказаться пятиколесным.

expl 17.12.2011 00:26

Более-менее понятно.
Т.е. протокол - это просто доки получается. А объекты то сами откуда берутся, из базы?
Или "Я клиент, ничего не хочу знать, кроме того, что здесь лежить swf со статикой, а вот на эти запросы мне вернут динамику, которую я просто дисериализую в типизированные объекты?"

А, ну у нас, кстати, были проекты, в которых хранилищем статики была не xml, а база и специальный серверный скрипт генерил xml исключительно для клиента.
Можно, наверное, было и в amf и в сериализованные типизированные объекты генерить, но как-то серверист не горел желанием трахаться с генератором, а клиентщиков вообще не напрягал парсинг.

Просто точка "!№;;!! Опять изменился протокол, где это долбаное поле?!! Что здесь должен присылать сервер!!?" была очень-очень далеко и так и не наступила по сравнению с другими проблемами.

wvxvw 17.12.2011 00:29

Цитата:

Сообщение от GBee (Сообщение 1051969)
Если использовать выше указанные классы, то первый недостаток - таковым не является.
Если писать нечто универсальное, то да.

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

Замысел в том, что AS класс не нужно самому писать - его должно что-то генерить. Генерация - вопрос не сложный. Сложность заключается в правельной передаче информации. А тут XML плохо подходит, т.как нет прямого соответствия типов.
Второй недостаток никак этим не "лечится" - как вы из XML поймете, что объект создается по ссылке (т.е. не создается вообще)?

dimarik 17.12.2011 00:39

XML нужен для людей, а не ЭВМ. Передача информации в этом формате сопряжена, как сказал бы мой коллега, с большим оверхедом. Сравните русский и английский языки. Первый для передачи эмоций.
Но если вы читаете байткод как Кира Булычева, то можете игнорировать мои заявления.

wvxvw 17.12.2011 00:46

Да нет такой связи... XML - только один из возможных форматов, со своими недостатками. Вообще изначально даже не задумывался для хранения информации, только для презентации. Тот же Protobuf при том, что в исходнике не менее читаемый, не страдает такими недостатками.

Кстати, раз уж мы заговорили о презентации информации, то, например, предствить табличные данные так, как это делает SQL - это гораздо более читаемо, чем XML. Там сaрзу же и на колонки разбито, и выравнено + заголовки над колонками говорят о типе и назначении поля - гораздо удобнее что-то искать / сравнивать значения и т.п.

dimarik 17.12.2011 01:00

Да есть такая связь, есть. Но я уважаю чужое мнение и не могу с ним не согласиться.
Зачем для передачи информации (все помнят, что такое бит?) кодировать бит с оверхедом over 9000?

GBee 17.12.2011 01:59

Цитата:

Замысел в том, что AS класс не нужно самому писать - его должно что-то генерить. Генерация - вопрос не сложный. Сложность заключается в правельной передаче информации. А тут XML плохо подходит, т.как нет прямого соответствия типов.
Не могу не согласится, просто мне в этом направлении знаний/опыта пока не хватает.

Значит dimarik за честную (де)сериализацию? Где вы таких покладистых серверных прогеров находите? :о))

carrotoff 17.12.2011 12:00

Цитата:

Сообщение от dimarik
Статика вполне может быть скомпилена в swf c необходимой структурой.

dimarik, а как вы храните динамические структуры? Поделитесь опытом

dimarik 18.12.2011 22:53

Под статикой для "фермы", вероятно, подразумевались прототипы объектов в рамках предметной области приложения. Имеем один раз сформированный файл (я предложил, в формате swf) со списком таких предметов (синонимы: артикул, шаблон). Например, есть предмет на грядке protoId=1, id=1, count=2. По protoId узнаем остальные характеристики (цвет, название, стоимость, что-то еще, что общее для такого типа объекта) предмета из статического списка. Собрали с грядки один предмет (count-1), получили второй предмет protoId=1, id=2, count=1. Первый предмет изменил свой count на count=1. Так вот динамика передается сервером и определяет что остается на грядке и что собрано.

Вернемся к вашему вопросу. Динамические структуры я не храню. Я меняю модель. Если модель динамическая структура, а она динамическая, то я храню ее... в памяти ) В общем, я не понял вопроса.


Часовой пояс GMT +4, время: 17:39.

Copyright © 1999-2008 Flasher.ru. All rights reserved.
Работает на vBulletin®. Copyright ©2000 - 2026, Jelsoft Enterprises Ltd. Перевод: zCarot
Администрация сайта не несёт ответственности за любую предоставленную посетителями информацию. Подробнее см. Правила.