text:
module foxtools {

	/**
	 * Классы для работы с хитрым API v2.0
	 */
	export module lib {

		/**
		 * Статичный класс хитрых настроек хитрой библиотеки.
		 */
		export class config {

			/**
			 * Имя куки, где следует хранить маркер доступа.
			 */
			static tokenCookieName: string = 'foxtoken';

			/**
			 * Базовый адрес, из которого формируется адрес, на который будет отправлен запрос классом request.
			 */
			static requestBaseUrl: string = '{0}://api.foxtools.ru/v2/{1}';

			/**
			 * Указывает, следует использовать защищенное соединение при выполнении запросов классом request.
			 * По умолчанию - false (использовать обычное соединение).
			 */
			static useSsl: boolean = false;

		}

		/** Представляет процесс выполнения запроса. */
		export class requestProcess {

			/** Маркер доступа, либо адрес для получения маркера доступа. */
			public token: any = null;

			/** Функция, которая будет вызвана после завершения выполнения запроса. */
			public complete: (result: any) => any;

			/** Функция, которая будет вызвана перед отправкой запроса. */
			public begin: (data: beginRequest) => any;

			/**
			 * Функция, которая будет вызываться при изменении процесса отправки запроса на хитрый сервер.
			 * @param percent Процент переданных данных.
			 * @param loaded Количество переданных байт.
			 * @param total Общий объем данных, которые будут отправлены на сервер.
			 */
			public uploading: (percent: number, loaded: number, total: number) => any;

			constructor() {
			}

		}

		/** Представляет базовый класс результата выполнения запроса. */
		export class baseResult {

			/** Тип результата. */
			public type: string;

			/** Системные сообщения. */
			public messages: Array<messageItem> = new Array<messageItem>();

			/** Детали выполнения запроса, если параметр details (при запросе) был равен true. */
			public trace: Array<traceItem> = new Array<traceItem>();

			/**
			 * Инициализирует новый экземпляр baseResult.
			 * @param t Тип запроса.
			 */
			constructor(t: string) {
				if (t === undefined || t === null || t === '') {
					throw new Error('Type is required.');
				}
				this.type = t;
			}

			public addTrace(message: string, elepsedTime: string): void {
				this.trace.push(new traceItem(message, elepsedTime));
				console.log(foxtools.utility.format('TRACE: {0} - {1}', [message, elepsedTime]));
			}

			public addMessage(type: string, text: string): void {
				this.messages.push(new messageItem(type, text));
				console.log(foxtools.utility.format('{0}: {1}', [ type, text ]));
			}

		}

		/**
		 * Клиент для работы с хитрым API v2.0.
		 */
		export class client {

			/**
			 * Универсальная вспомогательная функция, которая возвращает маркер доступа. Если необходимо, делает запрос к ВАШЕМУ серверу.
			 * @param url Адрес ВАШЕЙ страницы, которая вернет маркер доступа для текущего пользователя, если маркера не будет в cookies.
			 */
			static getToken(url: any, options?: any): string {
				var token = null;

				// если в первый параметр передали объект, обрабатываем его
				if (typeof url == 'object') {
					if (options === undefined && options === null) {
						options = url;
					} else {
						$.extend(options, url);
					}
					// адрес должен быть в параметре url
					url = options.url;
				}

				// чтобы не быть нулем, достаточно быть пустым
				options = options || {};

				// проверяем куку
				if (!(token = utility.readCookies(config.tokenCookieName))) {
					// нету в куки не куку, получаем маркер с сервера
					if (url === undefined || url === null) {
						console.log('Url is empty. No way to get a new token...');
						return token;
					}

					// адрес есть, дергаем его, за ухи
					$.ajax({
						type: options.type || 'POST',
						enctype: options.enctype || 'application/x-www-form-urlencoded',
						url: url,
						success: function (result) {
							// маркер доступа успешно получен, танцуют все!

							// смотрим, в каком формате данные
							if (typeof result == 'object') {
								// ай-я-яй, какая жаль
								// смотрим, что есть в объекте
								if (result.token !== undefined && result.token !== null && typeof result.token == 'string') {
									result = result.token;
								}
								else if (result.value !== undefined && result.value !== null && typeof result.value == 'string') {
									result = result.value;
								}
								else if (result.data !== undefined && result.data !== null && typeof result.data == 'string') {
									result = result.data;
								}
								else {
									// бамбарбия киргуду!
									// берем первое, что есть
									result = result[Object.keys(result)[0]];
								}
							}

							// бережно кладем маркер доступа в куку
							utility.createCookies(config.tokenCookieName, result);

							if (options.complete !== undefined && options.complete !== null && typeof options.complete === 'function') {
								// есть куда передавать ответ, передаем
								options.complete(new stringResult(result));
							}
						},
						error: function (x, textStatus, errorThrown) {
							// ошибка
							if (options.complete !== undefined && options.complete !== null && typeof options.complete === 'function') {
								// есть куда передавать ответ, передаем
								options.complete(new errorResult(new errorMessage(0, textStatus + ': ' + errorThrown)));
							} else {
								// некуда передавать ответ, просто пишем в консоль
								console.log(textStatus + ': ' + errorThrown);
							}
						}
					});
				}

				return token;
			}

			/**
			 * Делает указанную форму хитрой.
			 *
			 * @param selector Имя элемента или объект JQuery.
			 * @param options Дополнительные параметры.
			 */
			static foxyForm(selector?: string|HTMLFormElement|JQuery, options?: requestProcess): void {
				if ($(selector).length <= 0) {
					return;
				}
				// если есть к чему привязаться, привязываемся
				$(selector).each(function () {
					if (this.tagName.toLowerCase() == 'form') {
						// нашли форму, привязываем к событию отправки
						$(this).submit(function () {
							// создаем запрос
							options = options || new requestProcess();
							var request = new formRequest(this);
							request.token = options.token;
							request.complete = options.complete;
							request.begin = options.begin;
							request.uploading = options.uploading;
							// проверяем атрибуты формы
							if ($(this).data('begin') !== undefined && $(this).data('begin') !== '' && typeof window[$(this).data('begin')] === 'function') {
								request.begin = window[$(this).data('begin')];
							}
							if ($(this).data('complete') !== undefined && $(this).data('complete') !== '' && typeof window[$(this).data('complete')] === 'function') {
								request.complete = window[$(this).data('complete')];
							}
							if ($(this).data('token') !== undefined && $(this).data('token') !== '') {
								request.token = $(this).data('token');
							}
							// отправляем форму
							request.submit();
							// отменяем синхронную отправку формы
							return false;
						});
					} else {
						console.log('WARNING: Expected a form tag.');
					}
				});
			}

			/** Вызывается при загрузке документа. */
			static ready(): void {
				ya.init();
			}

		}

		/** Представляет аргументы события начала выполнения запроса. */
		export class beginRequest {

			/** Ссылка на запрос, который будет выполняться. */
			public request: requestProcess = null;

			/** Маркер доступа, который будет использоваться. */
			public token: foxtoolsToken = null;

			/** Указываает на необходимость отмены выполнение запроса. */
			public cancel: boolean = false;

			constructor(request: requestProcess, token: foxtoolsToken) {
				this.request = request;
				this.token = token;
			}

		}

		/** Представляет хитрый запрос к API из формы. */
		export class formRequest extends requestProcess {

			public form: HTMLFormElement = null;

			constructor(form: HTMLFormElement) {
				super();

				this.form = form;
			}

			/** Отправляет форму. */
			public submit() {
				var $request = this;

				// если есть маркер, hfp,bhftv tuj
				var token = foxtoolsToken.parse($request.token);

				// если есть адрес для получения маркера, а самого маркера нет
				if (token !== null && token.url !== null && token.value === null) {
					// смотрим, что есть в куке
					token.value = utility.readCookies(config.tokenCookieName);
				}

				// если опять есть адрес, а маркера нет, то делаем запрос на получения нового маркера
				if (token !== null && token.url !== null && token.value === null) {
					foxtools.lib.client.getToken(token.url, {
						type: $request.token.type,
						enctype: $request.token.enctype,
						complete: function (result: baseResult) {
							if (result.type == 'errorResult') {
								// ошибка, значит запрос невозможно выполнить
								throw new Error((<errorResult>result).getText());
							} else {
								// используем полученный маркер для выполнения запроса
								token.value = (<stringResult>result).value;
								$request.justSubmit(token);
							}
						}
					});
				} else {
					// адреса нет, либо есть маркер, просто выполняем запрос
					$request.justSubmit(token);
				}
			}

			/**
			 * Просто асинхронно отправляет форму.
			 * @param token Маркер доступа, который следует использовать для запроса.
			 */
			private justSubmit(token: foxtoolsToken) {
				var $request = this;
				var form = $request.form;
				var $form = $($request.form);

				//var enctype = $form.attr('enctype');
				var type = $form.attr('method') || 'GET';

				if ($('input[type=file]', form).length > 0) {
					type = 'POST';
					//enctype = 'multipart/form-data';
				}

				//if (type.toUpperCase() == 'POST' && (enctype === undefined || enctype === null || enctype == '')) {
				//enctype = 'application/x-www-form-urlencoded';
				//}

				var headers = null;

				// маркер доступа, если указан
				if (token !== null && token.value !== null && token.value != '') {
					headers = { Authorization: 'Bearer ' + token.value };
				}

				var methodName: string = $($form.attr('action').split('/')).last()[0].toString();
				methodName = (methodName.indexOf('?') != -1 ? methodName.substr(0, methodName.indexOf('?')) : methodName);

				// если есть обработчик, делаем событие begin
				if ($request.begin !== undefined && $request.begin !== null && typeof $request.begin === 'function') {
					var eventData = new beginRequest(<requestProcess>$request, token);
					$request.begin(eventData);
					// запрос отменен
					if (eventData.cancel) {
						return;
					}
					form = $request.form;
					$form = $(form);
				}
				// --

				var data = null;

				if (type.toUpperCase() == 'POST') { // && enctype == 'multipart/form-data'
					data = new window['FormData'](form); //TypeScript bug --new FormData(form);
				} else {
					data = $form.serialize();
				}

				ya.hit($form.attr('action'), methodName);

				$.ajax({
					type: type,
					url: $form.attr('action'),
					data: data,
					dataType: 'json', //jsonp
					headers: headers,
					xhr: function () {
						var xhr = $.ajaxSettings.xhr();
						if (xhr.upload) {
							xhr.upload.addEventListener('progress', function (e) {
								if (e.lengthComputable) {
									if ($request.uploading !== undefined && $request.uploading !== null && typeof $request.uploading === 'function') {
										$request.uploading(Math.round((e.loaded * 100) / e.total), e.loaded, e.total);
									}
								}
							}, false);
						}
						return xhr;
					},
					beforeSend: function (x) {
						// ставим отметку, что началось выполнение запроса
						$form.removeData('completed');
						$form.data('sending', 'true');
					},
					success: function (r) {
						if ($request.complete !== undefined && $request.complete !== null && typeof $request.complete === 'function') {
							$request.complete(new result(methodName, 200, null, r));
						} else {
							new result(methodName, 200, null, r);
						}
					},
					error: function (x, textStatus, errorThrown) {
						var r = new result(methodName, x.status, errorThrown, $.parseJSON(x.responseText || errorThrown)); // todo: проверить возможность использования x.responseJSON
						// смотрим, если ошибка связана с маркером доступа
						if (r.data.type == 'errorResult' && $.grep((<errorResult>r.data).items, function (error) { return error.code == errorCode.AccessTokenError || error.code == errorCode.AccessTokenNotFound || error.code == errorCode.NoPermissionToUseTokenForCurrentIp }).length > 0) {
							// то запрашиваем новый маркер; конечно, при наличии адреса и отсутствии ошибок получения
							if (token.url !== null && token.errors == 0) {
								// убиваем куку
								utility.eraseCookies(config.tokenCookieName);
								// адрес получения маркера передаем в запрос
								$request.token = { url: token.url, errors: token.errors + 1 };
								// выполняем запрос повторно
								$request.submit();
								return;
							}
						}
						// другая ошибка, или нет адреса для получения маркера доступа
						if ($request.complete !== undefined && $request.complete !== null && typeof $request.complete === 'function') {
							$request.complete(r);
						}
					},
					complete: function (x, status) {
						// ставим отметку, что запрос успешно выполнен
						$form.removeData('sending');
						$form.data('completed', 'true');
					},
					cache: false,
					processData: false,
					contentType: false
				});
			}

		}

		/** Представляет хитрый запрос к хитрому API. */
		export class request extends requestProcess {

			/** Базовый адрес, из которого формируется конечный адрес, на который будет отправлен запрос. */
			public baseUrl: string = config.requestBaseUrl;

			/** Использовать защищенное соединение или нет (пока не используется, нет нормального сертификата). */
			public useSsl: boolean = config.useSsl;

			/**
			 * Метод, который следует использовать для запроса: GET или POST.
			 * Может быть определен автоматически, в зависимости от параметров запроса.
			 */
			public type: string = 'GET';

			/** Параметры запроса.*/
			public parameters: httpParameterCollection;

			/** Итоговый адрес, на который будет отправлен запрос (формируется автоматически). */
			private endpoint: string = '';

			/** Название метода, который следует выполнить. */
			private methodName: string = '';

			/**
			 * Инициализирует новый экземпляр хитрого запроса.
			 *
			 * @param methodName Название метода, который необходимо выполнить.
			 */
			constructor(methodName: string) {
				super();

				if (methodName === undefined || methodName === null) {
					throw new Error(
						'Method name is required. Please add method name to request:\r\n' +
						'You can find a list of methods on the page: http://api.foxtools.ru/v2'
					);
				}

				this.methodName = methodName;
				this.endpoint = utility.format(this.baseUrl, [(this.useSsl ? 'https' : 'http'), methodName]);
				this.parameters = new httpParameterCollection();
			}

			/** Выполняет запрос к API. */
			public execute() {
				var $request = this;

				// если есть, разбираем маркер
				var token = foxtoolsToken.parse($request.token);

				// если есть адрес для получения маркера, а самого маркера нет
				if (token !== null && token.url !== null && token.value === null) {
					// смотрим, что есть в куке
					token.value = utility.readCookies(config.tokenCookieName);
				}

				// если опять есть адрес, а маркера нет, то делаем запрос на получения нового маркера
				if (token !== null && token.url !== null && token.value === null) {
					foxtools.lib.client.getToken(token.url, {
						type: $request.token.type,
						enctype: $request.token.enctype,
						complete: function (result: baseResult) {
							if (result.type == 'errorResult') {
								// ошибка, значит запрос невозможно выполнить
								throw new Error((<errorResult>result).getText());
							} else {
								// используем полученный маркер для выполнения запроса
								token.value = (<stringResult>result).value;
								$request.justExecute(token);
							}
						}
					});
				} else {
					// адреса нет, либо есть маркер, просто выполняем запрос
					$request.justExecute(token);
				}
			}

			/**
			 * Совершенно точно и без лишних вопросов выполняет хитрый запрос к хитрому API.
			 * @param token Маркер доступа, который следует использовать при отправке запроса.
			 */
			private justExecute(token: foxtoolsToken) {
				var $request = this;

				// определяем тип запроса и тип содержимого
				//var enctype = null;
				var type = $request.type || 'GET';

				if ($request.parameters.hasFiles) {
					type = 'POST';
					//enctype = 'multipart/form-data';
				}

				//if (type.toUpperCase() == 'POST' && (enctype === undefined || enctype === null || enctype == '')) {
				//enctype = 'application/x-www-form-urlencoded';
				//}

				// если указан маркер доступа, используем его для данного запроса
				var headers = null;
				if (token !== null && token.value !== null && token.value != '') {
					headers = { Authorization: 'Bearer ' + token.value };
				}

				// если есть обработчик, делаем событие begin
				if ($request.begin !== undefined && $request.begin !== null && typeof $request.begin === 'function') {
					var eventData = new beginRequest(<requestProcess>$request, token);
					$request.begin(eventData);
					// запрос отменен
					if (eventData.cancel) {
						return;
					}
				}

				var data = $request.parameters.toParams(type);

				ya.hit($request.endpoint, $request.methodName);

				$.ajax({
					url: $request.endpoint,
					type: type,
					//dataType: 'jsonp',
					headers: headers,
					xhr: function () {
						var xhr = $.ajaxSettings.xhr();
						if (xhr.upload) {
							xhr.upload.addEventListener('progress', function (e) {
								if (e.lengthComputable) {
									if ($request.uploading !== undefined && $request.uploading !== null && typeof $request.uploading === 'function') {
										$request.uploading(Math.round((e.loaded * 100) / e.total), e.loaded, e.total);
									}
								}
							}, false);
						}
						return xhr;
					},
					// beforeSend: function (x) { },
					success: function (r) {
						if ($request.complete !== undefined && $request.complete !== null && typeof $request.complete === 'function') {
							$request.complete(new result($request.methodName, 200, null, r));
						} else {
							new result($request.methodName, 200, null, r);
						}
					},
					error: function (x, textStatus, errorThrown) {
						var r = new result($request.methodName, x.status, errorThrown, $.parseJSON(x.responseText || errorThrown)); // todo: проверить возможность использования x.responseJSON
						// смотрим, если ошибка связана с маркером доступа
						if (r.data.type == 'errorResult' && $.grep((<errorResult>r.data).items, function (error) { return error.code == errorCode.AccessTokenError || error.code == errorCode.AccessTokenNotFound || error.code == errorCode.NoPermissionToUseTokenForCurrentIp }).length > 0) {
							// то запрашиваем новый маркер; конечно, при наличии адреса и отсутствии ошибок получения
							if (token.url !== null && token.errors == 0) {
								// убиваем куку
								utility.eraseCookies(config.tokenCookieName);
								// адрес получения маркера передаем в запрос
								$request.token = { url: token.url, errors: token.errors + 1 };
								// выполняем запрос повторно
								$request.execute();
								return;
							}
						}
						// другая ошибка, или нет адреса для получения маркера доступа
						if ($request.complete !== undefined && $request.complete !== null && typeof $request.complete === 'function') {
							$request.complete(r);
						}
						//else {
						//new result($request.methodName, x.status, errorThrown, $.parseJSON(x.responseText || errorThrown));
						//}
					},
					complete: function (x, status) {

					},
					data: data,
					cache: false,
					contentType: false,
					processData: false
				});
			}

		}

		/** Представляет коллекцию параметров запроса. */
		export class httpParameterCollection {

			/** Список параметров, находящихся в коллекции. */
			public items: Array<httpParameter>;

			constructor() {
				this.items = new Array<httpParameter>();
			}

			/**
			 * Добавляет параметр в коллекцию.
			 * @param name Имя параметра, либо элемент.
			 * @param value Значение параметра.
			 */
			public add(name: string|HTMLElement|JQuery, value?: any) {
				this.items.push(new httpParameter(name, value));
			}

			/**
			 * Возвращает параметры в виде объекта для выполнения запроса.
			 * @param type Тип HTTP запроса: GET или POST.
			 */
			public toParams(type: string): Object {
				if (this.hasFiles || type === undefined || type === null) {
					type = 'POST';
				}
				type = type.toUpperCase();

				var result = null;

				if (type == 'POST') {
					result = new FormData();
				} else {
					result = new Object();
				}

				for (var i = 0; i <= this.items.length - 1; i++) {
					if (type == 'POST') {
						if (this.items[i].isFile) {
							if (this.items[i].value.length == 1) {
								result.append(this.items[i].name, this.items[i].value[0]);
							}
							if (this.items[i].value.length > 1) {
								for (var j = 0; j <= this.items[i].value.length - 1; j++) {
									result.append(this.items[i].name + '[]', this.items[i].value[j]);
								}
							} else {
								result.append(this.items[i].name, null);
							}
						} else {
							result.append(this.items[i].name, this.items[i].value);
						}
					} else {
						result[this.items[i].name] = this.items[i].value;
					}
				}

				if (type == 'POST') {
					return result;
				} else {
					return $.param(result);
				}
			}

			/** Возвращает true, если в коллекции есть файлы.*/
			get hasFiles(): boolean {
				return $.grep(this.items, function (item: httpParameter) {
					if (item.isFile) {
						return true;
					}
				}).length > 0;
			}

		}

		/** Представляет один единственный параметр запроса. */
		export class httpParameter {

			/** Имя параметра. */
			public name: string;

			/** Значение параметра. */
			public value: any;

			/** Содержит значение true, если параметр является файлом. */
			public isFile: boolean = false;

			/**
			 * Создает новый параметр.
			 * @param name Имя параметра или элемент.
			 * @param value Значение параметра.
			 */
			constructor(name: string|HTMLElement|JQuery, value?: any) {
				// проверяем наличие имени
				if (name === undefined || name === null) {
					throw new Error('Name is required.');
				}
				// проверяем, что указано в имени
				if (utility.isElement(name)) {
					// элемент формы
					this.name = $(name).attr('name');
					this.setValue(name);;
				}
				else if (utility.isJqueryObject(name)) {
					// объект jquery
					if ((<JQuery>name).length) {
						this.name = (<JQuery>name).attr('name');
						this.setValue(name);
					} else {
						throw new Error('jQuery object can not be empty.');
					}
				}
				else { // string и другие
					this.name = name.toString();
					this.setValue(value);
				}
			}

			/**
			 * Задает значение параметра в правильном виде.
			 * @param value Значение, которое нужно преобразовать в правильный вид.
			 */
			private setValue(value: any): void {
				if (utility.isElement(value)) {
					// элемент формы, делаем объект jQuery
					value = $(value);
				}
				else if (utility.isJqueryObject(value)) {
					// объект jQuery, проверяем наличие элемента
					if (!value.length) {
						throw new Error('jQuery object can not be empty.');
					}
				}
				else { // string и другие
					this.isFile = false;
					this.value = value;
					return;
				}

				if (value.is(':file')) {
					// выдергиваем файл, если это файл
					this.isFile = true;
					this.value = value[0].files;
				} else {
					// если не файл, то возвращаем строковое значение
					this.isFile = false;
					this.value = value.val();
				}
			}

		}

		/** Представляет результат выполнения запроса к API. */
		export class result {

			/** Данные результата выполнения запроса к API. */
			public data: baseResult;

			/** Метод, которому соответствует текущий результат выполнения запроса. */
			public methodName: string;

			constructor(methodName: string, statusCode: number, errorThrown: string, data: any) {
				if (statusCode === 0) {
					data.response.errors.items = [{ code: 0, text: 'Not connect. Verify Network.' }];
				} else if (statusCode == 404) {
					data.response.errors.items = [{ code: 0, text: 'Requested page not found [HTTP404].' }];
					/*} else if (x.status == 500) {
						r = new result(methodName, 'Internal Server Error [HTTP500].');*/
				} else if (errorThrown === 'parsererror') {
					data.response.errors.items = [{ code: 0, text: 'Requested JSON parse failed.' }];
				} else if (errorThrown === 'timeout') {
					data.response.errors.items = [{ code: 0, text: 'Timeout error.' }];
				} else if (errorThrown === 'abort') {
					data.response.errors.items = [{ code: 0, text: 'Ajax request aborted.' }];
				}

				if (data === undefined || data === null) {
					//throw new Error('Server response is empty.');
					return;
				}

				this.methodName = (methodName !== undefined && methodName !== null ? methodName.toLowerCase() : '');

				if (typeof data == 'object') {
					if (data.response === undefined || data.response === null) {
						//throw new Error('Server response is empty.');
						return;
					} else {
						data = data.response;
					}
				}
				else {
					this.data = new stringResult(data);
					return;
				}

				// есть информация об ошибках
				if (data.errors !== undefined && data.errors !== null) {
					this.data = new errorResult(data.errors);
				}
				else {
					// нормальный ответ, проверяем тип
					switch (this.methodName) {
						case 'geoip':
							this.data = new ipResult(data);
							break;

						case 'hash':
							this.data = new hashResult(data.items);
							break;

						case 'proxy':
							this.data = new proxyResult(data);
							break;

						// todo: классы http://api.foxtools.ru/v2
						case 'machinekey':
						case 'stat':
						case 'textdecoder':
						case 'translator':
						case 'weather':
						case 'webconverter':
						case 'eventlogger':
							this.data = data;
							break;

						// stringArrayResult
						case 'pwd':
						case 'guid':
						case 'idn':
						case 'randomkey':
							this.data = new stringArrayResult(data);
							break;

						// stringResult
						default:
							this.data = new stringResult(data.value);
							break;
					}
				}

				if (data.messages !== undefined && data.messages !== null) {
					// добавляем сообщения, если есть
					for (var i = 0; i <= data.messages.length - 1; i++) {
						this.data.addMessage(data.messages[i].type, data.messages[i].text);
					}
				}

				if (data.trace !== undefined && data.trace !== null) {
					// добавляем детальную информацию о выполнении запроса, если есть
					for (var i = 0; i <= data.trace.length - 1; i++) {
						this.data.addTrace(data.trace[i].message, data.trace[i].elapsedTime);
					}
				}
			}

			/** Возвращает true, если текущий результат содержит информацию об ошибках. */
			public get isError(): boolean {
				return this.data.type === 'errorResult';
			}

			/** Возвращает строковое значение результата выполнения запроса к API. */
			public toString() {
				return this.data.toString();
			}

		}

		/** Представляет информацию об ошибке при выполнении запроса к API. */
		export class errorResult extends baseResult {

			public items: Array<errorMessage> = new Array<errorMessage>();

			constructor(errors: any) {
				super('errorResult');

				if (errors !== undefined && errors !== null) {
					for (var i = 0; i <= errors.length - 1; i++) {
						//errors[i].charAt(0).toUpperCase() + errors[i].code.slice(1)
						this.add(<any>errorCode[errors[i].code], errors[i].text);
					}
				}
			}

			/**
			 * Добавляет сообщение об ошибке в коллекцию.
			 * @param code Код ошибки.
			 * @param text Текст сообщения.
			 */
			public add(code: errorCode, text: string): void {
				this.items.push(new errorMessage(code, text));
			}

			/** Возвращает текст всех ошибок, находящихся в текущем экземпляре объекта. */
			public toString() {
				return this.getText();
			}

			public getText(): string {
				var errorMessage = '';
				for (var i = 0; i <= this.items.length - 1; i++) {
					if (errorMessage.length > 0) {
						errorMessage += '\n';
					}
					errorMessage += this.items[i].text;
				}
				return errorMessage;
			}

		}

		/** Представляет сообщение об ошибке. */
		export class errorMessage {

			/** Код ошибки.*/
			public code: errorCode;

			/** Текст сообщения.*/
			public text: string;

			/**
			 * Создает новый экземпляр класса errorMessage.
			 * @param code Код ошибки.
			 * @param text Текст сообщения.
			 */
			constructor(code: errorCode, text: string) {
				this.code = code;
				this.text = text;
			}

		}

		/** Представляет системное сообщение. */
		export class messageItem {

			/** Тип сообщения. */
			public type: string;

			/** Текст сообщения. */
			public text: string;

			/**
			 * Создает новый экземпляр класса messageItem.
			 * @param type Тип сообщения.
			 * @param text Текст сообщения.
			 */
			constructor(type: string, text: string) {
				this.type = type;
				this.text = text;
			}

		}

		/** Представляет элемент детальной информации о выполнении запроса. */
		export class traceItem {

			/** Текст сообщения. */
			public message: string;

			/** Затраченное время. */
			public elapsedTime: string;

			constructor(message: string, elapsedTime: string) {
				this.message = message;
				this.elapsedTime = elapsedTime;
			}

			/** Возвращает строковое представление текущего экземпляра объекта. */
			public toString() {
				return this.elapsedTime + '> ' + this.message;
			}

		}

		/** Представляет строковой результат выполнения запроса к API. */
		export class stringResult extends baseResult {

			/** Содержит строковое значение результата выполнения запроса к API. */
			public value: string;

			/**
			 * Создает новый экземпляр класса stringResult.
			 * @param value Строковое значение результата выполнения запроса к API.
			 */
			constructor(value: string) {
				super('stringResult');
				this.value = value;
			}

			/** Возвращает строковое значение результата выполнения запроса к API. */
			public toString() {
				return this.value;
			}

		}

		/** Представляет строковой массив. */
		export class stringArrayResult extends baseResult {

			/** Элементы коллекции. */
			public items: Array<string> = new Array<string>();

			/** Номер текущей страницы. */
			public pageNumber: number = 0;

			/** Общее количество страниц. */
			public pageCount: number = 0;

			/**
			 * Создает новый экземпляр класса stringArrayResult.
			 * @param data Результат выполнения запроса к API.
			 */
			constructor(data: any) {
				super('stringArrayResult');
				this.items = data.items;
				this.pageNumber = data.pageNumber;
				this.pageCount = data.pageCount;
			}

			/** Возвращает строковое значение результата выполнения запроса к API. */
			public toString() {
				return this.items.join('\n');
			}

		}

		/** Представляет результат расчета хеш-суммы. */
		export class hashResult extends baseResult {

			/** Список полученных хеш-сумм. */
			public items: Array<hashItem> = new Array<hashItem>();

			constructor(items: any) {
				super('hashResult');

				if (items !== undefined && items !== null) {
					for (var i = 0; i <= items.length - 1; i++) {
						this.add(items[i].type, items[i].value);
					}
				}
			}

			/**
			 * Добавляет элемент в коллекцию.
			 * @param type Алгоритм.
			 * @param value Хеш-сумма.
			 */
			public add(type: string, value: string): void {
				this.items.push(new hashItem(type, value));
			}

		}

		/** Элемент результата расчета хеш-суммы. */
		export class hashItem {

			/** Алгоритм. */
			public type: string;

			/** Хеш-сумма. */
			public value: string;

			constructor(type: string, value: string) {
				this.type = type;
				this.value = value;
			}

		}

		/** Представляет результат выполнения запроса информации об IP. */
		export class ipResult extends baseResult {

			/** Адрес IP, о котором получена информация. */
			public ip: string;

			/** Индикатор указывает, является адрес IP прокси-сервером или нет. */
			public proxy: boolean = false;

			/** Информация о стране, которой принадлежит адрес. */
			public country: countryResult = null;

			/** Информация о городе, которому принадлежит адрес. */
			public city: cityResult = null;

			constructor(data: any) {
				super('ipResult');
				this.ip = data.ip;
				this.proxy = data.proxy;
				this.country = data.country || null;
				this.city = data.city || null;
			}

		}

		/** Представляет информацию о стране. */
		export class countryResult extends baseResult {

			public nameEn: string = null;
			public nameRu: string = null;
			public domain: string = null;
			public domainNational: string = null;
			public fips10: string = null;
			public id: string = null;
			public ioc: string = null;
			public iso3166a2: string = null;
			public iso3166a3: string = null;
			public iso3166num: string = null;
			public itu: string = null;
			public phoneCode: string = null;
			public stanag: string = null;
			public un: string = null;
			public unIso: string = null;

		}

		/** Представляет информацию о городе. */
		export class cityResult extends baseResult {

			public nameEn: string = null;
			public nameRu: string = null;
			public zip: string = null;
			public metroCode: string = null;
			public areaCode: string = null;
			public longitude: number = null;
			public latitude: number = null;

		}

		/** Представляет результат обращения за списком прокси-серверов. */
		export class proxyResult extends baseResult {

			/** Номер текущей страницы. */
			public pageNumber: number = 0;

			/** Общее количество страниц. */
			public pageCount: number = 0;

			/** Список полученных прокси-серверов. */
			public items: Array<proxyItem> = new Array<proxyItem>();

			constructor(data: any) {
				super('proxyResult');

				this.pageNumber = data.pageNumber;
				this.pageCount = data.pageCount;

				if (data.items !== undefined && data.items !== null) {
					for (var i = 0; i <= data.items.length - 1; i++) {
						this.add(data.items[i]);
					}
				}
			}

			public add(proxy: any): void {
				this.items.push(proxy);
			}

		}

		/** Представляет прокси-сервер, полученный из хитрого хранилища. */
		export class proxyItem {

			/** Адрес прокси-сервера. */
			public ip: string = '';

			/** Номер порта, по которому доступен прокси-сервер. */
			public port: number;

			/** Тип прокси-сервера. */
			public type: number;

			/** Уровень анонимности, который предоставляет прокси-сервер. */
			public anonymity: string;

			/** Время ответа (в секундах) на последний тестовый запрос. */
			public uptime: number;

			/** Дата и время последней проверки доступности прокси-сервера. */
			public checked: boolean;

			/** Статус доступности прокси-сервера. */
			public available: yesNoAny = yesNoAny.Any;

			/** Бесплатность прокси-сервера. */
			public free: yesNoAny = yesNoAny.Any;

			/** Информация о стране, в которой расположен прокси-сервер. */
			public country: countryResult = null;

		}

		/** Представляет хитрый маркер доступа. */
		export class foxtoolsToken {

			/** Маркер доступа. */
			public value: string = null;

			/** Адрес, который может вернуть новый маркер доступа (адрес на вашем сервере). */
			public url: string = null;

			/**
			 * Число ошибок, которые возникли при получении маркера доступа.
			 * Технический параметр, меняется автоматически.
			 */
			public errors: number = 0;

			/**
			 * Разбирает хитрый маркер доступа.
			 * @param token Маркер доступа, который следует разобрать. Это может быть строка с маркером, либо url, либо объект.
			 */
			static parse(token: any): foxtoolsToken {
				if (token === undefined || token === null) {
					return null;
				}

				var result = new foxtoolsToken();

				// смотрим, чем является token
				if (typeof token == 'string') {
					// указана строка, проверяем, является значение маркером доступа или адресом
					if (/^[0-9A-Fa-f]{32}$/.test(token)) {
						// это маркер доступа
						result.value = token;
					} else {
						// считаем, что указан адрес для получения маркера доступа
						result.url = token;
					}
				}
				else if (typeof token == 'object') {
					// указан объект, смотрим что именно
					if (token.value !== undefined && token.value !== null && token != '') {
						// есть маркер доступа
						result.value = token.value;
					}
					if (token.url !== undefined && token.url !== null && token.url != '') {
						// указан адрес для получения маркера доступа
						result.url = token.url;
					}
					if (token.errors !== undefined && token.errors !== null) {
						// есть счетчик ошибок
						result.errors = token.errors;
					}
					if (result.value === null && result.url === null) {
						// ничего не указано
						throw new Error('Token not found.');
					}
				} else {
					// что-то другое
					throw new Error('Token value not supported.');
				}

				return result;
			}

		}

		/** Хитрый класс работы с Яндекс.Метрика. */
		class ya {

			/** Хитрый счетчик Яндекс.Метрика. */
			static counter: any;

			/**
			 * Отправляет хит в Яндекс.Метрику.
			 * @param url Адрес хитовой страницы.
			 * @param title Заголовок хитовой страницы.
			 */
			static hit(url: string, title?: string): void {
				try {
					//utility.initYa();
					if (ya.counter !== undefined && ya.counter !== null) {
						ya.counter.hit(url, title, null);
					}
				}
				catch (ex) { }
			}

			/**
			 * Инициализирует Яндекс.Метрику.
			 */
			static init(): void {
				try {

					// 24654338 - id хитрого счетчика (не менять! статистика нужна, статистика важна)

					if (window['Ya'] === undefined || window['Ya'] === null) {
						//$('<script src="//mc.yandex.ru/metrika/watch.js" type="text/javascript"></script>').appendTo('body');
						$.getScript('//mc.yandex.ru/metrika/watch.js', function () {
							ya.counter = new window['Ya'].Metrika({ id: 24654338, enableAll: true });
						});
					} else {
						if (ya.counter === undefined || ya.counter === null) {
							ya.counter = new window['Ya'].Metrika({ id: 24654338, enableAll: true });
						}
					}

				} catch (ex) { }
			}

		}

		/** Перечень режимов обработки данных. */
		export enum dataMode { auto = 0, encode = 1, decode = 2 }

		/** Перечень режимов обработки данных. */
		export enum outputFormat { text = 0, file = 1 }

		/** Перечень ошибок, которые может вернуть API. */
		export enum errorCode {
			None = 0,
			/** Внутренняя ошибка сервера. */
			InternalError = 1000,
			/** Не поддерживается. */
			NotSupported = 1001,
			/** Не реализовано. */
			NotImplemented = 1002,
			/** Невозможно обработать указанный тип данных. */
			UnableToProcessData = 1003,
			/** Вышло время ожидания завершения операции. */
			Timeout = 1004,
			/** Доступ запрещен. */
			AccessDenied = 1100,
			/** Общая ошибка маркера доступа. */
			AccessTokenError = 1110,
			/** Маркер доступа не найден. */
			AccessTokenNotFound = 1111,
			/** Нет разрешения на использования этого маркера доступа. */
			NoPermissionToUseTokenForCurrentIp = 1112,
			/** Адрес находится в черном списке. */
			AddressIsBlacklisted = 1121,
			/** Значение не может быть пустым. */
			ArgumentNullOrEmpty = 2001,
			/** Значение не попадает в поддерживаемый диапазон. */
			ArgumentOutOfRange = 2002,
			/** Неверный формат данных. */
			InvalidFormat = 3001,
			/** Неверный тип файла. */
			InvalidFileType = 3002,
			/** Неверный тип содержимого. */
			InvalidContentType = 3003,
			/** Преобразование невозможно. */
			InvalidCast = 3004,
			/** Неверное значение. */
			InvalidValue = 3005,
			/** Данные не найдены. */
			DataNotFound = 3101,
			/** Тип содержимого не найден. */
			ContentTypeNotFound = 3102,
			/** Внешняя служба не доступна или не отвечает. */
			ExternalServiceIsNotAvailable = 5000,
			/** Внешняя служба вернула ошибку. */
			ExternalServiceError = 5001,
		};

		/** Да, нет или всё, что угодно. */
		export enum yesNoAny {
			Any = 0,
			Yes = 1,
			No = 2
		}

	}

	/** Вспомогательные полезняшки. */
	export class utility {

		/**
		 * Форматирует указанную строку.
		 * @param value Строка, в которую следует вставить параметры.
		 * @param args Пермеаметры, которые следует вставить в строку.
		 */
		static format(value: string, args: any): string {
			if (value === undefined || value === null || value === '') {
				console.log('Value is empty.');
				return null;
			}

			if (args.length === 1 && args[0] !== null && typeof args[0] === 'object') {
				args = args[0];
			}

			if (typeof args === 'string') {
				args = [args];
			}

			return value.replace(/{([^}]*)}/g, function (match, key) {
				return (typeof args[key] !== "undefined" ? args[key] : match);
			});
		}

		/** Делает куку. */
		static createCookies(name: string, value: string, days?: number) {
			var expires = '';
			var date = new Date();

			if (days !== undefined && days !== null) {
				date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
				expires = '; expires=' + date.toUTCString();
			}

			document.cookie = this.format('{name}={value}{expires}; path=/', { name: name, value: value, expires: expires });
		}

		/** Зачитывает куку вслух, с выражением, стоя на одной ноге на табуретке на кухне, в шубе и шапке ушанке, летом, ночью, в полнолуние. */
		static readCookies(name: string): string {
			var ca = document.cookie.split(';');

			name = name + '=';

			for (var i = 0; i < ca.length; i++) {
				var c = ca[i];
				while (c.charAt(0) == ' ') c = c.substring(1, c.length);
				if (c.indexOf(name) == 0) return c.substring(name.length, c.length);
			}

			return null;
		}

		/** Стирает куку. */
		static eraseCookies(name: string) {
			utility.createCookies(name, '', -1);
		}

		/** Проверяет, является указанное значение элементом HTML или нет. */
		static isElement(element: any): boolean {
			if (element === undefined || element === null) {
				return false;
			}
			return (element instanceof HTMLElement);
		}

		/** Проверяет, является указанное значение объектом jQuery или нет. */
		static isJqueryObject(element: any): boolean {
			if (element === undefined || element === null) {
				return false;
			}
			return (element instanceof $);
		}

		/** Возвращает true, если браузер поддерживает работу с локальным хранилищем. */
		static clinetIsSupportLocalStorage(): boolean {
			try {
				return 'localStorage' in window && window['localStorage'] !== null;
			} catch (ex) {
				return false;
			}
		}

		/*
		 * Заменяет значение одной подстроки на другую.
		 *
		 * @param str Строка, в которой следует произвести поиск и замену.
		 * @param find Искомая подстрока.
		 * @param repl Подстрока, на которую следует заменить найденные совпадения.
		 */
		static replace(str, find, repl): string {
			return str.replace(new RegExp("(" + find + ")+", "g"), repl);
		}

		/**
		 * Кодирует сущености HTML.
		 *
		 * @param value Строка, в которой следует закодировать сущности HTML.
		 */
		static htmlEncode(value: string): string {
			var r = new RegExp("\x22+", "g");
			var div = document.createElement("div");
			var text = document.createTextNode(value);
			div.appendChild(text);
			return div.innerHTML.replace(r, """);
		}

		/**
		 * Декодирует сущености HTML.
		 *
		 * @param value Строка, в которой следует декодировать сущности HTML.
		 */
		static htmlDecode(value: string): string {
			var div = document.createElement("div");
			div.innerHTML = value;
			return div.innerText;
		}

		/**
		 * Получае значение указанного параметра из строки запроса.
		 *
		 * @param name Имя параметра, значение которого следует получить.
		 * @param queryString Строка параметров запроса, из которой следует извлечь указанный параметр. По умолчанию используйте текущая строка (location.search).
		 */
		static getQueryStringParameterByName(name: string, queryString?: string): string {
			if (queryString === undefined || queryString === null) {
				queryString = location.search;
			}
			name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
			var regex = new RegExp('[\\?&]' + name + '=([^&#]*)'), results = regex.exec(queryString);
			return results == null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
		}

		/**
		 * Проверяет, является указанная строка URL или нет.
		 */
		static isUrl(value: string): boolean {
			return new RegExp('^(http|https):\/\/([^\x20\r\n]*)$', 'i').test(value);
		}

	}

	/**
	 * Вспомогательный метод для создания хитрой формы.
	 *
	 * @param selector Имя элемента или объект JQuery.
	 * @param options Дополнительные параметры.
	 */
	export function apiForm(selector: string|HTMLFormElement|JQuery, options?: foxtools.lib.requestProcess): void {
		foxtools.lib.client.foxyForm(selector, options);
	}

	/**
	 * Вспомогательный для создания хитрого запроса.
	 *
	 * @param methodName Название метода, который необходимо выполнить.
	 */
	export function apiRequest(methodName: string): foxtools.lib.request {
		return new foxtools.lib.request(methodName);
	}

}

// альтернатива $(document).ready
if (document.addEventListener) {
	document.addEventListener('DOMContentLoaded', foxtools.lib.client.ready);
}
else if (document.attachEvent) {
	window.attachEvent("onload", foxtools.lib.client.ready);
}


text:
<?php

namespace ProAI\Handlebars;

use LightnCandy\LightnCandy;
use Orchestra\Testbench\TestCase;

class FunctionalTest extends TestCase
{
    public function setUp(): void
    {
        parent::setUp();
    }

    protected function getPackageProviders($app)
    {
        return ['ProAI\Handlebars\HandlebarsServiceProvider'];
    }

    protected function getEnvironmentSetUp($app)
    {
        $app['config']->set('view.paths', [__DIR__]);
        $app['config']->set('handlebars.flags', LightnCandy::FLAG_HANDLEBARSJS_FULL);
    }

    public function testHandlebars()
    {
        $actual = \View::make('views.handlebars', [
            'title' => 'All about
 Tags',
            'body' => '
This is a post about <p> tags
',
            'author' => [
                'firstName' => 'Manuel',
                'lastName' => 'Wieser'
            ],
            'people' => [
                'Taylor Otwell',
                'Jeffrey Way'
            ],
            'comments' => [
                [
                    'author' => [
                        'firstName' => 'Markus',
                        'lastName' => 'Wetzel'
                    ],
                    'body' => 'I ❤ Handlebars!'
                ]
            ],
        ]);
        $expected = file_get_contents(__DIR__ . '/expected/handlebars.html', FILE_TEXT);
        $this->assertEquals((string)$actual, $expected);
    }

    public function testBlade()
    {
        $actual = \View::make('views.blade');
        $expected = file_get_contents(__DIR__ . '/expected/blade.html', FILE_TEXT);
        $this->assertEquals((string)$actual, $expected);
    }

//    public function testLanguageHelpers()
//    {
//        $actual = \View::make('views.languageHelpers');
//        $expected = file_get_contents(__DIR__ . '/expected/languageHelpers.html', FILE_TEXT);
//        $this->assertEquals((string)$actual, $expected);
//    }

//    public function testOptionalRawOutput()
//    {
//        $actual = \View::make('views.optionalRawOutput');
//        $expected = file_get_contents(__DIR__ . '/expected/optionalRawOutput.html', FILE_TEXT);
//        $this->assertEquals((string)$actual, $expected);
//    }

//    public function testTranslatedRawOutput()
//    {
//        $actual = \View::make('views.translatedRawOutput');
//        $expected = file_get_contents(__DIR__ . '/expected/translatedRawOutput.html', FILE_TEXT);
//        $this->assertEquals((string)$actual, $expected);
//    }
}

redpajama_set_name: RedPajamaGithub


text:
package guru.nidi.ramltester;

import guru.nidi.ramltester.core.SimpleReport;
import guru.nidi.ramltester.core.Usage;
import org.junit.Test;

import java.util.*;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;

/**
 *
 */
public class SimpleReportAggregatorTest {
    @Test
    public void simple() {
        final SimpleReportAggregator aggregator = new SimpleReportAggregator();
        aggregator.addReport(SimpleReport.report("simple.raml", "/data", "/d", "/"));
        final Iterator<Map.Entry<String, Usage>> usages = aggregator.usages().iterator();
        final Map.Entry<String, Usage> usageEntry = usages.next();
        assertEquals("simple", usageEntry.getKey());
        assertEquals(new HashSet<>(Arrays.asList("/mediaType", "/schema")),
                usageEntry.getValue().getUnusedResources());
        assertFalse(usages.hasNext());
    }

    @Test
    public void clear() {
        final SimpleReportAggregator aggregator = new SimpleReportAggregator();
        aggregator.addReport(SimpleReport.report("simple.raml", "/data", "/d", "/"));
        aggregator.clear();
        assertFalse(aggregator.usages().iterator().hasNext());
    }

    @Test(expected = IllegalArgumentException.class)
    public void multiRamlDisallowed() {
        final SimpleReportAggregator aggregator = new SimpleReportAggregator();
        aggregator.addReport(SimpleReport.report("simple.raml"));
        aggregator.addReport(SimpleReport.report("header.raml"));
    }
}


text:
const React = require('react-native');

const { StyleSheet,Dimensions } = React;
const deviceWidth = Dimensions.get('window').width;

export default {
  container: {
    display: 'flex',
    flexDirection: 'row',
    flexWrap: 'wrap',
    flex: 1
  },
  bookItem: {
    width: deviceWidth / 3,
    overflow: 'hidden',
  },
  row: {
    display: 'flex',
    flexDirection: 'column',
    margin: 15,
    alignItems: 'center',
  },
  thumb: {
    height:70,
    borderWidth: 1,
    borderColor: '#ccc'
  },
  text: {
    fontSize: 14,
    marginTop: 3,
    textAlign: 'center'
  },
};


text:
import {NgModule} from '@angular/core';
import {AppComponent} from './app/app-component';
import {MatButtonToggleModule, MatDatepickerModule} from '@angular/material'
import {MyAppMaterialModule} from './material.module';

@NgModule({
  bootstrap: [
    AppComponent
  ],
  imports: [
    MyAppMaterialModule,
    MatButtonToggleModule,
    MatDatepickerModule
  ]
})
export class MdSampleProjectModule {}


text:
package ru.skuznetsov.calculateAreaOfTriangle;
/**
* Some comment.
*/
class Triangle {
	/**
	* Point a.
	*/
    private Point a;
	/**
	* Point b.
	*/
    private Point b;
	/**
	* Point c.
	*/
    private Point c;
	/**
	* Constructor with 3 points.
	* @param a - point a
	* @param b - point b
	* @param c - point c
	*/
    Triangle(Point a, Point b, Point c) {
        this.a = a;
        this.b = b;
        this.c = c;
    }
/**
* Method calculates area of triangle.
* @return calculated area
*/
    double area() {
		if (canBeBuild()) {
			double area = Math.abs((a.getX() * (b.getY() - c.getY()) + b.getX() * (c.getY() - a.getY()) + c.getX() * (a.getY() - b.getY())) / 2.0);
			return area;
		}

		return 0.0;
    }
/**
* Method check can be build triangle or no.
* @return boolean value is can be build triangle.
*/
    boolean canBeBuild() {
		if (this.a == null || this.b == null || this.c == null) {
			return false;
		}
        double distanceAToB = a.distanceTo(b);
        double distanceAToC = a.distanceTo(c);
        double distanceBToC = b.distanceTo(c);

        if (distanceAToB > distanceAToC && distanceAToB > distanceBToC) {
			return distanceAToB < (distanceAToC + distanceBToC);
		} else if (distanceAToC > distanceAToB && distanceAToC > distanceBToC) {
			return distanceAToC < (distanceAToB + distanceBToC);
		} else {
			return distanceBToC < (distanceAToC + distanceAToB);
		}
    }
}


text: 4.2 Etsy Iteration Exercise


text:
from setuptools import setup

setup(
    name='WikiChatter',
    version='0.3.0',
    description='Parser for MediaWiki talk pages',
    url='https://github.com/kjschiroo/WikiChatter',

    author='Kevin Schiroo',
    author_email='kjschiroo@gmail.com',
    license='MIT',

    packages=['wikichatter'],
    install_requires=['mwparserfromhell']
)


text:
from django.contrib.auth.models import Permission
from django.urls import reverse
from django.utils.http import urlencode
from django.utils.translation import gettext_lazy as _
from django.utils.translation import gettext
from draftjs_exporter.dom import DOM

import wagtail.admin.rich_text.editors.draftail.features as draftail_features
from wagtail.admin.auth import user_has_any_page_permission
from wagtail.admin.localization import get_available_admin_languages, get_available_admin_time_zones
from wagtail.admin.menu import MenuItem, SubmenuMenuItem, reports_menu, settings_menu
from wagtail.admin.navigation import get_explorable_root_page
from wagtail.admin.rich_text import (
    HalloFormatPlugin, HalloHeadingPlugin, HalloListPlugin, HalloPlugin)
from wagtail.admin.rich_text.converters.contentstate import link_entity
from wagtail.admin.rich_text.converters.editor_html import (
    LinkTypeRule, PageLinkHandler, WhitelistRule)
from wagtail.admin.rich_text.converters.html_to_contentstate import (
    BlockElementHandler, ExternalLinkElementHandler, HorizontalRuleHandler,
    InlineStyleElementHandler, ListElementHandler, ListItemElementHandler, PageLinkElementHandler)
from wagtail.admin.search import SearchArea
from wagtail.admin.site_summary import PagesSummaryItem
from wagtail.admin.views.account import email_management_enabled, password_management_enabled
from wagtail.admin.viewsets import viewsets
from wagtail.admin.widgets import Button, ButtonWithDropdownFromHook, PageListingButton
from wagtail.core import hooks
from wagtail.core.models import UserPagePermissionsProxy
from wagtail.core.permissions import (
    collection_permission_policy, task_permission_policy, workflow_permission_policy)
from wagtail.core.whitelist import allow_without_attributes, attribute_rule, check_url

class ExplorerMenuItem(MenuItem):
    template = 'wagtailadmin/shared/explorer_menu_item.html'

    def is_shown(self, request):
        return user_has_any_page_permission(request.user)

    def get_context(self, request):
        context = super().get_context(request)
        start_page = get_explorable_root_page(request.user)

        if start_page:
            context['start_page_id'] = start_page.id

        return context

@hooks.register('register_admin_menu_item')
def register_explorer_menu_item():
    return ExplorerMenuItem(
        _('Pages'), reverse('wagtailadmin_explore_root'),
        name='explorer',
        icon_name='folder-open-inverse',
        order=100)

class SettingsMenuItem(SubmenuMenuItem):
    template = 'wagtailadmin/shared/menu_settings_menu_item.html'

@hooks.register('register_admin_menu_item')
def register_settings_menu():
    return SettingsMenuItem(
        _('Settings'),
        settings_menu,
        icon_name='cogs',
        order=10000)

@hooks.register('register_permissions')
def register_permissions():
    return Permission.objects.filter(content_type__app_label='wagtailadmin', codename='access_admin')

class PageSearchArea(SearchArea):
    def __init__(self):
        super().__init__(
            _('Pages'), reverse('wagtailadmin_pages:search'),
            name='pages',
            classnames='icon icon-folder-open-inverse',
            order=100)

    def is_shown(self, request):
        return user_has_any_page_permission(request.user)

@hooks.register('register_admin_search_area')
def register_pages_search_area():
    return PageSearchArea()

class CollectionsMenuItem(MenuItem):
    def is_shown(self, request):
        return collection_permission_policy.user_has_any_permission(
            request.user, ['add', 'change', 'delete']
        )

@hooks.register('register_settings_menu_item')
def register_collections_menu_item():
    return CollectionsMenuItem(_('Collections'), reverse('wagtailadmin_collections:index'), icon_name='folder-open-1', order=700)

class WorkflowsMenuItem(MenuItem):
    def is_shown(self, request):
        return workflow_permission_policy.user_has_any_permission(
            request.user, ['add', 'change', 'delete']
        )

class WorkflowTasksMenuItem(MenuItem):
    def is_shown(self, request):
        return task_permission_policy.user_has_any_permission(
            request.user, ['add', 'change', 'delete']
        )

@hooks.register('register_settings_menu_item')
def register_workflows_menu_item():
    return WorkflowsMenuItem(_('Workflows'), reverse('wagtailadmin_workflows:index'), icon_name='tasks', order=100)

@hooks.register('register_settings_menu_item')
def register_workflow_tasks_menu_item():
    return WorkflowTasksMenuItem(_('Workflow tasks'), reverse('wagtailadmin_workflows:task_index'), icon_name='thumbtack', order=150)

@hooks.register('register_page_listing_buttons')
def page_listing_buttons(page, page_perms, is_parent=False, next_url=None):
    if page_perms.can_edit():
        yield PageListingButton(
            _('Edit'),
            reverse('wagtailadmin_pages:edit', args=[page.id]),
            attrs={'aria-label': _("Edit '%(title)s'") % {'title': page.get_admin_display_title()}},
            priority=10
        )
    if page.has_unpublished_changes and page.is_previewable():
        yield PageListingButton(
            _('View draft'),
            reverse('wagtailadmin_pages:view_draft', args=[page.id]),
            attrs={
                'aria-label': _("Preview draft version of '%(title)s'") % {'title': page.get_admin_display_title()},
                'target': '_blank', 'rel': 'noopener noreferrer'
            },
            priority=20
        )
    if page.live and page.url:
        yield PageListingButton(
            _('View live'),
            page.url,
            attrs={
                'target': "_blank", 'rel': 'noopener noreferrer',
                'aria-label': _("View live version of '%(title)s'") % {'title': page.get_admin_display_title()},
            },
            priority=30
        )
    if page_perms.can_add_subpage():
        if is_parent:
            yield Button(
                _('Add child page'),
                reverse('wagtailadmin_pages:add_subpage', args=[page.id]),
                attrs={
                    'aria-label': _("Add a child page to '%(title)s' ") % {'title': page.get_admin_display_title()},
                },
                classes={'button', 'button-small', 'bicolor', 'icon', 'white', 'icon-plus'},
                priority=40
            )
        else:
            yield PageListingButton(
                _('Add child page'),
                reverse('wagtailadmin_pages:add_subpage', args=[page.id]),
                attrs={'aria-label': _("Add a child page to '%(title)s' ") % {'title': page.get_admin_display_title()}},
                priority=40
            )

    yield ButtonWithDropdownFromHook(
        _('More'),
        hook_name='register_page_listing_more_buttons',
        page=page,
        page_perms=page_perms,
        is_parent=is_parent,
        next_url=next_url,
        attrs={
            'target': '_blank', 'rel': 'noopener noreferrer',
            'title': _("View more options for '%(title)s'") % {'title': page.get_admin_display_title()}
        },
        priority=50
    )

@hooks.register('register_page_listing_more_buttons')
def page_listing_more_buttons(page, page_perms, is_parent=False, next_url=None):
    if page_perms.can_move():
        yield Button(
            _('Move'),
            reverse('wagtailadmin_pages:move', args=[page.id]),
            attrs={"title": _("Move page '%(title)s'") % {'title': page.get_admin_display_title()}},
            priority=10
        )
    if page_perms.can_copy():
        url = reverse('wagtailadmin_pages:copy', args=[page.id])
        if next_url:
            url += '?' + urlencode({'next': next_url})

        urlencode
        yield Button(
            _('Copy'),
            url,
            attrs={'title': _("Copy page '%(title)s'") % {'title': page.get_admin_display_title()}},
            priority=20
        )
    if page_perms.can_delete():
        url = reverse('wagtailadmin_pages:delete', args=[page.id])
        if next_url:
            url += '?' + urlencode({'next': next_url})

        yield Button(
            _('Delete'),
            url,
            attrs={'title': _("Delete page '%(title)s'") % {'title': page.get_admin_display_title()}},
            priority=30
        )
    if page_perms.can_unpublish():
        url = reverse('wagtailadmin_pages:unpublish', args=[page.id])
        if next_url:
            url += '?' + urlencode({'next': next_url})

        yield Button(
            _('Unpublish'),
            url,
            attrs={'title': _("Unpublish page '%(title)s'") % {'title': page.get_admin_display_title()}},
            priority=40
        )

    if page_perms.can_view_revisions():
        yield Button(
            _('History'),
            reverse('wagtailadmin_pages:history', args=[page.id]),
            attrs={'title': _("View page history for '%(title)s'") % {'title': page.get_admin_display_title()}},
            priority=50
        )

@hooks.register('register_admin_urls')
def register_viewsets_urls():
    viewsets.populate()
    return viewsets.get_urlpatterns()

@hooks.register('register_account_menu_item')
def register_account_set_profile_picture(request):
    return {
        'url': reverse('wagtailadmin_account_change_avatar'),
        'label': _('Set profile picture'),
        'help_text': _("Change your profile picture.")
    }

@hooks.register('register_account_menu_item')
def register_account_change_email(request):
    if email_management_enabled():
        return {
            'url': reverse('wagtailadmin_account_change_email'),
            'label': _('Change email'),
            'help_text': _('Change the email address linked to your account.'),
        }

@hooks.register('register_account_menu_item')
def register_account_change_password(request):
    if password_management_enabled() and request.user.has_usable_password():
        return {
            'url': reverse('wagtailadmin_account_change_password'),
            'label': _('Change password'),
            'help_text': _('Change the password you use to log in.'),
        }

@hooks.register('register_account_menu_item')
def register_account_notification_preferences(request):
    user_perms = UserPagePermissionsProxy(request.user)
    if user_perms.can_edit_pages() or user_perms.can_publish_pages():
        return {
            'url': reverse('wagtailadmin_account_notification_preferences'),
            'label': _('Notification preferences'),
            'help_text': _('Choose which email notifications to receive.'),
        }

@hooks.register('register_account_menu_item')
def register_account_preferred_language_preferences(request):
    if len(get_available_admin_languages()) > 1:
        return {
            'url': reverse('wagtailadmin_account_language_preferences'),
            'label': _('Language preferences'),
            'help_text': _('Choose the language you want to use here.'),
        }

@hooks.register('register_account_menu_item')
def register_account_current_time_zone(request):
    if len(get_available_admin_time_zones()) > 1:
        return {
            'url': reverse('wagtailadmin_account_current_time_zone'),
            'label': _('Current Time Zone'),
            'help_text': _('Choose your current time zone.'),
        }

@hooks.register('register_account_menu_item')
def register_account_change_name(request):
    return {
        'url': reverse('wagtailadmin_account_change_name'),
        'label': _('Change name'),
        'help_text': _('Change your first and last name on your account.'),
    }

@hooks.register('register_rich_text_features')
def register_core_features(features):
    # Hallo.js
    features.register_editor_plugin(
        'hallo', 'hr',
        HalloPlugin(
            name='hallohr',
            js=['wagtailadmin/js/hallo-plugins/hallo-hr.js'],
            order=45,
        )
    )
    features.register_converter_rule('editorhtml', 'hr', [
        WhitelistRule('hr', allow_without_attributes)
    ])

    features.register_editor_plugin(
        'hallo', 'link',
        HalloPlugin(
            name='hallowagtaillink',
            js=[
                'wagtailadmin/js/page-chooser-modal.js',
                'wagtailadmin/js/hallo-plugins/hallo-wagtaillink.js',
            ],
        )
    )
    features.register_converter_rule('editorhtml', 'link', [
        WhitelistRule('a', attribute_rule({'href': check_url})),
        LinkTypeRule('page', PageLinkHandler),
    ])

    features.register_editor_plugin(
        'hallo', 'bold', HalloFormatPlugin(format_name='bold')
    )
    features.register_converter_rule('editorhtml', 'bold', [
        WhitelistRule('b', allow_without_attributes),
        WhitelistRule('strong', allow_without_attributes),
    ])

    features.register_editor_plugin(
        'hallo', 'italic', HalloFormatPlugin(format_name='italic')
    )
    features.register_converter_rule('editorhtml', 'italic', [
        WhitelistRule('i', allow_without_attributes),
        WhitelistRule('em', allow_without_attributes),
    ])

    headings_elements = ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
    headings_order_start = HalloHeadingPlugin.default_order + 1
    for order, element in enumerate(headings_elements, start=headings_order_start):
        features.register_editor_plugin(
            'hallo', element, HalloHeadingPlugin(element=element, order=order)
        )
        features.register_converter_rule('editorhtml', element, [
            WhitelistRule(element, allow_without_attributes)
        ])

    features.register_editor_plugin(
        'hallo', 'ol', HalloListPlugin(list_type='ordered')
    )
    features.register_converter_rule('editorhtml', 'ol', [
        WhitelistRule('ol', allow_without_attributes),
        WhitelistRule('li', allow_without_attributes),
    ])

    features.register_editor_plugin(
        'hallo', 'ul', HalloListPlugin(list_type='unordered')
    )
    features.register_converter_rule('editorhtml', 'ul', [
        WhitelistRule('ul', allow_without_attributes),
        WhitelistRule('li', allow_without_attributes),
    ])

    # Draftail
    features.register_editor_plugin(
        'draftail', 'hr', draftail_features.BooleanFeature('enableHorizontalRule')
    )
    features.register_converter_rule('contentstate', 'hr', {
        'from_database_format': {
            'hr': HorizontalRuleHandler(),
        },
        'to_database_format': {
            'entity_decorators': {'HORIZONTAL_RULE': lambda props: DOM.create_element('hr')}
        }
    })

    features.register_editor_plugin(
        'draftail', 'h1', draftail_features.BlockFeature({
            'label': 'H1',
            'type': 'header-one',
            'description': gettext('Heading %(level)d') % {'level': 1},
        })
    )
    features.register_converter_rule('contentstate', 'h1', {
        'from_database_format': {
            'h1': BlockElementHandler('header-one'),
        },
        'to_database_format': {
            'block_map': {'header-one': 'h1'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'h2', draftail_features.BlockFeature({
            'label': 'H2',
            'type': 'header-two',
            'description': gettext('Heading %(level)d') % {'level': 2},
        })
    )
    features.register_converter_rule('contentstate', 'h2', {
        'from_database_format': {
            'h2': BlockElementHandler('header-two'),
        },
        'to_database_format': {
            'block_map': {'header-two': 'h2'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'h3', draftail_features.BlockFeature({
            'label': 'H3',
            'type': 'header-three',
            'description': gettext('Heading %(level)d') % {'level': 3},
        })
    )
    features.register_converter_rule('contentstate', 'h3', {
        'from_database_format': {
            'h3': BlockElementHandler('header-three'),
        },
        'to_database_format': {
            'block_map': {'header-three': 'h3'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'h4', draftail_features.BlockFeature({
            'label': 'H4',
            'type': 'header-four',
            'description': gettext('Heading %(level)d') % {'level': 4},
        })
    )
    features.register_converter_rule('contentstate', 'h4', {
        'from_database_format': {
            'h4': BlockElementHandler('header-four'),
        },
        'to_database_format': {
            'block_map': {'header-four': 'h4'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'h5', draftail_features.BlockFeature({
            'label': 'H5',
            'type': 'header-five',
            'description': gettext('Heading %(level)d') % {'level': 5},
        })
    )
    features.register_converter_rule('contentstate', 'h5', {
        'from_database_format': {
            'h5': BlockElementHandler('header-five'),
        },
        'to_database_format': {
            'block_map': {'header-five': 'h5'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'h6', draftail_features.BlockFeature({
            'label': 'H6',
            'type': 'header-six',
            'description': gettext('Heading %(level)d') % {'level': 6},
        })
    )
    features.register_converter_rule('contentstate', 'h6', {
        'from_database_format': {
            'h6': BlockElementHandler('header-six'),
        },
        'to_database_format': {
            'block_map': {'header-six': 'h6'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'ul', draftail_features.BlockFeature({
            'type': 'unordered-list-item',
            'icon': 'list-ul',
            'description': gettext('Bulleted list'),
        })
    )
    features.register_converter_rule('contentstate', 'ul', {
        'from_database_format': {
            'ul': ListElementHandler('unordered-list-item'),
            'li': ListItemElementHandler(),
        },
        'to_database_format': {
            'block_map': {'unordered-list-item': {'element': 'li', 'wrapper': 'ul'}}
        }
    })
    features.register_editor_plugin(
        'draftail', 'ol', draftail_features.BlockFeature({
            'type': 'ordered-list-item',
            'icon': 'list-ol',
            'description': gettext('Numbered list'),
        })
    )
    features.register_converter_rule('contentstate', 'ol', {
        'from_database_format': {
            'ol': ListElementHandler('ordered-list-item'),
            'li': ListItemElementHandler(),
        },
        'to_database_format': {
            'block_map': {'ordered-list-item': {'element': 'li', 'wrapper': 'ol'}}
        }
    })
    features.register_editor_plugin(
        'draftail', 'blockquote', draftail_features.BlockFeature({
            'type': 'blockquote',
            'icon': 'openquote',
            'description': gettext('Blockquote'),
        })
    )
    features.register_converter_rule('contentstate', 'blockquote', {
        'from_database_format': {
            'blockquote': BlockElementHandler('blockquote'),
        },
        'to_database_format': {
            'block_map': {'blockquote': 'blockquote'}
        }
    })

    features.register_editor_plugin(
        'draftail', 'bold', draftail_features.InlineStyleFeature({
            'type': 'BOLD',
            'icon': 'bold',
            'description': gettext('Bold'),
        })
    )
    features.register_converter_rule('contentstate', 'bold', {
        'from_database_format': {
            'b': InlineStyleElementHandler('BOLD'),
            'strong': InlineStyleElementHandler('BOLD'),
        },
        'to_database_format': {
            'style_map': {'BOLD': 'b'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'italic', draftail_features.InlineStyleFeature({
            'type': 'ITALIC',
            'icon': 'italic',
            'description': gettext('Italic'),
        })
    )
    features.register_converter_rule('contentstate', 'italic', {
        'from_database_format': {
            'i': InlineStyleElementHandler('ITALIC'),
            'em': InlineStyleElementHandler('ITALIC'),
        },
        'to_database_format': {
            'style_map': {'ITALIC': 'i'}
        }
    })

    features.register_editor_plugin(
        'draftail', 'link', draftail_features.EntityFeature({
            'type': 'LINK',
            'icon': 'link',
            'description': gettext('Link'),
            # We want to enforce constraints on which links can be pasted into rich text.
            # Keep only the attributes Wagtail needs.
            'attributes': ['url', 'id', 'parentId'],
            'whitelist': {
                # Keep pasted links with http/https protocol, and not-pasted links (href = undefined).
                'href': "^(http:|https:|undefined$)",
            }
        }, js=[
            'wagtailadmin/js/page-chooser-modal.js',
        ])
    )
    features.register_converter_rule('contentstate', 'link', {
        'from_database_format': {
            'a[href]': ExternalLinkElementHandler('LINK'),
            'a[linktype="page"]': PageLinkElementHandler('LINK'),
        },
        'to_database_format': {
            'entity_decorators': {'LINK': link_entity}
        }
    })
    features.register_editor_plugin(
        'draftail', 'superscript', draftail_features.InlineStyleFeature({
            'type': 'SUPERSCRIPT',
            'icon': 'superscript',
            'description': gettext('Superscript'),
        })
    )
    features.register_converter_rule('contentstate', 'superscript', {
        'from_database_format': {
            'sup': InlineStyleElementHandler('SUPERSCRIPT'),
        },
        'to_database_format': {
            'style_map': {'SUPERSCRIPT': 'sup'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'subscript', draftail_features.InlineStyleFeature({
            'type': 'SUBSCRIPT',
            'icon': 'subscript',
            'description': gettext('Subscript'),
        })
    )
    features.register_converter_rule('contentstate', 'subscript', {
        'from_database_format': {
            'sub': InlineStyleElementHandler('SUBSCRIPT'),
        },
        'to_database_format': {
            'style_map': {'SUBSCRIPT': 'sub'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'strikethrough', draftail_features.InlineStyleFeature({
            'type': 'STRIKETHROUGH',
            'icon': 'strikethrough',
            'description': gettext('Strikethrough'),
        })
    )
    features.register_converter_rule('contentstate', 'strikethrough', {
        'from_database_format': {
            's': InlineStyleElementHandler('STRIKETHROUGH'),
        },
        'to_database_format': {
            'style_map': {'STRIKETHROUGH': 's'}
        }
    })
    features.register_editor_plugin(
        'draftail', 'code', draftail_features.InlineStyleFeature({
            'type': 'CODE',
            'icon': 'code',
            'description': gettext('Code'),
        })
    )
    features.register_converter_rule('contentstate', 'code', {
        'from_database_format': {
            'code': InlineStyleElementHandler('CODE'),
        },
        'to_database_format': {
            'style_map': {'CODE': 'code'}
        }
    })

class ReportsMenuItem(SubmenuMenuItem):
    template = 'wagtailadmin/shared/menu_submenu_item.html'

class LockedPagesMenuItem(MenuItem):
    def is_shown(self, request):
        return UserPagePermissionsProxy(request.user).can_remove_locks()

class WorkflowReportMenuItem(MenuItem):
    def is_shown(self, request):
        return True

class SiteHistoryReportMenuItem(MenuItem):
    def is_shown(self, request):
        return UserPagePermissionsProxy(request.user).explorable_pages().exists()

@hooks.register('register_reports_menu_item')
def register_locked_pages_menu_item():
    return LockedPagesMenuItem(_('Locked Pages'), reverse('wagtailadmin_reports:locked_pages'), icon_name='lock', order=700)

@hooks.register('register_reports_menu_item')
def register_workflow_report_menu_item():
    return WorkflowReportMenuItem(_('Workflows'), reverse('wagtailadmin_reports:workflow'), icon_name='tasks', order=800)

@hooks.register('register_reports_menu_item')
def register_workflow_tasks_report_menu_item():
    return WorkflowReportMenuItem(_('Workflow tasks'), reverse('wagtailadmin_reports:workflow_tasks'), icon_name='thumbtack', order=900)

@hooks.register('register_reports_menu_item')
def register_site_history_report_menu_item():
    return SiteHistoryReportMenuItem(_('Site history'), reverse('wagtailadmin_reports:site_history'), icon_name='history', order=1000)

@hooks.register('register_admin_menu_item')
def register_reports_menu():
    return ReportsMenuItem(
        _('Reports'), reports_menu, icon_name='site', order=9000)

@hooks.register('register_icons')
def register_icons(icons):
    for icon in [
        'arrow-down-big.svg',
        'arrow-down.svg',
        'arrow-left.svg',
        'arrow-right.svg',
        'arrow-up-big.svg',
        'arrow-up.svg',
        'arrows-up-down.svg',
        'bin.svg',
        'bold.svg',
        'chain-broken.svg',
        'clipboard-list.svg',
        'code.svg',
        'cog.svg',
        'cogs.svg',
        'collapse-down.svg',
        'collapse-up.svg',
        'cross.svg',
        'date.svg',
        'doc-empty-inverse.svg',
        'doc-empty.svg',
        'doc-full-inverse.svg',
        'doc-full.svg',  # aka file-text-alt
        'download-alt.svg',
        'download.svg',
        'draft.svg',
        'duplicate.svg',
        'edit.svg',
        'error.svg',
        'folder-inverse.svg',
        'folder-open-1.svg',
        'folder-open-inverse.svg',
        'folder.svg',
        'form.svg',
        'grip.svg',
        'group.svg',
        'help.svg',
        'history.svg',
        'home.svg',
        'horizontalrule.svg',
        'image.svg',  # aka picture
        'italic.svg',
        'link.svg',
        'link-external.svg',
        'list-ol.svg',
        'list-ul.svg',
        'lock-open.svg',
        'lock.svg',
        'login.svg',
        'logout.svg',
        'mail.svg',
        'media.svg',
        'no-view.svg',
        'openquote.svg',
        'order-down.svg',
        'order-up.svg',
        'order.svg',
        'password.svg',
        'pick.svg',
        'pilcrow.svg',
        'placeholder.svg',  # aka marquee
        'plus-inverse.svg',
        'plus.svg',
        'radio-empty.svg',
        'radio-full.svg',
        'redirect.svg',
        'repeat.svg',
        'reset.svg',
        'resubmit.svg',
        'search.svg',
        'site.svg',
        'snippet.svg',
        'spinner.svg',
        'strikethrough.svg',
        'success.svg',
        'subscript.svg',
        'superscript.svg',
        'table.svg',
        'tag.svg',
        'tasks.svg',
        'thumbtack.svg',
        'tick-inverse.svg',
        'tick.svg',
        'time.svg',
        'title.svg',
        'undo.svg',
        'uni52.svg',  # Is this a redundant icon?
        'upload.svg',
        'user.svg',
        'view.svg',
        'wagtail-inverse.svg',
        'wagtail.svg',
        'warning.svg',
    ]:
        icons.append('wagtailadmin/icons/{}'.format(icon))
    return icons

@hooks.register('construct_homepage_summary_items')
def add_pages_summary_item(request, items):
    items.insert(0, PagesSummaryItem(request))

@hooks.register('register_log_actions')
def register_core_log_actions(actions):
    actions.register_action('wagtail.create', _('Create'), _('Created'))
    actions.register_action('wagtail.edit', _('Save draft'), _('Draft saved'))
    actions.register_action('wagtail.delete', _('Delete'), _('Deleted'))
    actions.register_action('wagtail.publish', _('Publish'), _('Published'))
    actions.register_action('wagtail.publish.scheduled', _("Publish scheduled draft"), _('Published scheduled draft'))
    actions.register_action('wagtail.unpublish', _('Unpublish'), _('Unpublished'))
    actions.register_action('wagtail.unpublish.scheduled', _('Unpublish scheduled draft'), _('Unpublished scheduled draft'))
    actions.register_action('wagtail.lock', _('Lock'), _('Locked'))
    actions.register_action('wagtail.unlock', _('Unlock'), _('Unlocked'))
    actions.register_action('wagtail.moderation.approve', _('Approve'), _('Approved'))
    actions.register_action('wagtail.moderation.reject', _('Reject'), _('Rejected'))

    def revert_message(data):
        try:
            return _('Reverted to previous revision with id %(revision_id)s from %(created_at)s') % {
                'revision_id': data['revision']['id'],
                'created_at': data['revision']['created'],
            }
        except KeyError:
            return _('Reverted to previous revision')

    def copy_message(data):
        try:
            return _('Copied from %(title)s') % {
                'title': data['source']['title'],
            }
        except KeyError:
            return _("Copied")

    def move_message(data):
        try:
            return _("Moved from '%(old_parent)s' to '%(new_parent)s'") % {
                'old_parent': data['source']['title'],
                'new_parent': data['destination']['title'],
            }
        except KeyError:
            return _('Moved')

    def schedule_publish_message(data):
        try:
            if data['revision']['has_live_version']:
                return _('Revision %(revision_id)s from %(created_at)s scheduled for publishing at %(go_live_at)s.') % {
                    'revision_id': data['revision']['id'],
                    'created_at': data['revision']['created'],
                    'go_live_at': data['revision']['go_live_at'],
                }
            else:
                return _('Page scheduled for publishing at %(go_live_at)s') % {
                    'go_live_at': data['revision']['go_live_at'],
                }
        except KeyError:
            return _('Page scheduled for publishing')

    def unschedule_publish_message(data):
        try:
            if data['revision']['has_live_version']:
                return _('Revision %(revision_id)s from %(created_at)s unscheduled from publishing at %(go_live_at)s.') % {
                    'revision_id': data['revision']['id'],
                    'created_at': data['revision']['created'],
                    'go_live_at': data['revision']['go_live_at'],
                }
            else:
                return _('Page unscheduled for publishing at %(go_live_at)s') % {
                    'go_live_at': data['revision']['go_live_at'],
                }
        except KeyError:
            return _('Page unscheduled from publishing')

    def add_view_restriction(data):
        try:
            return _("Added the '%(restriction)s' view restriction") % {
                'restriction': data['restriction']['title'],
            }
        except KeyError:
            return _('Added view restriction')

    def edit_view_restriction(data):
        try:
            return _("Updated the view restriction to '%(restriction)s'") % {
                'restriction': data['restriction']['title'],
            }
        except KeyError:
            return _('Updated view restriction')

    def delete_view_restriction(data):
        try:
            return _("Removed the '%(restriction)s' view restriction") % {
                'restriction': data['restriction']['title'],
            }
        except KeyError:
            return _('Removed view restriction')

    def rename_message(data):
        try:
            return _("Renamed from '%(old)s' to '%(new)s'") % {
                'old': data['title']['old'],
                'new': data['title']['new'],
            }
        except KeyError:
            return _('Renamed')

    actions.register_action('wagtail.rename', _('Rename'), rename_message)
    actions.register_action('wagtail.revert', _('Revert'), revert_message)
    actions.register_action('wagtail.copy', _('Copy'), copy_message)
    actions.register_action('wagtail.move', _('Move'), move_message)
    actions.register_action('wagtail.publish.schedule', _("Schedule publication"), schedule_publish_message)
    actions.register_action('wagtail.schedule.cancel', _("Unschedule publication"), unschedule_publish_message)
    actions.register_action('wagtail.view_restriction.create', _("Add view restrictions"), add_view_restriction)
    actions.register_action('wagtail.view_restriction.edit', _("Update view restrictions"), edit_view_restriction)
    actions.register_action('wagtail.view_restriction.delete', _("Remove view restrictions"), delete_view_restriction)

@hooks.register('register_log_actions')
def register_workflow_log_actions(actions):
    def workflow_start_message(data):
        try:
            return _("'%(workflow)s' started. Next step '%(task)s'") % {
                'workflow': data['workflow']['title'],
                'task': data['workflow']['next']['title'],
            }
        except (KeyError, TypeError):
            return _('Workflow started')

    def workflow_approve_message(data):
        try:
            if data['workflow']['next']:
                return _("Approved at '%(task)s'. Next step '%(next_task)s'") % {
                    'task': data['workflow']['task']['title'],
                    'next_task': data['workflow']['next']['title'],
                }
            else:
                return _("Approved at '%(task)s'. '%(workflow)s' complete") % {
                    'task': data['workflow']['task']['title'],
                    'workflow': data['workflow']['title'],
                }
        except (KeyError, TypeError):
            return _('Workflow task approved')

    def workflow_reject_message(data):
        try:
            return _("Rejected at '%(task)s'. Changes requested") % {
                'task': data['workflow']['task']['title'],
            }
        except (KeyError, TypeError):
            return _('Workflow task rejected. Workflow complete')

    def workflow_resume_message(data):
        try:
            return _("Resubmitted '%(task)s'. Workflow resumed'") % {
                'task': data['workflow']['task']['title'],
            }
        except (KeyError, TypeError):
            return _('Workflow task resubmitted. Workflow resumed')

    def workflow_cancel_message(data):
        try:
            return _("Cancelled '%(workflow)s' at '%(task)s'") % {
                'workflow': data['workflow']['title'],
                'task': data['workflow']['task']['title'],
            }
        except (KeyError, TypeError):
            return _('Workflow cancelled')

    actions.register_action('wagtail.workflow.start', _('Workflow: start'), workflow_start_message)
    actions.register_action('wagtail.workflow.approve', _('Workflow: approve task'), workflow_approve_message)
    actions.register_action('wagtail.workflow.reject', _('Workflow: reject task'), workflow_reject_message)
    actions.register_action('wagtail.workflow.resume', _('Workflow: resume task'), workflow_resume_message)
    actions.register_action('wagtail.workflow.cancel', _('Workflow: cancel'), workflow_cancel_message)


text:
/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package model;

import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

/**
 *
 * @author Alberto Lorente Leal <albll@kth.se>, <a.lorenteleal@gmail.com>
 */
@XmlRootElement
public class Profile {
    private Company company;
    private Cv cv;

    public Profile() {
    }

    public Profile(Company company, Cv cv) {
        this.company = company;
        this.cv = cv;
    }

    @XmlElement(name="CompanyInfo")
    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

    @XmlElement(name="Cv")
    public Cv getCv() {
        return cv;
    }

    public void setCv(Cv cv) {
        this.cv = cv;
    }

}


text:
using BusinessForms.FSControllers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
using BusinessForms;

namespace PalvelutoriModel.FSControllers
{
    public class OLD_KayttajaController: FSCRUDController
    {
        public OLD_KayttajaController(BFContext context, IFSProvider fsProvider):
            base(context, fsProvider, "Kayttaja")
        {

        }

        protected override Task<JObject> PerformCreate(string parentId)
        {
            return Task.FromResult(
                JObject.Parse("{ tunnus: '', salasana: '', etunimi: '', sukunimi: ''}")
                );
        }
    }
}


text:
module FamiliesHelper

  def formatted_name(family)
    "#{family.first_name} #{family.last_name}"
  end

end


text:
/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */
package etla.mod.etl.db;

import java.sql.Connection;
import java.util.Date;

/**
 *
 * @author Sergio Flores
 */
public class SEtlPackage {

    SDbEtlLog EtlLog;
    Connection ConnectionSiie;
    Connection ConnectionAvista;
    Date PeriodStart;
    Date PeriodEnd;
    Date DateIssue;
    int InvoiceBatch;
    boolean UpdateData;
    int UpdateMode;

    public SEtlPackage() {
        EtlLog = null;
        ConnectionSiie = null;
        ConnectionAvista = null;
        PeriodStart = null;
        PeriodEnd = null;
        DateIssue = null;
        InvoiceBatch = 0;
        UpdateData = false;
        UpdateMode = 0;
    }
}


text:
<package format="2">
  <name>swri_system_util</name>
  <version>1.0.0</version>
  <description>

     swri_system_util

  </description>
  <author>Marc Alban</author>
  <maintainer email="malban@swri.org">Marc Alban</maintainer>
  <license>BSD</license>
  <url>https://github.com/swri-robotics/marti_common</url>

  <buildtool_depend>catkin</buildtool_depend>

  <depend>roscpp</depend>
  <test_depend>rostest</test_depend>

</package>


text:
<?php
namespace DM\MovieApp\Model;

use DM\MovieApp\Model\Movie;
use DM\MovieApp\Services\MovieAPI;
use \Phalcon\Mvc\Controller;

describe(Movie::class, function () {
    describe("->getId", function () {
        it("returns the movie id", function () {
            $movie_instance = new Movie();
            $movie_instance->id = "1234";
            expect($movie_instance->getId())->toBe("1234");
        });
    });

    describe("->getPosterpath", function () {
        it("returns the movie poster path", function () {
            $movie_instance = new Movie();
            $movie_instance->poster_path = "1234";
            expect($movie_instance->getPosterpath())->toBe("1234");
        });
    });

    describe("->getName", function () {
        it("returns the movie name", function () {
            $movie_instance = new Movie();
            $movie_instance->title = "1234";
            expect($movie_instance->getName())->toBe("1234");
        });
    });

    describe("->getRating", function () {
        it("returns the movie rating", function () {
            $movie_instance = new Movie();
            $movie_instance->rating = "1234";
            expect($movie_instance->getRating())->toBe("1234");
        });
    });

    describe("->getReleaseData", function () {
        it("returns the movie release_data", function () {
            $movie_instance = new Movie();
            $movie_instance->release_data = "1234";
            expect($movie_instance->getReleaseData())->toBe("1234");
        });
    });

    describe("->getOverview", function () {
        it("returns the movie overview", function () {
            $movie_instance = new Movie();
            $movie_instance->overview = "1234";
            expect($movie_instance->getOverview())->toBe("1234");
        });
    });
});


text:
/* */
"use strict";
var Observable_1 = require('../../Observable');
var never_1 = require('../../observable/never');
Observable_1.Observable.never = never_1.never;


text:
/* Code generated by IfcQuery EXPRESS generator, www.ifcquery.com */
#include <sstream>
#include <limits>

#include "ifcpp/model/AttributeObject.h"
#include "ifcpp/model/BuildingException.h"
#include "ifcpp/model/BuildingGuid.h"
#include "ifcpp/reader/ReaderUtil.h"
#include "ifcpp/writer/WriterUtil.h"
#include "ifcpp/IFC4/include/IfcAppliedValue.h"
#include "ifcpp/IFC4/include/IfcConstructionEquipmentResource.h"
#include "ifcpp/IFC4/include/IfcConstructionEquipmentResourceTypeEnum.h"
#include "ifcpp/IFC4/include/IfcGloballyUniqueId.h"
#include "ifcpp/IFC4/include/IfcIdentifier.h"
#include "ifcpp/IFC4/include/IfcLabel.h"
#include "ifcpp/IFC4/include/IfcOwnerHistory.h"
#include "ifcpp/IFC4/include/IfcPhysicalQuantity.h"
#include "ifcpp/IFC4/include/IfcRelAggregates.h"
#include "ifcpp/IFC4/include/IfcRelAssigns.h"
#include "ifcpp/IFC4/include/IfcRelAssignsToResource.h"
#include "ifcpp/IFC4/include/IfcRelAssociates.h"
#include "ifcpp/IFC4/include/IfcRelDeclares.h"
#include "ifcpp/IFC4/include/IfcRelDefinesByObject.h"
#include "ifcpp/IFC4/include/IfcRelDefinesByProperties.h"
#include "ifcpp/IFC4/include/IfcRelDefinesByType.h"
#include "ifcpp/IFC4/include/IfcRelNests.h"
#include "ifcpp/IFC4/include/IfcResourceTime.h"
#include "ifcpp/IFC4/include/IfcText.h"

// ENTITY IfcConstructionEquipmentResource
IfcConstructionEquipmentResource::IfcConstructionEquipmentResource( int id ) { m_entity_id = id; }
shared_ptr<BuildingObject> IfcConstructionEquipmentResource::getDeepCopy( BuildingCopyOptions& options )
{
	shared_ptr<IfcConstructionEquipmentResource> copy_self( new IfcConstructionEquipmentResource() );
	if( m_GlobalId )
	{
		if( options.create_new_IfcGloballyUniqueId ) { copy_self->m_GlobalId = make_shared<IfcGloballyUniqueId>( createBase64Uuid_wstr().data() ); }
		else { copy_self->m_GlobalId = dynamic_pointer_cast<IfcGloballyUniqueId>( m_GlobalId->getDeepCopy(options) ); }
	}
	if( m_OwnerHistory )
	{
		if( options.shallow_copy_IfcOwnerHistory ) { copy_self->m_OwnerHistory = m_OwnerHistory; }
		else { copy_self->m_OwnerHistory = dynamic_pointer_cast<IfcOwnerHistory>( m_OwnerHistory->getDeepCopy(options) ); }
	}
	if( m_Name ) { copy_self->m_Name = dynamic_pointer_cast<IfcLabel>( m_Name->getDeepCopy(options) ); }
	if( m_Description ) { copy_self->m_Description = dynamic_pointer_cast<IfcText>( m_Description->getDeepCopy(options) ); }
	if( m_ObjectType ) { copy_self->m_ObjectType = dynamic_pointer_cast<IfcLabel>( m_ObjectType->getDeepCopy(options) ); }
	if( m_Identification ) { copy_self->m_Identification = dynamic_pointer_cast<IfcIdentifier>( m_Identification->getDeepCopy(options) ); }
	if( m_LongDescription ) { copy_self->m_LongDescription = dynamic_pointer_cast<IfcText>( m_LongDescription->getDeepCopy(options) ); }
	if( m_Usage ) { copy_self->m_Usage = dynamic_pointer_cast<IfcResourceTime>( m_Usage->getDeepCopy(options) ); }
	for( size_t ii=0; ii<m_BaseCosts.size(); ++ii )
	{
		auto item_ii = m_BaseCosts[ii];
		if( item_ii )
		{
			copy_self->m_BaseCosts.emplace_back( dynamic_pointer_cast<IfcAppliedValue>(item_ii->getDeepCopy(options) ) );
		}
	}
	if( m_BaseQuantity ) { copy_self->m_BaseQuantity = dynamic_pointer_cast<IfcPhysicalQuantity>( m_BaseQuantity->getDeepCopy(options) ); }
	if( m_PredefinedType ) { copy_self->m_PredefinedType = dynamic_pointer_cast<IfcConstructionEquipmentResourceTypeEnum>( m_PredefinedType->getDeepCopy(options) ); }
	return copy_self;
}
void IfcConstructionEquipmentResource::getStepLine( std::stringstream& stream ) const
{
	stream << "#" << m_entity_id << "= IFCCONSTRUCTIONEQUIPMENTRESOURCE" << "(";
	if( m_GlobalId ) { m_GlobalId->getStepParameter( stream ); } else { stream << "$"; }
	stream << ",";
	if( m_OwnerHistory ) { stream << "#" << m_OwnerHistory->m_entity_id; } else { stream << "$"; }
	stream << ",";
	if( m_Name ) { m_Name->getStepParameter( stream ); } else { stream << "$"; }
	stream << ",";
	if( m_Description ) { m_Description->getStepParameter( stream ); } else { stream << "$"; }
	stream << ",";
	if( m_ObjectType ) { m_ObjectType->getStepParameter( stream ); } else { stream << "$"; }
	stream << ",";
	if( m_Identification ) { m_Identification->getStepParameter( stream ); } else { stream << "$"; }
	stream << ",";
	if( m_LongDescription ) { m_LongDescription->getStepParameter( stream ); } else { stream << "$"; }
	stream << ",";
	if( m_Usage ) { stream << "#" << m_Usage->m_entity_id; } else { stream << "$"; }
	stream << ",";
	writeEntityList( stream, m_BaseCosts );
	stream << ",";
	if( m_BaseQuantity ) { stream << "#" << m_BaseQuantity->m_entity_id; } else { stream << "$"; }
	stream << ",";
	if( m_PredefinedType ) { m_PredefinedType->getStepParameter( stream ); } else { stream << "$"; }
	stream << ");";
}
void IfcConstructionEquipmentResource::getStepParameter( std::stringstream& stream, bool /*is_select_type*/ ) const { stream << "#" << m_entity_id; }
const std::wstring IfcConstructionEquipmentResource::toString() const { return L"IfcConstructionEquipmentResource"; }
void IfcConstructionEquipmentResource::readStepArguments( const std::vector<std::wstring>& args, const std::map<int,shared_ptr<BuildingEntity> >& map )
{
	const size_t num_args = args.size();
	if( num_args != 11 ){ std::stringstream err; err << "Wrong parameter count for entity IfcConstructionEquipmentResource, expecting 11, having " << num_args << ". Entity ID: " << m_entity_id << std::endl; throw BuildingException( err.str().c_str() ); }
	m_GlobalId = IfcGloballyUniqueId::createObjectFromSTEP( args[0], map );
	readEntityReference( args[1], m_OwnerHistory, map );
	m_Name = IfcLabel::createObjectFromSTEP( args[2], map );
	m_Description = IfcText::createObjectFromSTEP( args[3], map );
	m_ObjectType = IfcLabel::createObjectFromSTEP( args[4], map );
	m_Identification = IfcIdentifier::createObjectFromSTEP( args[5], map );
	m_LongDescription = IfcText::createObjectFromSTEP( args[6], map );
	readEntityReference( args[7], m_Usage, map );
	readEntityReferenceList( args[8], m_BaseCosts, map );
	readEntityReference( args[9], m_BaseQuantity, map );
	m_PredefinedType = IfcConstructionEquipmentResourceTypeEnum::createObjectFromSTEP( args[10], map );
}
void IfcConstructionEquipmentResource::getAttributes( std::vector<std::pair<std::string, shared_ptr<BuildingObject> > >& vec_attributes ) const
{
	IfcConstructionResource::getAttributes( vec_attributes );
	vec_attributes.emplace_back( std::make_pair( "PredefinedType", m_PredefinedType ) );
}
void IfcConstructionEquipmentResource::getAttributesInverse( std::vector<std::pair<std::string, shared_ptr<BuildingObject> > >& vec_attributes_inverse ) const
{
	IfcConstructionResource::getAttributesInverse( vec_attributes_inverse );
}
void IfcConstructionEquipmentResource::setInverseCounterparts( shared_ptr<BuildingEntity> ptr_self_entity )
{
	IfcConstructionResource::setInverseCounterparts( ptr_self_entity );
}
void IfcConstructionEquipmentResource::unlinkFromInverseCounterparts()
{
	IfcConstructionResource::unlinkFromInverseCounterparts();
}