making a lot of progress on the js side
This commit is contained in:
parent
3d8c30e8b6
commit
deecbbaf0f
33 changed files with 533 additions and 826 deletions
22
app/Controllers/StatusController.php
Normal file
22
app/Controllers/StatusController.php
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* Holds the status controller.
|
||||||
|
*
|
||||||
|
* @package Sakura
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace Sakura\Controllers;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The status page and related stuff.
|
||||||
|
*
|
||||||
|
* @package Sakura
|
||||||
|
* @author Julian van de Groep <me@flash.moe>
|
||||||
|
*/
|
||||||
|
class StatusController extends Controller
|
||||||
|
{
|
||||||
|
public function index()
|
||||||
|
{
|
||||||
|
return view('status/index');
|
||||||
|
}
|
||||||
|
}
|
|
@ -90,10 +90,20 @@ class Template
|
||||||
$views_dir = ROOT . 'resources/views/';
|
$views_dir = ROOT . 'resources/views/';
|
||||||
|
|
||||||
// Initialise Twig Filesystem Loader
|
// Initialise Twig Filesystem Loader
|
||||||
$twigLoader = new Twig_Loader_Filesystem([$views_dir . self::$name, $views_dir . 'shared/']);
|
$loader = new Twig_Loader_Filesystem();
|
||||||
|
|
||||||
|
foreach (glob("{$views_dir}*") as $dir) {
|
||||||
|
$key = basename($dir);
|
||||||
|
|
||||||
|
if ($key === self::$name) {
|
||||||
|
$key = '__main__';
|
||||||
|
}
|
||||||
|
|
||||||
|
$loader->addPath($dir, $key);
|
||||||
|
}
|
||||||
|
|
||||||
// Environment variable
|
// Environment variable
|
||||||
$twigEnv = [
|
$env = [
|
||||||
'cache' => config("performance.template_cache")
|
'cache' => config("performance.template_cache")
|
||||||
? realpath(ROOT . config("performance.cache_dir") . 'views')
|
? realpath(ROOT . config("performance.cache_dir") . 'views')
|
||||||
: false,
|
: false,
|
||||||
|
@ -102,7 +112,7 @@ class Template
|
||||||
];
|
];
|
||||||
|
|
||||||
// And now actually initialise the templating engine
|
// And now actually initialise the templating engine
|
||||||
self::$engine = new Twig_Environment($twigLoader, $twigEnv);
|
self::$engine = new Twig_Environment($loader, $env);
|
||||||
|
|
||||||
// Load String template loader
|
// Load String template loader
|
||||||
self::$engine->addExtension(new Twig_Extension_StringLoader());
|
self::$engine->addExtension(new Twig_Extension_StringLoader());
|
||||||
|
|
BIN
public/images/status-banner.png
Normal file
BIN
public/images/status-banner.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 487 KiB |
17
resources/assets/typescript/Sakura.ts
Normal file
17
resources/assets/typescript/Sakura.ts
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
/// <reference path="Sakura/AJAX.ts" />
|
||||||
|
/// <reference path="Sakura/Changelog.ts" />
|
||||||
|
/// <reference path="Sakura/Comments.ts" />
|
||||||
|
/// <reference path="Sakura/Config.ts" />
|
||||||
|
/// <reference path="Sakura/Cookies.ts" />
|
||||||
|
/// <reference path="Sakura/DOM.ts" />
|
||||||
|
/// <reference path="Sakura/Friend.ts" />
|
||||||
|
/// <reference path="Sakura/HTTPMethod.ts" />
|
||||||
|
/// <reference path="Sakura/IChangelogAction.ts" />
|
||||||
|
/// <reference path="Sakura/IChangelogChange.ts" />
|
||||||
|
/// <reference path="Sakura/IChangelogContributor.ts" />
|
||||||
|
/// <reference path="Sakura/IChangelogDate.ts" />
|
||||||
|
/// <reference path="Sakura/IChangelogRelease.ts" />
|
||||||
|
/// <reference path="Sakura/IFriendResponse.ts" />
|
||||||
|
/// <reference path="Sakura/INotification.ts" />
|
||||||
|
/// <reference path="Sakura/Notifications.ts" />
|
||||||
|
/// <reference path="Sakura/TimeAgo.ts" />
|
|
@ -3,7 +3,7 @@ namespace Sakura
|
||||||
export class Changelog
|
export class Changelog
|
||||||
{
|
{
|
||||||
private static Client: AJAX;
|
private static Client: AJAX;
|
||||||
private static Element: DOM;
|
private static Element: HTMLDivElement;
|
||||||
private static Fetch: number = 0;
|
private static Fetch: number = 0;
|
||||||
private static Colours: string[] = [
|
private static Colours: string[] = [
|
||||||
'inherit', // Unknown
|
'inherit', // Unknown
|
||||||
|
@ -15,25 +15,25 @@ namespace Sakura
|
||||||
'#C44', // Revert
|
'#C44', // Revert
|
||||||
];
|
];
|
||||||
|
|
||||||
public static Build(target: DOM)
|
public static Build(target: HTMLElement)
|
||||||
{
|
{
|
||||||
this.Client = new AJAX;
|
this.Client = new AJAX;
|
||||||
this.Element = DOM.Create('table', 'changelog panelTable');
|
this.Element = <HTMLDivElement>DOM.Create('table', 'changelog panelTable');
|
||||||
|
|
||||||
this.Element.Element.style.borderSpacing = '0 1px';
|
this.Element.style.borderSpacing = '0 1px';
|
||||||
|
|
||||||
var title: DOM = DOM.Create('div', 'head'),
|
var title: HTMLDivElement = <HTMLDivElement>DOM.Create('div', 'head'),
|
||||||
link: DOM = DOM.Create('a', 'underline');
|
link: HTMLLinkElement = <HTMLLinkElement>DOM.Create('a', 'underline');
|
||||||
|
|
||||||
title.Element.style.marginBottom = '1px';
|
title.style.marginBottom = '1px';
|
||||||
|
|
||||||
link.Text('Changelog');
|
link.innerText = 'Changelog';
|
||||||
(<HTMLLinkElement>link.Element).href = Config.ChangelogUrl + '#r' + Config.Revision;
|
link.href = Config.ChangelogUrl + '#r' + Config.Revision;
|
||||||
(<HTMLLinkElement>link.Element).target = '_blank';
|
link.target = '_blank';
|
||||||
|
|
||||||
title.Append(link);
|
DOM.Append(title, link);
|
||||||
target.Append(title);
|
DOM.Append(target, title);
|
||||||
target.Append(this.Element);
|
DOM.Append(target, this.Element);
|
||||||
|
|
||||||
this.Client.SetUrl(Config.ChangelogUrl + Config.ChangelogApi);
|
this.Client.SetUrl(Config.ChangelogUrl + Config.ChangelogApi);
|
||||||
|
|
||||||
|
@ -53,34 +53,33 @@ namespace Sakura
|
||||||
|
|
||||||
private static Add(changelog: IChangelogDate)
|
private static Add(changelog: IChangelogDate)
|
||||||
{
|
{
|
||||||
var header: DOM = DOM.Create('tr', 'changelog__row changelog__row--header'),
|
var header: HTMLTableRowElement = <HTMLTableRowElement>DOM.Create('tr', 'changelog__row changelog__row--header'),
|
||||||
headerInner: DOM = DOM.Create('th', 'changelog__header');
|
headerInner: HTMLTableHeaderCellElement = <HTMLTableHeaderCellElement>DOM.Create('th', 'changelog__header');
|
||||||
|
|
||||||
headerInner.Text(changelog.date);
|
headerInner.innerText = changelog.date;
|
||||||
headerInner.Element.style.fontSize = '1.2em';
|
headerInner.style.fontSize = '1.2em';
|
||||||
(<HTMLTableHeaderCellElement>headerInner.Element).colSpan = 2;
|
headerInner.colSpan = 2;
|
||||||
|
|
||||||
header.Append(headerInner);
|
DOM.Append(header, headerInner);
|
||||||
this.Element.Append(header);
|
DOM.Append(this.Element, header);
|
||||||
|
|
||||||
for (var _i in changelog.changes)
|
for (var _i in changelog.changes)
|
||||||
{
|
{
|
||||||
var change: IChangelogChange = changelog.changes[_i],
|
var change: IChangelogChange = changelog.changes[_i],
|
||||||
row: DOM = DOM.Create('tr', 'changelog__row'),
|
row: HTMLTableRowElement = <HTMLTableRowElement>DOM.Create('tr', 'changelog__row'),
|
||||||
action: DOM = DOM.Create('td', 'changelog__column'),
|
action: HTMLTableCellElement = <HTMLTableCellElement>DOM.Create('td', 'changelog__column'),
|
||||||
message: DOM = DOM.Create('td', 'changelog__column');
|
message: HTMLTableCellElement = <HTMLTableCellElement>DOM.Create('td', 'changelog__column');
|
||||||
|
|
||||||
action.Text(change.action.name);
|
action.innerText = change.action.name;
|
||||||
action.Element.style.backgroundColor = this.Colours[change.action.id];
|
action.style.backgroundColor = this.Colours[change.action.id];
|
||||||
action.Element.style.borderBottom = '1px solid ' + this.Colours[change.action.id];
|
action.style.borderBottom = '1px solid ' + this.Colours[change.action.id];
|
||||||
|
|
||||||
message.Text(change.message);
|
message.innerText = change.message;
|
||||||
message.Element.style.borderBottom = '1px solid ' + this.Colours[change.action.id];
|
message.style.borderBottom = '1px solid ' + this.Colours[change.action.id];
|
||||||
|
|
||||||
row.Append(action);
|
DOM.Append(row, action);
|
||||||
row.Append(message);
|
DOM.Append(row, message);
|
||||||
|
DOM.Append(this.Element, row);
|
||||||
this.Element.Append(row);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,9 +5,6 @@ namespace Sakura
|
||||||
public static Revision: number = 0;
|
public static Revision: number = 0;
|
||||||
public static UserId: number = 0;
|
public static UserId: number = 0;
|
||||||
public static SessionId: string = "";
|
public static SessionId: string = "";
|
||||||
public static UserNameMinLength: number = 3;
|
|
||||||
public static UserNameMaxLength: number = 16;
|
|
||||||
public static PasswordMinEntropy: number = 48;
|
|
||||||
public static LoggedIn: boolean = false;
|
public static LoggedIn: boolean = false;
|
||||||
public static ChangelogUrl: string = "https://sakura.flash.moe/";
|
public static ChangelogUrl: string = "https://sakura.flash.moe/";
|
||||||
public static ChangelogApi: string = "api.php/";
|
public static ChangelogApi: string = "api.php/";
|
||||||
|
|
|
@ -2,73 +2,186 @@ namespace Sakura
|
||||||
{
|
{
|
||||||
export class DOM
|
export class DOM
|
||||||
{
|
{
|
||||||
public Element: HTMLElement;
|
public static BEM(block: string, element: string = null, modifiers: string[] = [], firstModifierOnly: boolean = false): string
|
||||||
|
{
|
||||||
|
var className: string = "";
|
||||||
|
|
||||||
constructor(object: any, mode: DOMSelector) {
|
if (firstModifierOnly && modifiers.length === 0) {
|
||||||
switch (mode) {
|
return null;
|
||||||
case DOMSelector.ID:
|
|
||||||
this.Element = document.getElementById(object);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DOMSelector.CLASS:
|
|
||||||
this.Element = <HTMLElement>document.getElementsByClassName(object)[0];
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DOMSelector.ELEMENT:
|
|
||||||
this.Element = object;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DOMSelector.QUERY:
|
|
||||||
this.Element = <HTMLElement>document.querySelector(object);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Create(element: string, className: string = null, id: string = null): DOM {
|
className += block;
|
||||||
var elem: HTMLElement = document.createElement(element),
|
|
||||||
cont: DOM = new DOM(elem, DOMSelector.ELEMENT);
|
if (element !== null) {
|
||||||
|
className += "__" + element;
|
||||||
|
}
|
||||||
|
|
||||||
|
var baseName: string = className;
|
||||||
|
|
||||||
|
for (var _i in modifiers) {
|
||||||
|
if (firstModifierOnly) {
|
||||||
|
return baseName + "--" + modifiers[_i];
|
||||||
|
}
|
||||||
|
|
||||||
|
className += " " + baseName + "--" + modifiers[_i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return className;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Create(name: string, className: string = null, id: string = null): HTMLElement {
|
||||||
|
var element = document.createElement(name);
|
||||||
|
|
||||||
if (className !== null) {
|
if (className !== null) {
|
||||||
cont.SetClass(className);
|
element.className = className;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (id !== null) {
|
if (id !== null) {
|
||||||
cont.SetId(id);
|
element.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return cont;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Text(text: string): void {
|
public static Text(text: string): Text {
|
||||||
this.Element.appendChild(document.createTextNode(text));
|
return document.createTextNode(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Append(element: DOM): void {
|
public static ID(id: string): HTMLElement {
|
||||||
this.Element.appendChild(element.Element);
|
return document.getElementById(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Prepend(element: DOM, before: HTMLElement | Node = null): void {
|
public static Remove(element: HTMLElement): void {
|
||||||
|
element.parentNode.removeChild(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Class(className: string): NodeListOf<HTMLElement> {
|
||||||
|
return <NodeListOf<HTMLElement>>document.getElementsByClassName(className);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Prepend(target: HTMLElement, element: HTMLElement | Text, before: HTMLElement | Node = null): void {
|
||||||
if (before === null) {
|
if (before === null) {
|
||||||
before = this.Element.firstChild;
|
before = target.firstChild;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.Element.children.length) {
|
if (target.children.length) {
|
||||||
this.Element.insertBefore(element.Element, before);
|
target.insertBefore(element, before);
|
||||||
} else {
|
} else {
|
||||||
this.Append(element);
|
this.Append(target, element);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public SetId(id: string): void {
|
public static Append(target: HTMLElement, element: HTMLElement | Text): void {
|
||||||
this.Element.id = id;
|
target.appendChild(element);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SetClass(name: string): void {
|
public static ClassNames(target: HTMLElement): string[] {
|
||||||
this.Element.className = name;
|
var className: string = target.className,
|
||||||
|
classes: string[] = [];
|
||||||
|
|
||||||
|
if (className.length > 1) {
|
||||||
|
classes = className.split(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
public Remove(): void {
|
return classes;
|
||||||
this.Element.parentNode.removeChild(this.Element);
|
}
|
||||||
|
|
||||||
|
public static AddClass(target: HTMLElement, classes: string[]): void {
|
||||||
|
for (var _i in classes) {
|
||||||
|
var current: string[] = this.ClassNames(target),
|
||||||
|
index: number = current.indexOf(classes[_i]);
|
||||||
|
|
||||||
|
if (index >= 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.push(classes[_i]);
|
||||||
|
|
||||||
|
target.className = current.join(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static RemoveClass(target: HTMLElement, classes: string[]): void {
|
||||||
|
for (var _i in classes) {
|
||||||
|
var current: string[] = this.ClassNames(target),
|
||||||
|
index: number = current.indexOf(classes[_i]);
|
||||||
|
|
||||||
|
if (index < 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
current.splice(index, 1);
|
||||||
|
|
||||||
|
target.className = current.join(' ');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Clone(subject: HTMLElement): HTMLElement {
|
||||||
|
return (<HTMLElement>subject.cloneNode(true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Query(query: string): NodeListOf<Element> {
|
||||||
|
return document.querySelectorAll(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SetPosition(element: HTMLTextAreaElement, pos: number, end: boolean = false): void {
|
||||||
|
if (end) {
|
||||||
|
element.selectionEnd = pos;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
element.selectionStart = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GetPosition(element: HTMLTextAreaElement, end: boolean = false): number {
|
||||||
|
if (end) {
|
||||||
|
return element.selectionEnd;
|
||||||
|
}
|
||||||
|
|
||||||
|
return element.selectionStart;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GoToStart(element: HTMLTextAreaElement): void {
|
||||||
|
this.SetPosition(element, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GoToEnd(element: HTMLTextAreaElement): void {
|
||||||
|
this.SetPosition(element, element.value.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GetSelectionLength(element: HTMLTextAreaElement): number {
|
||||||
|
var length: number = this.GetPosition(element, true) - this.GetPosition(element);
|
||||||
|
|
||||||
|
if (length < 0) {
|
||||||
|
length = this.GetPosition(element) - this.GetPosition(element, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static EnterAtCursor(element: HTMLTextAreaElement, text: string, overwrite: boolean = false): void {
|
||||||
|
var value: string = this.GetText(element),
|
||||||
|
final: string = "",
|
||||||
|
current: number = this.GetPosition(element);
|
||||||
|
|
||||||
|
final += value.slice(0, current);
|
||||||
|
final += text;
|
||||||
|
final += value.slice(current + (overwrite ? text.length : 0));
|
||||||
|
|
||||||
|
this.SetText(element, final);
|
||||||
|
this.SetPosition(element, current);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GetSelectedText(element: HTMLTextAreaElement): string {
|
||||||
|
return this.GetText(element).slice(this.GetPosition(element), this.GetPosition(element, true));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GetText(element: HTMLTextAreaElement): string {
|
||||||
|
return element.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SetText(element: HTMLTextAreaElement, text: string): void {
|
||||||
|
element.value = text;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +0,0 @@
|
||||||
namespace Sakura
|
|
||||||
{
|
|
||||||
export enum DOMSelector
|
|
||||||
{
|
|
||||||
ID,
|
|
||||||
CLASS,
|
|
||||||
ELEMENT,
|
|
||||||
QUERY
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,5 +1,3 @@
|
||||||
declare var ajaxBusyView: any;
|
|
||||||
|
|
||||||
namespace Sakura
|
namespace Sakura
|
||||||
{
|
{
|
||||||
export class Friend
|
export class Friend
|
||||||
|
@ -28,18 +26,22 @@ namespace Sakura
|
||||||
this.Client.SetSend({ "session": Config.SessionId });
|
this.Client.SetSend({ "session": Config.SessionId });
|
||||||
this.Client.AddCallback(200, (client: AJAX) => {
|
this.Client.AddCallback(200, (client: AJAX) => {
|
||||||
var response: IFriendResponse = <IFriendResponse>client.JSON(),
|
var response: IFriendResponse = <IFriendResponse>client.JSON(),
|
||||||
error: string = response.error || null;
|
alert: INotification = {
|
||||||
|
id: -(Date.now()),
|
||||||
|
user: Config.UserId,
|
||||||
|
time: Math.round(Date.now() / 1000),
|
||||||
|
read: false,
|
||||||
|
title: response.error || response.message,
|
||||||
|
text: "",
|
||||||
|
link: null,
|
||||||
|
image: "FONT:fa-user-plus",
|
||||||
|
timeout: 60000
|
||||||
|
};
|
||||||
|
|
||||||
if (error !== null) {
|
Notifications.DisplayMethod.call(this, alert);
|
||||||
ajaxBusyView(true, error, 'fail');
|
|
||||||
|
|
||||||
setTimeout(() => {
|
// replace this with a thing that just updates the dom
|
||||||
ajaxBusyView(false);
|
window.location.reload();
|
||||||
}, 1500);
|
|
||||||
} else {
|
|
||||||
ajaxBusyView(true, response.message, 'ok');
|
|
||||||
location.reload();
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
this.Client.Start(HTTPMethod.POST);
|
this.Client.Start(HTTPMethod.POST);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
declare function escape(a);
|
|
||||||
|
|
||||||
namespace Sakura
|
|
||||||
{
|
|
||||||
export class Legacy
|
|
||||||
{
|
|
||||||
// Alternative for Math.log2() since it's still experimental
|
|
||||||
public static log2(num: number): number {
|
|
||||||
return Math.log(num) / Math.log(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the number of unique characters in a string
|
|
||||||
public static unique(string: string): number {
|
|
||||||
// Store the already found character
|
|
||||||
var used: string[] = [];
|
|
||||||
|
|
||||||
// The amount of characters we've already found
|
|
||||||
var count: number = 0;
|
|
||||||
|
|
||||||
// Count the amount of unique characters
|
|
||||||
for (var i = 0; i < string.length; i++) {
|
|
||||||
// Check if we already counted this character
|
|
||||||
if (used.indexOf(string[i]) == -1) {
|
|
||||||
// Push the character into the used array
|
|
||||||
used.push(string[i]);
|
|
||||||
|
|
||||||
// Up the count
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the count
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate password entropy
|
|
||||||
public static entropy(string: string): number {
|
|
||||||
// Decode utf-8 encoded characters
|
|
||||||
string = this.utf8_decode(string);
|
|
||||||
|
|
||||||
// Count the unique characters in the string
|
|
||||||
var unique: number = this.unique(string);
|
|
||||||
|
|
||||||
// Do the entropy calculation
|
|
||||||
return unique * this.log2(256);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate string lengths
|
|
||||||
public static stringLength(string: string, minimum: number, maximum: number): boolean {
|
|
||||||
// Get length of string
|
|
||||||
var length = string.length;
|
|
||||||
|
|
||||||
// Check if it meets the minimum/maximum
|
|
||||||
if (length < minimum || length > maximum) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If it passes both return true
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Validate email address formats
|
|
||||||
public static validateEmail(email: string): boolean {
|
|
||||||
// RFC compliant e-mail address regex
|
|
||||||
var re = /^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,48})+$/;
|
|
||||||
|
|
||||||
// Test it on the email var which'll return a boolean
|
|
||||||
return re.test(email);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decode a utf-8 string
|
|
||||||
public static utf8_decode(string): string {
|
|
||||||
return decodeURIComponent(escape(string));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,16 +0,0 @@
|
||||||
namespace Sakura
|
|
||||||
{
|
|
||||||
export class Main
|
|
||||||
{
|
|
||||||
public static Startup(): void {
|
|
||||||
console.log(this.Supported());
|
|
||||||
TimeAgo.Init();
|
|
||||||
Friend.Init();
|
|
||||||
Notifications.Init();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Supported(): boolean {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -4,19 +4,17 @@ namespace Sakura
|
||||||
{
|
{
|
||||||
private static Client: AJAX;
|
private static Client: AJAX;
|
||||||
private static IntervalContainer: number;
|
private static IntervalContainer: number;
|
||||||
public static DisplayMethod: Function = (alert: INotification) => {
|
public static DisplayMethod: Function = Notifications.Display;
|
||||||
console.log(alert);
|
|
||||||
};
|
|
||||||
|
|
||||||
public static Init(): void
|
public static Init(): void
|
||||||
{
|
{
|
||||||
this.Client = new AJAX;
|
Notifications.Client = new AJAX;
|
||||||
this.Client.SetUrl("/notifications");
|
Notifications.Client.SetUrl("/notifications");
|
||||||
this.Client.AddCallback(200, (client: AJAX) => {
|
Notifications.Client.AddCallback(200, (client: AJAX) => {
|
||||||
Notifications.Load(<INotification[]>client.JSON());
|
Notifications.Load(<INotification[]>client.JSON());
|
||||||
});
|
});
|
||||||
this.Poll();
|
Notifications.Poll();
|
||||||
this.Start();
|
Notifications.Start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Poll(): void
|
public static Poll(): void
|
||||||
|
@ -32,7 +30,7 @@ namespace Sakura
|
||||||
}
|
}
|
||||||
|
|
||||||
Notifications.Poll();
|
Notifications.Poll();
|
||||||
}, 5000);
|
}, 60000);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Stop(): void
|
public static Stop(): void
|
||||||
|
@ -47,5 +45,10 @@ namespace Sakura
|
||||||
this.DisplayMethod(alerts[i]);
|
this.DisplayMethod(alerts[i]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static Display(alert: INotification): void
|
||||||
|
{
|
||||||
|
console.log(alert);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ namespace Sakura
|
||||||
|
|
||||||
public static Init(): void
|
public static Init(): void
|
||||||
{
|
{
|
||||||
var watchElements: NodeListOf<Element> = document.getElementsByClassName(this.WatchClass);
|
var watchElements: NodeListOf<Element> = DOM.Class(this.WatchClass);
|
||||||
|
|
||||||
for (var _i in watchElements) {
|
for (var _i in watchElements) {
|
||||||
if ((typeof watchElements[_i]).toLowerCase() !== 'object') {
|
if ((typeof watchElements[_i]).toLowerCase() !== 'object') {
|
||||||
|
|
6
resources/assets/typescript/Yuuno.ts
Normal file
6
resources/assets/typescript/Yuuno.ts
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
/// <reference path="Yuuno/Busy.ts" />
|
||||||
|
/// <reference path="Yuuno/BusyMode.ts" />
|
||||||
|
/// <reference path="Yuuno/Editor.ts" />
|
||||||
|
/// <reference path="Yuuno/Main.ts" />
|
||||||
|
/// <reference path="Yuuno/Notifications.ts" />
|
||||||
|
/// <reference path="Yuuno/Ybabstat.ts" />
|
47
resources/assets/typescript/Yuuno/Busy.ts
Normal file
47
resources/assets/typescript/Yuuno/Busy.ts
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
namespace Yuuno
|
||||||
|
{
|
||||||
|
export class Busy
|
||||||
|
{
|
||||||
|
private static Container: HTMLElement;
|
||||||
|
private static Text: HTMLElement;
|
||||||
|
private static Icon: HTMLElement;
|
||||||
|
|
||||||
|
public static Init(): void
|
||||||
|
{
|
||||||
|
this.Container = Sakura.DOM.ID('busy-window');
|
||||||
|
this.Text = Sakura.DOM.ID('busy-status');
|
||||||
|
this.Icon = Sakura.DOM.ID('busy-icon');
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Hide(): void
|
||||||
|
{
|
||||||
|
Sakura.DOM.AddClass(this.Container, ['hidden']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Show(mode: BusyMode = BusyMode.BUSY, text: string = null, hideAfter: number = 0): void
|
||||||
|
{
|
||||||
|
var icon: string = "fa fa-4x ";
|
||||||
|
|
||||||
|
switch (mode) {
|
||||||
|
case BusyMode.OK:
|
||||||
|
icon += 'fa-check';
|
||||||
|
break;
|
||||||
|
case BusyMode.FAIL:
|
||||||
|
icon += 'fa-remove';
|
||||||
|
break;
|
||||||
|
case BusyMode.BUSY:
|
||||||
|
default:
|
||||||
|
icon += 'fa-refresh fa-spin';
|
||||||
|
}
|
||||||
|
|
||||||
|
Sakura.DOM.RemoveClass(this.Icon, Sakura.DOM.ClassNames(this.Icon));
|
||||||
|
Sakura.DOM.AddClass(this.Icon, icon.split(' '));
|
||||||
|
this.Text.innerText = text || '';
|
||||||
|
Sakura.DOM.RemoveClass(this.Container, ['hidden']);
|
||||||
|
|
||||||
|
if (hideAfter > 0) {
|
||||||
|
setTimeout(Busy.Hide, hideAfter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
9
resources/assets/typescript/Yuuno/BusyMode.ts
Normal file
9
resources/assets/typescript/Yuuno/BusyMode.ts
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Yuuno
|
||||||
|
{
|
||||||
|
export enum BusyMode
|
||||||
|
{
|
||||||
|
OK,
|
||||||
|
FAIL,
|
||||||
|
BUSY
|
||||||
|
}
|
||||||
|
}
|
20
resources/assets/typescript/Yuuno/Editor.ts
Normal file
20
resources/assets/typescript/Yuuno/Editor.ts
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
/// <reference path="../Sakura.ts" />
|
||||||
|
|
||||||
|
namespace Yuuno
|
||||||
|
{
|
||||||
|
export class Editor
|
||||||
|
{
|
||||||
|
public static InsertBBCode(target: HTMLTextAreaElement, code: string, param: boolean): void
|
||||||
|
{
|
||||||
|
var start: string = "[" + code + (param ? "=" : "") + "]",
|
||||||
|
end: string = "[/" + code + "]",
|
||||||
|
selectionLength = Sakura.DOM.GetSelectionLength(target);
|
||||||
|
|
||||||
|
Sakura.DOM.EnterAtCursor(target, start);
|
||||||
|
Sakura.DOM.SetPosition(target, Sakura.DOM.GetPosition(target) + selectionLength + start.length);
|
||||||
|
Sakura.DOM.EnterAtCursor(target, end);
|
||||||
|
Sakura.DOM.SetPosition(target, Sakura.DOM.GetPosition(target) - selectionLength);
|
||||||
|
Sakura.DOM.SetPosition(target, Sakura.DOM.GetPosition(target) + selectionLength, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,28 +1,75 @@
|
||||||
/// <reference path="../Sakura/INotification.ts" />
|
/// <reference path="../Sakura.ts" />
|
||||||
|
|
||||||
namespace Yuuno
|
namespace Yuuno
|
||||||
{
|
{
|
||||||
export class Notifications
|
export class Notifications extends Sakura.Notifications
|
||||||
{
|
{
|
||||||
private static Container;
|
private static Container: HTMLElement;
|
||||||
|
|
||||||
public static RegisterDisplay(): void
|
public static Init(): void
|
||||||
{
|
{
|
||||||
this.Container = new Sakura.DOM('notifications', Sakura.DOMSelector.ID);
|
|
||||||
Sakura.Notifications.DisplayMethod = this.Display;
|
Sakura.Notifications.DisplayMethod = this.Display;
|
||||||
|
super.Init();
|
||||||
|
Notifications.Container = Sakura.DOM.ID('notifications');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Display(alert: Sakura.INotification): void
|
public static Display(alert: Sakura.INotification): void
|
||||||
{
|
{
|
||||||
var id = 'yuuno-alert-' + Date.now(),
|
var id = 'yuuno-alert-' + Date.now(),
|
||||||
container = Sakura.DOM.Create('div', 'notification-enter', id),
|
container: HTMLDivElement = <HTMLDivElement>Sakura.DOM.Create('div', 'notification-enter', id),
|
||||||
icon = Sakura.DOM.Create('div', 'notification-icon'),
|
iconContent: HTMLDivElement = <HTMLDivElement>Sakura.DOM.Create('div'),
|
||||||
inner = Sakura.DOM.Create('div', 'notification-content'),
|
icon: HTMLDivElement = <HTMLDivElement>Sakura.DOM.Create('div', 'notification-icon'),
|
||||||
title = Sakura.DOM.Create('div', 'notification-title'),
|
inner: HTMLDivElement = <HTMLDivElement>Sakura.DOM.Create('div', 'notification-content'),
|
||||||
text = Sakura.DOM.Create('div', 'notification-text'),
|
title: HTMLDivElement = <HTMLDivElement>Sakura.DOM.Create('div', 'notification-title'),
|
||||||
close = Sakura.DOM.Create('div', 'notification-close'),
|
text: HTMLDivElement = <HTMLDivElement>Sakura.DOM.Create('div', 'notification-text'),
|
||||||
closeIcon = Sakura.DOM.Create('div'),
|
close: HTMLDivElement = <HTMLDivElement>Sakura.DOM.Create('div', 'notification-close'),
|
||||||
clear = Sakura.DOM.Create('div');
|
closeIcon: HTMLDivElement = <HTMLDivElement>Sakura.DOM.Create('div');
|
||||||
|
|
||||||
|
if (alert.image === null) {
|
||||||
|
Sakura.DOM.AddClass(iconContent, ['font-icon', 'fa', 'fa-info', 'fa-4x']);
|
||||||
|
} else if (alert.image.substring(0, 5) == 'FONT:') {
|
||||||
|
Sakura.DOM.AddClass(iconContent, ['font-icon', 'fa', alert.image.replace('FONT:', ''), 'fa-4x']);
|
||||||
|
} else {
|
||||||
|
iconContent.style.background = "url(0) no-repeat center center / cover transparent".replace('0', alert.image);
|
||||||
|
iconContent.style.width = "100%";
|
||||||
|
iconContent.style.height = "100%";
|
||||||
|
}
|
||||||
|
|
||||||
|
Sakura.DOM.Append(icon, iconContent);
|
||||||
|
Sakura.DOM.Append(container, icon);
|
||||||
|
|
||||||
|
title.innerText = alert.title;
|
||||||
|
text.innerText = alert.text;
|
||||||
|
|
||||||
|
if (alert.link !== null) {
|
||||||
|
inner.setAttribute('onclick', alert.link.substr(0, 11) == 'javascript:' ? alert.link.substring(11) : 'window.location.assign("' + alert.link + '");');
|
||||||
|
}
|
||||||
|
|
||||||
|
Sakura.DOM.Append(inner, title);
|
||||||
|
Sakura.DOM.Append(inner, text);
|
||||||
|
Sakura.DOM.Append(container, inner);
|
||||||
|
|
||||||
|
close.setAttribute('onclick', 'Yuuno.Notifications.CloseAlert(this.parentNode.id);');
|
||||||
|
|
||||||
|
Sakura.DOM.Append(close, closeIcon);
|
||||||
|
Sakura.DOM.Append(container, close);
|
||||||
|
|
||||||
|
Sakura.DOM.Append(Notifications.Container, container);
|
||||||
|
|
||||||
|
if (alert.timeout > 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
Notifications.CloseAlert(id);
|
||||||
|
}, alert.timeout);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static CloseAlert(id: string): void
|
||||||
|
{
|
||||||
|
var element: HTMLElement = Sakura.DOM.ID(id);
|
||||||
|
Sakura.DOM.AddClass(element, ['notification-exit']);
|
||||||
|
setTimeout(() => {
|
||||||
|
Sakura.DOM.Remove(element);
|
||||||
|
}, 410);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,19 @@
|
||||||
|
/// <reference path="../Sakura.ts" />
|
||||||
|
|
||||||
namespace Yuuno
|
namespace Yuuno
|
||||||
{
|
{
|
||||||
export class Main
|
export class Main
|
||||||
{
|
{
|
||||||
public static Startup()
|
public static Startup()
|
||||||
{
|
{
|
||||||
Sakura.Main.Startup();
|
Sakura.TimeAgo.Init();
|
||||||
Notifications.RegisterDisplay();
|
Sakura.Friend.Init();
|
||||||
|
Notifications.Init();
|
||||||
|
Busy.Init();
|
||||||
|
|
||||||
|
if (window.location.pathname === '/' || window.location.pathname === '/forum' || window.location.pathname === '/forum/') {
|
||||||
|
Ybabstat.Initiate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,63 +1,24 @@
|
||||||
var illuminati: Array<number> = new Array<number>();
|
/// <reference path="../Sakura.ts" />
|
||||||
var startTime: number = (new Date()).getTime();
|
|
||||||
|
|
||||||
function hideYourMind(conflictions: KeyboardEvent): void {
|
namespace Yuuno
|
||||||
var twoThousandTwelveIsTheYearWeAscendToSpaceRobots: number = conflictions.keyCode;
|
{
|
||||||
|
export class Ybabstat
|
||||||
|
{
|
||||||
|
private static Illuminati: number[] = [];
|
||||||
|
private static FreeMason: number = Date.now();
|
||||||
|
|
||||||
illuminati.push(twoThousandTwelveIsTheYearWeAscendToSpaceRobots);
|
public static Initiate(): void
|
||||||
|
{
|
||||||
if (illuminati[0] == 68 && illuminati[1] == 73 && illuminati[2] == 67 && illuminati[3] == 75 && illuminati[4] == 83) {
|
document.addEventListener('keydown', Ybabstat.HideYourMind);
|
||||||
var dicksAre: HTMLAudioElement = document.createElement('audio');
|
|
||||||
var forMyFriends: HTMLSourceElement = document.createElement('source');
|
|
||||||
var whenTheyCome: HTMLSourceElement = document.createElement('source');
|
|
||||||
|
|
||||||
forMyFriends.type = 'audio/mp3';
|
|
||||||
whenTheyCome.type = 'audio/ogg';
|
|
||||||
|
|
||||||
forMyFriends.src = 'https://data.flashii.net/sounds/dicks.mp3';
|
|
||||||
whenTheyCome.src = 'https://data.flashii.net/sounds/dicks.ogg';
|
|
||||||
|
|
||||||
dicksAre.appendChild(forMyFriends);
|
|
||||||
dicksAre.appendChild(whenTheyCome);
|
|
||||||
|
|
||||||
var toMyHouse: HTMLAudioElement = dicksAre;
|
|
||||||
|
|
||||||
toMyHouse.play();
|
|
||||||
|
|
||||||
illuminati = new Array<number>();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (illuminati[0] == 77 && illuminati[1] == 69 && illuminati[2] == 87 && illuminati[3] == 79 && illuminati[4] == 87) {
|
private static TwoThousandSixteenIsTheYearWePhysicallyMergeWithCats(): void
|
||||||
var noklz: HTMLAudioElement = document.createElement('audio');
|
{
|
||||||
var von: HTMLSourceElement = document.createElement('source');
|
var diff: number = Date.now() - Ybabstat.FreeMason,
|
||||||
var schnitzel: HTMLSourceElement = document.createElement('source');
|
vals: number[] = [
|
||||||
|
-7 / Math.cos((diff / 500) * (.85 * Math.PI)),
|
||||||
von.type = 'audio/mp3';
|
-7 * Math.tan((diff / 250) * (.85 * Math.PI))
|
||||||
schnitzel.type = 'audio/ogg';
|
];
|
||||||
|
|
||||||
von.src = 'https://data.flashii.net/sounds/mewow.mp3';
|
|
||||||
schnitzel.src = 'https://data.flashii.net/sounds/mewow.ogg';
|
|
||||||
|
|
||||||
noklz.appendChild(von);
|
|
||||||
noklz.appendChild(schnitzel);
|
|
||||||
|
|
||||||
noklz.play();
|
|
||||||
|
|
||||||
document.body.style.animation = 'spin 5s infinite linear';
|
|
||||||
|
|
||||||
illuminati = new Array<number>();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (illuminati[0] == 83 && illuminati[1] == 79 && illuminati[2] == 67 && illuminati[3] == 75 && illuminati[4] == 67 && illuminati[5] == 72 && illuminati[6] == 65 && illuminati[7] == 84) {
|
|
||||||
setInterval(twoThousandSixteenIsTheYearWePhysicallyMergeWithCats, 20);
|
|
||||||
|
|
||||||
illuminati = new Array<number>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function twoThousandSixteenIsTheYearWePhysicallyMergeWithCats() {
|
|
||||||
var diff: number = (new Date()).getTime() - startTime;
|
|
||||||
var vals: Array<number> = [-7 / Math.cos((diff / 500) * (.85 * Math.PI)), -7 * Math.tan((diff / 250) * (.85 * Math.PI))];
|
|
||||||
|
|
||||||
document.body.style.position = 'absolute';
|
document.body.style.position = 'absolute';
|
||||||
document.body.style.left = vals[0] + 'px';
|
document.body.style.left = vals[0] + 'px';
|
||||||
|
@ -65,4 +26,57 @@ function twoThousandSixteenIsTheYearWePhysicallyMergeWithCats() {
|
||||||
document.body.style.fontSize = vals[0] + 'px';
|
document.body.style.fontSize = vals[0] + 'px';
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('keydown', hideYourMind, false);
|
private static HideYourMind(conflictions: KeyboardEvent): void
|
||||||
|
{
|
||||||
|
var twoThousandTwelveIsTheYearWeAscendToSpaceRobots: number = conflictions.keyCode;
|
||||||
|
|
||||||
|
Ybabstat.Illuminati.push(twoThousandTwelveIsTheYearWeAscendToSpaceRobots);
|
||||||
|
|
||||||
|
if (Ybabstat.Illuminati[0] === 68 && Ybabstat.Illuminati[1] === 73 && Ybabstat.Illuminati[2] === 67 && Ybabstat.Illuminati[3] === 75 && Ybabstat.Illuminati[4] === 83) {
|
||||||
|
var dicksAreForMy: HTMLAudioElement = <HTMLAudioElement>Sakura.DOM.Create('audio'),
|
||||||
|
friendsWhenThey: HTMLSourceElement = <HTMLSourceElement>Sakura.DOM.Create('source'),
|
||||||
|
comeToMyHouse: HTMLSourceElement = <HTMLSourceElement>Sakura.DOM.Create('source');
|
||||||
|
|
||||||
|
friendsWhenThey.type = 'audio/mp3';
|
||||||
|
comeToMyHouse.type = 'audio/ogg';
|
||||||
|
|
||||||
|
friendsWhenThey.src = 'https://data.flashii.net/assets/sounds/dicks.mp3';
|
||||||
|
comeToMyHouse.src = 'https://data.flashii.net/assets/sounds/dicks.ogg';
|
||||||
|
|
||||||
|
Sakura.DOM.Append(dicksAreForMy, friendsWhenThey);
|
||||||
|
Sakura.DOM.Append(dicksAreForMy, comeToMyHouse);
|
||||||
|
|
||||||
|
dicksAreForMy.play();
|
||||||
|
|
||||||
|
Ybabstat.Illuminati = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Ybabstat.Illuminati[0] === 77 && Ybabstat.Illuminati[1] === 69 && Ybabstat.Illuminati[2] === 87 && Ybabstat.Illuminati[3] === 79 && Ybabstat.Illuminati[4] === 87) {
|
||||||
|
var noklz: HTMLAudioElement = <HTMLAudioElement>Sakura.DOM.Create('audio'),
|
||||||
|
von: HTMLSourceElement = <HTMLSourceElement>Sakura.DOM.Create('source'),
|
||||||
|
schnitzel: HTMLSourceElement = <HTMLSourceElement>Sakura.DOM.Create('source');
|
||||||
|
|
||||||
|
von.type = 'audio/mp3';
|
||||||
|
schnitzel.type = 'audio/ogg';
|
||||||
|
|
||||||
|
von.src = 'https://data.flashii.net/assets/sounds/mewow.mp3';
|
||||||
|
schnitzel.src = 'https://data.flashii.net/assets/sounds/mewow.ogg';
|
||||||
|
|
||||||
|
Sakura.DOM.Append(noklz, von);
|
||||||
|
Sakura.DOM.Append(noklz, schnitzel);
|
||||||
|
|
||||||
|
noklz.play();
|
||||||
|
|
||||||
|
document.body.style.animation = 'spin 5s infinite linear';
|
||||||
|
|
||||||
|
Ybabstat.Illuminati = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Ybabstat.Illuminati[0] == 83 && Ybabstat.Illuminati[1] == 79 && Ybabstat.Illuminati[2] == 67 && Ybabstat.Illuminati[3] == 75 && Ybabstat.Illuminati[4] == 67 && Ybabstat.Illuminati[5] == 72 && Ybabstat.Illuminati[6] == 65 && Ybabstat.Illuminati[7] == 84) {
|
||||||
|
setInterval(Ybabstat.TwoThousandSixteenIsTheYearWePhysicallyMergeWithCats, 20);
|
||||||
|
|
||||||
|
Ybabstat.Illuminati = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,497 +0,0 @@
|
||||||
/*
|
|
||||||
* Sakura Yuuno
|
|
||||||
*/
|
|
||||||
|
|
||||||
declare var Sakura: any;
|
|
||||||
declare var AJAX: any;
|
|
||||||
declare var HTTPMethods: any;
|
|
||||||
declare var sakuraVars: any;
|
|
||||||
|
|
||||||
// Notification class
|
|
||||||
interface Notification {
|
|
||||||
read: boolean;
|
|
||||||
title: string;
|
|
||||||
text: string;
|
|
||||||
link: string;
|
|
||||||
image: string;
|
|
||||||
timeout: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Spawns a notification
|
|
||||||
function notifyUI(content: Notification): void {
|
|
||||||
// Grab the container and create an ID
|
|
||||||
var cont: HTMLElement = document.getElementById('notifications');
|
|
||||||
var id: string = 'sakura-notification-' + Date.now();
|
|
||||||
|
|
||||||
// Create the elements
|
|
||||||
var alert: HTMLDivElement = document.createElement('div');
|
|
||||||
var aIcon: HTMLDivElement = document.createElement('div');
|
|
||||||
var aCont: HTMLDivElement = document.createElement('div');
|
|
||||||
var aTitle: HTMLDivElement = document.createElement('div');
|
|
||||||
var aText: HTMLDivElement = document.createElement('div');
|
|
||||||
var aClose: HTMLDivElement = document.createElement('div');
|
|
||||||
var aCIcon: HTMLDivElement = document.createElement('div');
|
|
||||||
var aClear: HTMLDivElement = document.createElement('div');
|
|
||||||
var aIconCont: any;
|
|
||||||
|
|
||||||
// Add attributes to the main element
|
|
||||||
alert.className = 'notification-enter';
|
|
||||||
alert.id = id;
|
|
||||||
|
|
||||||
// Add the icon
|
|
||||||
if ((typeof content.image).toLowerCase() === 'undefined' || content.image == null || content.image.length < 2) {
|
|
||||||
aIconCont = document.createElement('div');
|
|
||||||
aIconCont.className = 'font-icon fa fa-info fa-4x';
|
|
||||||
} else if (content.image.substr(0, 5) == 'FONT:') {
|
|
||||||
aIconCont = document.createElement('div');
|
|
||||||
aIconCont.className = 'font-icon fa ' + content.image.replace('FONT:', '') + ' fa-4x';
|
|
||||||
} else {
|
|
||||||
aIconCont = document.createElement('img');
|
|
||||||
aIconCont.alt = id;
|
|
||||||
aIconCont.src = content.image;
|
|
||||||
}
|
|
||||||
|
|
||||||
aIcon.appendChild(aIconCont);
|
|
||||||
aIcon.className = 'notification-icon';
|
|
||||||
alert.appendChild(aIcon);
|
|
||||||
|
|
||||||
// Add the content
|
|
||||||
aCont.className = 'notification-content';
|
|
||||||
aTitle.className = 'notification-title';
|
|
||||||
aText.className = 'notifcation-text';
|
|
||||||
aTitle.textContent = content.title;
|
|
||||||
aText.textContent = content.text;
|
|
||||||
|
|
||||||
// Check if a link exists and add if it does
|
|
||||||
if ((typeof content.link).toLowerCase() !== 'undefined' && content.link !== null && content.link.length > 1) {
|
|
||||||
alert.setAttribute('sakurahref', content.link);
|
|
||||||
aCont.setAttribute('onclick', content.link.substr(0, 11) == 'javascript:' ? content.link.substring(11) : 'notifyOpen(this.parentNode.id);');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append stuff
|
|
||||||
aCont.appendChild(aTitle);
|
|
||||||
aCont.appendChild(aText);
|
|
||||||
alert.appendChild(aCont);
|
|
||||||
|
|
||||||
// Add the close button
|
|
||||||
aClose.className = 'notification-close';
|
|
||||||
aClose.setAttribute('onclick', 'notifyClose(this.parentNode.id);');
|
|
||||||
aClose.appendChild(aCIcon);
|
|
||||||
alert.appendChild(aClose);
|
|
||||||
|
|
||||||
// Append the notification to the document
|
|
||||||
cont.appendChild(alert);
|
|
||||||
|
|
||||||
// If keepalive is 0 keep the notification open forever
|
|
||||||
if (content.timeout > 0) {
|
|
||||||
// Set a timeout and close after an amount
|
|
||||||
setTimeout(() => {
|
|
||||||
notifyClose(id);
|
|
||||||
}, content.timeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Closing a notification
|
|
||||||
function notifyClose(id: string): void {
|
|
||||||
// Get the element
|
|
||||||
var e: HTMLElement = document.getElementById(id);
|
|
||||||
|
|
||||||
// Add the animation
|
|
||||||
e.className = 'notification-exit';
|
|
||||||
|
|
||||||
// Remove after 410 ms
|
|
||||||
setTimeout(() => {
|
|
||||||
(new Sakura.DOM(id, Sakura.DOMSelector.ID)).Remove();
|
|
||||||
}, 410);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opening an alerted link
|
|
||||||
function notifyOpen(id: string): void {
|
|
||||||
var sakuraHref: string = document.getElementById(id).getAttribute('sakurahref');
|
|
||||||
|
|
||||||
if ((typeof sakuraHref).toLowerCase() !== 'undefined') {
|
|
||||||
window.location.assign(sakuraHref);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show the full page busy window
|
|
||||||
function ajaxBusyView(show: boolean, message: string = null, type: string = null): void {
|
|
||||||
// Get elements
|
|
||||||
var cont: HTMLElement = document.getElementById('ajaxBusy');
|
|
||||||
var stat: HTMLElement = document.getElementById('ajaxStatus');
|
|
||||||
var anim: HTMLElement = document.getElementById('ajaxAnimate');
|
|
||||||
var body: HTMLElement = document.getElementById('contentwrapper');
|
|
||||||
var icon: string = 'fa fa-4x ';
|
|
||||||
|
|
||||||
// Select the proper icon
|
|
||||||
switch (type) {
|
|
||||||
case 'ok':
|
|
||||||
icon += 'fa-check';
|
|
||||||
break;
|
|
||||||
case 'fail':
|
|
||||||
icon += 'fa-remove';
|
|
||||||
break;
|
|
||||||
case 'busy':
|
|
||||||
default:
|
|
||||||
icon += 'fa-refresh fa-spin';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If request to show the window, build it
|
|
||||||
if (show) {
|
|
||||||
if ((typeof cont).toLowerCase() === 'undefined' || cont === null) {
|
|
||||||
// Container
|
|
||||||
var cCont = document.createElement('div');
|
|
||||||
cCont.className = 'ajax-busy';
|
|
||||||
cCont.id = 'ajaxBusy';
|
|
||||||
|
|
||||||
// Inner
|
|
||||||
var cInner = document.createElement('div');
|
|
||||||
cInner.className = 'ajax-inner';
|
|
||||||
cCont.appendChild(cInner);
|
|
||||||
|
|
||||||
// Desc
|
|
||||||
var cMsg = document.createElement('h2');
|
|
||||||
cMsg.id = 'ajaxStatus';
|
|
||||||
cInner.appendChild(cMsg);
|
|
||||||
|
|
||||||
// Icon
|
|
||||||
var cIco = document.createElement('div');
|
|
||||||
cIco.id = 'ajaxAnimate';
|
|
||||||
cInner.appendChild(cIco);
|
|
||||||
|
|
||||||
// Append to document
|
|
||||||
body.appendChild(cCont);
|
|
||||||
|
|
||||||
// Reassign
|
|
||||||
cont = document.getElementById('ajaxBusy');
|
|
||||||
stat = document.getElementById('ajaxStatus');
|
|
||||||
anim = document.getElementById('ajaxAnimate');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the icon
|
|
||||||
anim.className = icon;
|
|
||||||
|
|
||||||
// Update the message
|
|
||||||
stat.textContent = (message === null ? '' : message);
|
|
||||||
} else {
|
|
||||||
if (cont !== null) {
|
|
||||||
var out: any = setInterval(() => {
|
|
||||||
if (cont.style.opacity === null || cont.style.opacity === "") {
|
|
||||||
cont.style.opacity = "1";
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the value isn't 0 yet subtract by .1
|
|
||||||
if (parseInt(cont.style.opacity) > 0) {
|
|
||||||
cont.style.opacity = (parseInt(cont.style.opacity) - 0.1).toString();
|
|
||||||
} else {
|
|
||||||
(new Sakura.DOM('ajaxBusy', Sakura.DOMSelector.ID)).Remove();
|
|
||||||
clearInterval(out);
|
|
||||||
}
|
|
||||||
}, 10);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Making a post request using AJAX
|
|
||||||
function ajaxPost(url: string, data: Object, callback: Function) {
|
|
||||||
// Create AJAX
|
|
||||||
var request = new Sakura.AJAX;
|
|
||||||
|
|
||||||
// Set url
|
|
||||||
request.SetUrl(url);
|
|
||||||
|
|
||||||
// Add callbacks
|
|
||||||
request.AddCallback(200, function() {
|
|
||||||
callback.call(request.Response())
|
|
||||||
});
|
|
||||||
request.AddCallback(0, function() {
|
|
||||||
ajaxBusyView(false);
|
|
||||||
|
|
||||||
throw "POST Request failed";
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add header
|
|
||||||
request.AddHeader('Content-Type', 'application/x-www-form-urlencoded');
|
|
||||||
|
|
||||||
// Set the post data
|
|
||||||
request.SetSend(data);
|
|
||||||
|
|
||||||
// Make the request
|
|
||||||
request.Start(Sakura.HTTPMethod.POST);
|
|
||||||
|
|
||||||
// Return the AJAX object
|
|
||||||
return request;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert a href attr to an object
|
|
||||||
function prepareAjaxLink(linkId: any, callback: Function, attrs: string = null): void {
|
|
||||||
// Get element
|
|
||||||
var link: HTMLElement = (typeof linkId).toLowerCase() === 'object' ? linkId : document.getElementById(linkId);
|
|
||||||
|
|
||||||
// Catch null
|
|
||||||
if (link === null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the raw HREF value
|
|
||||||
var href: string = link.getAttribute('href');
|
|
||||||
|
|
||||||
// Get the action
|
|
||||||
var action: string = href.split('?')[0];
|
|
||||||
|
|
||||||
// Split the request variables
|
|
||||||
var varEarly: string[] = href.split('?')[1].split('&');
|
|
||||||
|
|
||||||
// Create storage thing
|
|
||||||
var variables: Object = new Object();
|
|
||||||
|
|
||||||
// Split them
|
|
||||||
for (var k in varEarly) {
|
|
||||||
// Split
|
|
||||||
var newVar: string[] = varEarly[k].split('=');
|
|
||||||
|
|
||||||
// Push
|
|
||||||
variables[newVar[0]] = newVar[1];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add ajax=true
|
|
||||||
variables['ajax'] = true;
|
|
||||||
|
|
||||||
// Update link attributes
|
|
||||||
link.setAttribute('href', 'javascript:void(0);');
|
|
||||||
link.setAttribute('onclick', callback + '(\'' + action + '\', JSON.parse(\'' + JSON.stringify(variables) + '\')' + (typeof attrs != 'undefined' ? attrs : '') + ');');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Prepare a form for an AJAX request
|
|
||||||
function prepareAjaxForm(formId: string, message: string, resetCaptcha: boolean = false): void {
|
|
||||||
// Get the form
|
|
||||||
var form: HTMLElement = document.getElementById(formId);
|
|
||||||
|
|
||||||
// Create hidden ajax input
|
|
||||||
var hide: HTMLInputElement = document.createElement('input');
|
|
||||||
|
|
||||||
// Set the attributes
|
|
||||||
hide.name = 'ajax';
|
|
||||||
hide.value = 'true';
|
|
||||||
hide.type = 'hidden';
|
|
||||||
form.appendChild(hide);
|
|
||||||
|
|
||||||
// Update form
|
|
||||||
form.setAttribute('onsubmit', 'submitPost(\'' + form.getAttribute('action') + '\', formToObject(\'' + formId + '\'), true, \'' + (message ? message : 'Please wait...') + '\', ' + (resetCaptcha ? 'true' : 'false') + ');');
|
|
||||||
form.setAttribute('action', 'javascript:void(0);');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Convert form to an object
|
|
||||||
function formToObject(formId: string): Object {
|
|
||||||
// Get the form
|
|
||||||
var form: any = document.getElementById(formId);
|
|
||||||
|
|
||||||
// Make an object for the request parts
|
|
||||||
var requestParts: Object = new Object();
|
|
||||||
|
|
||||||
// Get all the children with a name attr
|
|
||||||
var children = form.querySelectorAll('[name]');
|
|
||||||
|
|
||||||
// Sort the children and make them ready for submission
|
|
||||||
for (var i in children) {
|
|
||||||
if ((typeof children[i]).toLowerCase() === 'object') {
|
|
||||||
requestParts[children[i].name] = ((typeof children[i].type !== "undefined" && children[i].type.toLowerCase() == "checkbox") ? (children[i].checked ? 1 : 0) : children[i].value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Return the request parts
|
|
||||||
return requestParts;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Quickly building a form
|
|
||||||
function generateForm(formId: string, formAttr: Object, formData: Object, appendTo: string = null): HTMLFormElement {
|
|
||||||
// Create form element
|
|
||||||
var form: HTMLFormElement = document.createElement('form');
|
|
||||||
form.id = formId;
|
|
||||||
|
|
||||||
// Set additional attrs
|
|
||||||
for (var c in formAttr) {
|
|
||||||
form.setAttribute(c, formAttr[c]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set data
|
|
||||||
for (var a in formData) {
|
|
||||||
var b: HTMLInputElement = document.createElement('input');
|
|
||||||
b.type = 'hidden';
|
|
||||||
b.name = a;
|
|
||||||
b.value = formData[a];
|
|
||||||
form.appendChild(b);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Append to something if requested
|
|
||||||
if (appendTo !== null) {
|
|
||||||
document.getElementById(appendTo).appendChild(form);
|
|
||||||
}
|
|
||||||
|
|
||||||
return form;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Submitting a post using AJAX
|
|
||||||
function submitPost(action: string, requestParts: Object, busyView: boolean, msg: string, resetCaptcha: boolean): void {
|
|
||||||
// If requested display the busy thing
|
|
||||||
if (busyView) {
|
|
||||||
ajaxBusyView(true, msg, 'busy');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Submit the AJAX
|
|
||||||
var request = ajaxPost(action, requestParts, () => {
|
|
||||||
submitPostHandler(request.response(), busyView, resetCaptcha);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handling a submitted form using AJAX
|
|
||||||
function submitPostHandler(data: string, busyView: boolean, resetCaptcha: boolean): void {
|
|
||||||
// Split the result
|
|
||||||
var result: string[] = data.split('|');
|
|
||||||
|
|
||||||
// If using the bust view thing update the text displayed to the return of the request
|
|
||||||
if (busyView) {
|
|
||||||
ajaxBusyView(true, result[0], (result[1] == '1' ? 'ok' : 'fail'));
|
|
||||||
}
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
if (busyView) {
|
|
||||||
ajaxBusyView(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (result[1] == '1') {
|
|
||||||
window.location.assign(result[2]);
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if a password is within the minimum entropy value
|
|
||||||
function checkPwdEntropy(pwd: string): boolean {
|
|
||||||
return (Sakura.Legacy.entropy(pwd) >= sakuraVars.minPwdEntropy);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check registration variables
|
|
||||||
function registerVarCheck(id: string, mode: string, option: any = null): void {
|
|
||||||
// Get the element we're working with
|
|
||||||
var input: any = document.getElementById(id);
|
|
||||||
var check: boolean = null;
|
|
||||||
|
|
||||||
// Use the proper mode
|
|
||||||
switch (mode) {
|
|
||||||
case 'confirmpw':
|
|
||||||
option = document.getElementById(option);
|
|
||||||
check = input.value === option.value;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'password':
|
|
||||||
check = checkPwdEntropy(input.value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'email':
|
|
||||||
check = Sakura.Legacy.validateEmail(input.value);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'username':
|
|
||||||
default:
|
|
||||||
check = Sakura.Legacy.stringLength(input.value, sakuraVars.minUserLen, sakuraVars.maxUserLen);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.className.indexOf(check ? 'green' : 'red') < 0) {
|
|
||||||
input.className = input.className + ' ' + (check ? 'green' : 'red');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (input.className.indexOf(check ? 'red' : 'green') > 0) {
|
|
||||||
input.className = input.className.replace(check ? 'red' : 'green', '');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Replace some special tags
|
|
||||||
function replaceTag(tag: string): string {
|
|
||||||
return { '&': '&', '<': '<', '>': '>' }[tag] || tag;
|
|
||||||
}
|
|
||||||
|
|
||||||
// ^
|
|
||||||
function safeTagsReplace(str: string): string {
|
|
||||||
return str.replace(/[&<>]/g, replaceTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserting text into text box
|
|
||||||
// Borrowed from http://stackoverflow.com/questions/1064089/inserting-a-text-where-cursor-is-using-javascript-jquery (therefore not in Typescript format, fix this later)
|
|
||||||
function insertText(areaId, text) {
|
|
||||||
var txtarea: any = document.getElementById(areaId);
|
|
||||||
var scrollPos: any = txtarea.scrollTop;
|
|
||||||
var strPos = 0;
|
|
||||||
var br = ((txtarea.selectionStart || txtarea.selectionStart == '0') ?
|
|
||||||
"ff" : ((<any>document).selection ? "ie" : false));
|
|
||||||
if (br == "ie") {
|
|
||||||
txtarea.focus();
|
|
||||||
var range = (<any>document).selection.createRange();
|
|
||||||
range.moveStart('character', -txtarea.value.length);
|
|
||||||
strPos = range.text.length;
|
|
||||||
}
|
|
||||||
else if (br == "ff") strPos = txtarea.selectionStart;
|
|
||||||
|
|
||||||
var front = (txtarea.value).substring(0, strPos);
|
|
||||||
var back = (txtarea.value).substring(strPos, txtarea.value.length);
|
|
||||||
txtarea.value = front + text + back;
|
|
||||||
strPos = strPos + text.length;
|
|
||||||
if (br == "ie") {
|
|
||||||
txtarea.focus();
|
|
||||||
var range = (<any>document).selection.createRange();
|
|
||||||
range.moveStart('character', -txtarea.value.length);
|
|
||||||
range.moveStart('character', strPos);
|
|
||||||
range.moveEnd('character', 0);
|
|
||||||
range.select();
|
|
||||||
}
|
|
||||||
else if (br == "ff") {
|
|
||||||
txtarea.selectionStart = strPos;
|
|
||||||
txtarea.selectionEnd = strPos;
|
|
||||||
txtarea.focus();
|
|
||||||
}
|
|
||||||
txtarea.scrollTop = scrollPos;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Inserting a bbcode
|
|
||||||
function insertBBcode(textarea: string, tag: string, arg: boolean = false): void {
|
|
||||||
var element: any = document.getElementById(textarea);
|
|
||||||
var before = "[" + tag + (arg ? "=" : "") + "]";
|
|
||||||
var after = "[/" + tag + "]";
|
|
||||||
|
|
||||||
if ((<any>document).selection) {
|
|
||||||
element.focus();
|
|
||||||
var sel = (<any>document).selection.createRange();
|
|
||||||
sel.text = before + sel.text + after;
|
|
||||||
element.focus();
|
|
||||||
} else if (element.selectionStart || element.selectionStart === 0) {
|
|
||||||
var startPos = element.selectionStart;
|
|
||||||
var endPos = element.selectionEnd;
|
|
||||||
var scrollTop = element.scrollTop;
|
|
||||||
element.value = element.value.substring(0, startPos) + before + element.value.substring(startPos, endPos) + after + element.value.substring(endPos, element.value.length);
|
|
||||||
element.focus();
|
|
||||||
element.selectionStart = startPos + before.length;
|
|
||||||
element.selectionEnd = endPos + before.length;
|
|
||||||
element.scrollTop = scrollTop;
|
|
||||||
} else {
|
|
||||||
element.value += before + after;
|
|
||||||
element.focus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface Number {
|
|
||||||
formatMoney(u, c, k);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Formatting money
|
|
||||||
Number.prototype.formatMoney = function(u, c, k) {
|
|
||||||
var f = this,
|
|
||||||
u = isNaN(u = Math.abs(u)) ? 2 : u,
|
|
||||||
c = c == undefined ? "." : c,
|
|
||||||
k = k == undefined ? "," : k,
|
|
||||||
i = f < 0 ? "-" : "",
|
|
||||||
n: any = parseInt(f = Math.abs(+f || 0).toFixed(u)) + "",
|
|
||||||
g = (g = n.length) > 3 ? g % 3 : 0;
|
|
||||||
|
|
||||||
return i + (g ? n.substr(0, g) + k : "") + n.substr(g).replace(/(\c{3})(?=\c)/g, "$1" + k) + (u ? c + Math.abs(f - n).toFixed(u).slice(2) : "");
|
|
||||||
};
|
|
|
@ -18,7 +18,7 @@
|
||||||
{#<script>
|
{#<script>
|
||||||
window["sakurakoSettings"] = route('chat.settings');
|
window["sakurakoSettings"] = route('chat.settings');
|
||||||
</script>#}
|
</script>#}
|
||||||
<script src="/js/libraries.js" data-turbolinks-track></script>
|
<!-- we'll disable turbolinks for now script src="/js/libraries.js" data-turbolinks-track></script-->
|
||||||
<script src="/js/aitemu.js" data-turbolinks-track></script>
|
<script src="/js/aitemu.js" data-turbolinks-track></script>
|
||||||
{#<script src="{{ config('general.chat') }}/app.js"></script>#}
|
{#<script src="{{ config('general.chat') }}/app.js"></script>#}
|
||||||
{{ block('js') }}
|
{{ block('js') }}
|
||||||
|
@ -29,7 +29,7 @@
|
||||||
<nav class="header">
|
<nav class="header">
|
||||||
<div class="header__navigation">
|
<div class="header__navigation">
|
||||||
<a class="header__entry fa-home" href="{{ route('main.index') }}">home</a>
|
<a class="header__entry fa-home" href="{{ route('main.index') }}">home</a>
|
||||||
<a class="header__entry fa-comments" href="{{ forums.index }}">forum</a>
|
<a class="header__entry fa-comments" href="{{ route('forums.index') }}">forum</a>
|
||||||
<a class="header__entry fa-commenting" href="{{ route('chat.redirect') }}">chat</a>
|
<a class="header__entry fa-commenting" href="{{ route('chat.redirect') }}">chat</a>
|
||||||
<a class="header__entry fa-sign-out" href="{{ route('auth.logout') }}">logout</a>
|
<a class="header__entry fa-sign-out" href="{{ route('auth.logout') }}">logout</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
# To make sure Git keeps this folder here
|
|
|
@ -25,19 +25,19 @@
|
||||||
<label for="registerUserName">Username:</label>
|
<label for="registerUserName">Username:</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="centreAlign">
|
<div class="centreAlign">
|
||||||
<input class="inputStyling" type="text" id="registerUserName" name="username" onkeyup="registerVarCheck(this.id, 'username');" placeholder="Any character"{% if get.username %} value="{{ get.username }}"{% endif %}>
|
<input class="inputStyling" type="text" id="registerUserName" name="username" placeholder="Any character"{% if get.username %} value="{{ get.username }}"{% endif %}>
|
||||||
</div>
|
</div>
|
||||||
<div class="leftAlign">
|
<div class="leftAlign">
|
||||||
<label for="registerEmail">E-mail:</label>
|
<label for="registerEmail">E-mail:</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="centreAlign">
|
<div class="centreAlign">
|
||||||
<input class="inputStyling" type="text" id="registerEmail" name="email" onkeyup="registerVarCheck(this.id, 'email');" placeholder="Used for e.g. password retrieval"{% if get.email %} value="{{ get.email }}"{% endif %}>
|
<input class="inputStyling" type="text" id="registerEmail" name="email" placeholder="Used for e.g. password retrieval"{% if get.email %} value="{{ get.email }}"{% endif %}>
|
||||||
</div>
|
</div>
|
||||||
<div class="leftAlign">
|
<div class="leftAlign">
|
||||||
<label for="registerPassword">Password:</label>
|
<label for="registerPassword">Password:</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="centreAlign">
|
<div class="centreAlign">
|
||||||
<input class="inputStyling" type="password" id="registerPassword" name="password" onkeyup="registerVarCheck(this.id, 'password');" placeholder="Using special characters is recommended">
|
<input class="inputStyling" type="password" id="registerPassword" name="password" placeholder="Using special characters is recommended">
|
||||||
</div>
|
</div>
|
||||||
<div class="centreAlign">
|
<div class="centreAlign">
|
||||||
<input class="inputStyling" type="submit" name="submit" value="Register" id="registerAccBtn">
|
<input class="inputStyling" type="submit" name="submit" value="Register" id="registerAccBtn">
|
||||||
|
|
|
@ -44,11 +44,7 @@
|
||||||
var resp = JSON.parse(commentClient.Response());
|
var resp = JSON.parse(commentClient.Response());
|
||||||
|
|
||||||
if (resp.error) {
|
if (resp.error) {
|
||||||
ajaxBusyView(true, resp.error, 'fail');
|
Yuuno.Busy.Show(Yuuno.BusyMode.FAIL, resp.error, 1500);
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
ajaxBusyView(false);
|
|
||||||
}, 1500);
|
|
||||||
} else {
|
} else {
|
||||||
commentAdd(resp);
|
commentAdd(resp);
|
||||||
}
|
}
|
||||||
|
@ -197,7 +193,7 @@
|
||||||
|
|
||||||
// Remove it if it already exists
|
// Remove it if it already exists
|
||||||
if (replyBox) {
|
if (replyBox) {
|
||||||
(new Sakura.DOM('comment-reply-container-' + id, Sakura.DOMSelector.ID)).Remove();
|
Sakura.DOM.Remove('comment-reply-container-' + id);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -256,13 +252,9 @@
|
||||||
var resp = JSON.parse(commentClient.Response());
|
var resp = JSON.parse(commentClient.Response());
|
||||||
|
|
||||||
if (resp.error) {
|
if (resp.error) {
|
||||||
ajaxBusyView(true, resp.error, 'fail');
|
Yuuno.Busy.Show(Yuuno.BusyMode.FAIL, resp.error, 1500);
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
ajaxBusyView(false);
|
|
||||||
}, 1500);
|
|
||||||
} else {
|
} else {
|
||||||
(new Sakura.DOM("comment-" + id, Sakura.DOMSelector.ID)).Remove();
|
Sakura.DOM.Remove('comment-' + id);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -282,11 +274,7 @@
|
||||||
var resp = JSON.parse(commentClient.Response());
|
var resp = JSON.parse(commentClient.Response());
|
||||||
|
|
||||||
if (resp.error) {
|
if (resp.error) {
|
||||||
ajaxBusyView(true, resp.error, 'fail');
|
Yuuno.Busy.Show(Yuuno.BusyMode.FAIL, resp.error, 1500);
|
||||||
|
|
||||||
setTimeout(function () {
|
|
||||||
ajaxBusyView(false);
|
|
||||||
}, 1500);
|
|
||||||
} else {
|
} else {
|
||||||
upvotes.innerText = resp.upvotes;
|
upvotes.innerText = resp.upvotes;
|
||||||
downvotes.innerText = resp.downvotes;
|
downvotes.innerText = resp.downvotes;
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
<div class="posting-buttons">
|
<div class="posting-buttons">
|
||||||
<div style="float: left;">
|
<div style="float: left;">
|
||||||
{% for code,meta in bbcode %}
|
{% for code,meta in bbcode %}
|
||||||
<button onclick="insertBBcode('postingText', '{{ code }}'{% if meta[2] %}, true{% endif %});" type="button"{% if meta[0] %} title="{{ meta[0] }}"{% endif %} class="forumbtn{% if meta[1] %} fa fa-{{ meta[1] }}{% endif %}">{% if not meta[1] %}{{ code }}{% endif %}</button>
|
<button onclick="Yuuno.Editor.InsertBBCode(Sakura.DOM.ID('postingText'), '{{ code }}'{% if meta[2] %}, true{% endif %});" type="button"{% if meta[0] %} title="{{ meta[0] }}"{% endif %} class="forumbtn{% if meta[1] %} fa fa-{{ meta[1] }}{% endif %}">{% if not meta[1] %}{{ code }}{% endif %}</button>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
<div style="float: right;">
|
<div style="float: right;">
|
||||||
|
|
|
@ -18,37 +18,17 @@
|
||||||
|
|
||||||
<script type="text/javascript" src="/js/app.js"></script>
|
<script type="text/javascript" src="/js/app.js"></script>
|
||||||
<script type="text/javascript" src="/js/yuuno.js"></script>
|
<script type="text/javascript" src="/js/yuuno.js"></script>
|
||||||
<script type="text/javascript">
|
|
||||||
// Create an object so we can access certain settings from remote JavaScript files
|
|
||||||
var sakuraVars = {
|
|
||||||
"cookie": {
|
|
||||||
"prefix": "{{ config('cookie.prefix') }}"
|
|
||||||
},
|
|
||||||
|
|
||||||
"siteName": "{{ config('general.name') }}",
|
|
||||||
"content": "",
|
|
||||||
"recaptchaEnabled": false,
|
|
||||||
|
|
||||||
"minUserLen": {{ config('user.name_min') }},
|
|
||||||
"maxUserLen": {{ config('user.name_max') }},
|
|
||||||
"minPwdEntropy": {{ config('user.pass_min_entropy') }},
|
|
||||||
"checkLogin": {{ user.isActive ? 'true' : 'false' }}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Error reporter
|
|
||||||
window.onerror = function(msg, url, line, col, error) {
|
|
||||||
notifyUI({
|
|
||||||
"title": "An error has occurred!",
|
|
||||||
"text": "There was a problem while executing the JavaScript code for this page: " + msg + ", URL: " + url + ", Line: " + line + ", Column: " + col + ". Please report this to a developer.",
|
|
||||||
"image": "FONT:fa-warning"
|
|
||||||
});
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
{{ block('js') }}
|
{{ block('js') }}
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div id="container">
|
<div id="container">
|
||||||
<span id="top"></span>
|
<span id="top"></span>
|
||||||
|
<div id="busy-window" class="ajax-busy hidden">
|
||||||
|
<div class="ajax-inner">
|
||||||
|
<h2 id="busy-status"></h2>
|
||||||
|
<div class="fa fa-4x fa-refresh fa-spin" id="busy-icon"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="header" id="header">
|
<div class="header" id="header">
|
||||||
<a class="logo" href="{{ route('main.index') }}">{% if config('general.logo') %}<img src="{{ config('general.logo') }}" alt="{{ config('general.name') }}">{% else %}{{ config('general.name') }}{% endif %}</a>
|
<a class="logo" href="{{ route('main.index') }}">{% if config('general.logo') %}<img src="{{ config('general.logo') }}" alt="{{ config('general.name') }}">{% else %}{{ config('general.name') }}{% endif %}</a>
|
||||||
<div class="menu fa">
|
<div class="menu fa">
|
||||||
|
@ -154,7 +134,7 @@
|
||||||
<li class="fthead">Information</li>
|
<li class="fthead">Information</li>
|
||||||
<li><a href="{{ route('main.faq') }}">FAQ</a></li>
|
<li><a href="{{ route('main.faq') }}">FAQ</a></li>
|
||||||
<li><a href="{{ route('info.rules') }}">Rules</a></li>
|
<li><a href="{{ route('info.rules') }}">Rules</a></li>
|
||||||
<li><a href="//fiistat.us" target="_blank">Server Status</a></li>
|
<li><a href="{{ route('status.index') }}">Server Status</a></li>
|
||||||
<li><a href="{{ route('info.terms') }}">Terms of Service</a></li>
|
<li><a href="{{ route('info.terms') }}">Terms of Service</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
@ -163,9 +143,6 @@
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
Sakura.Config.Set({
|
Sakura.Config.Set({
|
||||||
Revision: {{ constant('SAKURA_VERSION') }},
|
Revision: {{ constant('SAKURA_VERSION') }},
|
||||||
UserNameMinLength: {{ config('user.name_min') }},
|
|
||||||
UserNameMaxLength: {{ config('user.name_max') }},
|
|
||||||
PasswordMinEntropy: {{ config('user.pass_min_entropy') }},
|
|
||||||
LoggedIn: {{ user.isActive ? 'true' : 'false' }},
|
LoggedIn: {{ user.isActive ? 'true' : 'false' }},
|
||||||
UserId: {{ user.id }},
|
UserId: {{ user.id }},
|
||||||
SessionId: "{{ session_id() }}",
|
SessionId: "{{ session_id() }}",
|
||||||
|
@ -173,8 +150,20 @@
|
||||||
|
|
||||||
Yuuno.Main.Startup();
|
Yuuno.Main.Startup();
|
||||||
|
|
||||||
{% if config('dev.show_changelog', true) and stats %}
|
{% if config('dev.show_changelog') and stats %}
|
||||||
Sakura.Changelog.Build(new Sakura.DOM('indexPanel', Sakura.DOMSelector.ID));
|
Sakura.Changelog.Build(Sakura.DOM.ID('indexPanel'));
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if config('dev.show_errors') %}
|
||||||
|
window.addEventListener("error", function () {
|
||||||
|
Yuuno.Notifications.Display({
|
||||||
|
id: -(Date.now()),
|
||||||
|
title: "Something happened!",
|
||||||
|
text: "Check the console for more info.",
|
||||||
|
image: "FONT:fa-bomb",
|
||||||
|
link: null,
|
||||||
|
});
|
||||||
|
});
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -14,7 +14,3 @@
|
||||||
<div class="clear"></div>
|
<div class="clear"></div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block js %}
|
|
||||||
<script type="text/javascript" src="/js/ybabstat.js"></script>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
|
@ -68,14 +68,14 @@
|
||||||
</div>
|
</div>
|
||||||
{% if user.isActive and user.permission(constant('Sakura\\Perms\\Site::OBTAIN_PREMIUM')) %}
|
{% if user.isActive and user.permission(constant('Sakura\\Perms\\Site::OBTAIN_PREMIUM')) %}
|
||||||
<div class="slider">
|
<div class="slider">
|
||||||
<input class="inputStyling" type="range" min="1" max="{{ amountLimit }}" value="1" onchange="document.getElementById('monthsNo').value = this.value; document.getElementById('monthNoBtn').innerHTML = this.value; document.getElementById('monthsTrailingS').innerHTML = (this.value == 1 ? '' : 's'); document.getElementById('totalAmount').innerHTML = (this.value * {{ price }}).formatMoney(2);">
|
<input class="inputStyling" type="range" min="1" max="{{ amountLimit }}" value="1" onchange="document.getElementById('monthsNo').value = this.value; document.getElementById('monthNoBtn').innerText = this.value; document.getElementById('monthsTrailingS').innerText = (this.value == 1 ? '' : 's'); document.getElementById('totalAmount').innerText = (this.value * {{ price }});">
|
||||||
</div>
|
</div>
|
||||||
<div class="checkout" style="line-height: 28px;">
|
<div class="checkout" style="line-height: 28px;">
|
||||||
<div style="float: left;">
|
<div style="float: left;">
|
||||||
<h1 class="stylised">Total: €<span id="totalAmount"></span></h1>
|
<h1 class="stylised">Total: €<span id="totalAmount"></span></h1>
|
||||||
</div>
|
</div>
|
||||||
<div style="float: right;">
|
<div style="float: right;">
|
||||||
<button class="inputStyling" onclick="ajaxBusyView(true, 'Please wait...');document.getElementById('purchaseForm').submit();">Get <span id="monthNoBtn">1</span> month<span id="monthsTrailingS"></span> of Tenshi</button>
|
<button class="inputStyling" onclick="Yuuno.Busy.Show(Yuuno.BusyMode.BUSY, 'Please wait...');document.getElementById('purchaseForm').submit();">Get <span id="monthNoBtn">1</span> month<span id="monthsTrailingS"></span> of Tenshi</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="clear"></div>
|
<div class="clear"></div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -93,7 +93,7 @@
|
||||||
<input type="hidden" name="months" id="monthsNo" value="1">
|
<input type="hidden" name="months" id="monthsNo" value="1">
|
||||||
</form>
|
</form>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
window.onload = function() { document.getElementById('totalAmount').innerHTML = ({{ price }}).formatMoney(2); };
|
window.onload = function() { document.getElementById('totalAmount').innerText = {{ price }}; };
|
||||||
</script>
|
</script>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
16
resources/views/yuuno/status/index.twig
Normal file
16
resources/views/yuuno/status/index.twig
Normal file
|
@ -0,0 +1,16 @@
|
||||||
|
{% extends '@aitemu/master.twig' %}
|
||||||
|
|
||||||
|
{% set title = 'Status' %}
|
||||||
|
{% set banner = '/images/status-banner.png' %}
|
||||||
|
{% set banner_large = true %}
|
||||||
|
|
||||||
|
{% block banner_content %}
|
||||||
|
{# ignore this, it's temporary #}
|
||||||
|
<div style="display:flex;width:100%;height:100%;"><div class="fa-4x" style="align-self:center;padding:1em"><span class="fa fa-check"></span> I can write in here too!</div></div>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<div class="platform">
|
||||||
|
all services are operational and i haven't designed this yet
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
|
@ -104,6 +104,11 @@ Router::group(['before' => 'maintenance'], function () {
|
||||||
Router::get('/welcome', 'InfoController@welcome', 'info.welcome');
|
Router::get('/welcome', 'InfoController@welcome', 'info.welcome');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Status
|
||||||
|
Router::group(['prefix' => 'status'], function () {
|
||||||
|
Router::get('/', 'StatusController@index', 'status.index');
|
||||||
|
});
|
||||||
|
|
||||||
// News
|
// News
|
||||||
Router::group(['prefix' => 'news'], function () {
|
Router::group(['prefix' => 'news'], function () {
|
||||||
Router::get('/{category:c}?', 'NewsController@category', 'news.category');
|
Router::get('/{category:c}?', 'NewsController@category', 'news.category');
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
namespace Sakura;
|
namespace Sakura;
|
||||||
|
|
||||||
// Define version and root path
|
// Define version and root path
|
||||||
define('SAKURA_VERSION', 20160730);
|
define('SAKURA_VERSION', 20160804);
|
||||||
define('ROOT', __DIR__ . '/');
|
define('ROOT', __DIR__ . '/');
|
||||||
|
|
||||||
// CLI mode
|
// CLI mode
|
||||||
|
|
|
@ -4,7 +4,6 @@
|
||||||
"noImplicitAny": false,
|
"noImplicitAny": false,
|
||||||
"removeComments": true,
|
"removeComments": true,
|
||||||
"preserveConstEnums": true,
|
"preserveConstEnums": true,
|
||||||
"moduleResolution": "node",
|
|
||||||
"rootDir": "resources/assets/typescript"
|
"rootDir": "resources/assets/typescript"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue