Added base CBDL files. At the moment, CBDL.Loop is broken and CBDL.Includes are hacked - to be fixed, but initial commits are important!
commit
2820319212
|
@ -0,0 +1,450 @@
|
|||
/* **** CBDL - Cross-browser DirectMedia Layer
|
||||
*
|
||||
* look at: http://en.wikipedia.org/wiki/SFML
|
||||
*/
|
||||
var CBDL = CBDL || {};
|
||||
|
||||
CBDL.version = 0.1;
|
||||
|
||||
CBDL.dump = function() {
|
||||
console.log("CBDL version "+CBDL.version);
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
CBDL.Includes
|
||||
|
||||
Object that manages includes for an App. Attempts to load all includes
|
||||
specified by the App, providing they are provided prior to the App's onExecute
|
||||
member function being called.
|
||||
===============================================================================
|
||||
*/
|
||||
CBDL.Includes = function(app) {
|
||||
_Includes = this;
|
||||
this.app = app;
|
||||
this.state = 0x00;
|
||||
this.current = 0;
|
||||
this.includes = [];
|
||||
|
||||
this.add = function(some_library) {
|
||||
if (this.state != CBDL.states.LOADED) {
|
||||
this.includes.push(some_library);
|
||||
if (this.state != CBDL.states.LOADING) {
|
||||
this.load();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
this.load = function() {
|
||||
this.state = CBDL.states.LOADING;
|
||||
if (this.current < this.includes.length) {
|
||||
this.netLoad();
|
||||
} else {
|
||||
this.state = CBDL.states.LOADED;
|
||||
this.app.includeCallback();
|
||||
}
|
||||
};
|
||||
|
||||
this.netLoad = function() {
|
||||
var element = document.createElement("script");
|
||||
element.type = "text/javascript";
|
||||
element.src = _Includes.includes[_Includes.current];
|
||||
element.onload = function() {
|
||||
_Includes.state = 0x00;
|
||||
_Includes.current++;
|
||||
_Includes.load(); // load next file
|
||||
}
|
||||
document.getElementsByTagName('head')[0].appendChild(element);
|
||||
// if (!/*@cc_on!@*/false) {
|
||||
/* var request = new XMLHttpRequest();
|
||||
} else {
|
||||
var request = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
request.onreadystatechange = function() {
|
||||
if (this.readyState == 4) { // done loading
|
||||
console.log(this.status);
|
||||
if (this.status == 200) {
|
||||
var element = document.createElement("script");
|
||||
element.type = "text/javascript";
|
||||
element.text = this.responseText;
|
||||
document.getElementsByTagName('head')[0].appendChild(element);
|
||||
_Includes.state = 0x00;
|
||||
_Includes.current++;
|
||||
_Includes.load(); // load next file
|
||||
} else {
|
||||
_Includes.state = CBDL.states.FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
request.open('GET', _Includes.includes[_Includes.current], true);
|
||||
request.send(null);
|
||||
*/
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
CBDL.App
|
||||
|
||||
Object that should be the base for all CBDL-based applications. Contains the
|
||||
bare-minimum logic, members, and vars that any CBDL app should hold.
|
||||
|
||||
Usage:
|
||||
some_app = new CBDL.App();
|
||||
some_app.Init();
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
CBDL.App = CBDL.App || function() {};
|
||||
|
||||
CBDL.App.prototype.Main = function() {};
|
||||
CBDL.App.prototype.Go = function() {
|
||||
if (this.requires) {
|
||||
this._includes = new CBDL.Includes(this);
|
||||
for (var require in this.requires) {
|
||||
this._includes.add(this.requires[require]);
|
||||
}
|
||||
}
|
||||
if (this._includes) {
|
||||
if (this._includes.state != CBDL.states.LOADED) {
|
||||
this.includeCallback = function(return_value) {
|
||||
if (return_value < 0) { // fail
|
||||
console.log("CBDL error: includes failed to load!");
|
||||
return return_value;
|
||||
}
|
||||
this.Main();
|
||||
};
|
||||
} else if (this._includes.state == CBDL.states.LOADED) {
|
||||
this.Main();
|
||||
}
|
||||
} else {
|
||||
this.Main();
|
||||
}
|
||||
};
|
||||
CBDL.App.prototype.include = function(some_library) {
|
||||
if (!this._includes) {
|
||||
this._includes = new CBDL.Includes(this);
|
||||
}
|
||||
this._includes.add(some_library);
|
||||
};
|
||||
/* called when all includes succeed or fail */
|
||||
CBDL.App.prototype.includeCallback = function(return_value) {
|
||||
if (return_value < 0) { // fail
|
||||
console.log("CBDL error: includes failed to load!");
|
||||
return return_value;
|
||||
} else {
|
||||
this.Main();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
================
|
||||
CBDL.inherit(baseClass, newClass)
|
||||
|
||||
Function that inherits the properties from baseClass into newClass
|
||||
================
|
||||
*/
|
||||
CBDL.inherit = function(base_class, new_class) {
|
||||
base_class.call(new_class);
|
||||
new_class.constructor = new_class;
|
||||
}
|
||||
CBDL.inherits = function(base_class, new_class) {
|
||||
new_class.prototype = new base_class;
|
||||
new_class.prototype.constructor = base_class; // fix constructor
|
||||
}
|
||||
|
||||
CBDL.extends = function(base_class, new_class) {
|
||||
new_class.prototype = new base_class;
|
||||
new_class.prototype.constructor = base_class; // fix constructor
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
================
|
||||
CBDL.startApp(function)
|
||||
|
||||
Function which is used to start the CBDL-based application that is passed
|
||||
as its argument. The application, being an Object, requires, at bare minimum,
|
||||
onInit(), onExecute(), and onLoop() member functions. This function then calls
|
||||
onInit(), and if all goes well, to call onExecute().
|
||||
What this effectively accomplishes is header/defines in the onInit, and a
|
||||
base main() call in the onExecute().
|
||||
|
||||
Usage:
|
||||
someApp = function() {
|
||||
this.onInit = function() {
|
||||
include("some_library.js");
|
||||
};
|
||||
this.onExecute = function() {
|
||||
console.log("some_app executed!");
|
||||
};
|
||||
}
|
||||
CBDL.startApp(new someApp);
|
||||
================
|
||||
*/
|
||||
CBDL.startApp = function(app) {
|
||||
if (!app.onInit || !app.onExecute) {
|
||||
console.log("CBDL error: missing onInit or onExecute app member!");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
CBDL.Loop(callback, interval)
|
||||
|
||||
Object that acts as a simple interface to creating and managing loops within
|
||||
the script. Attempts to call callback() after X interval has passed. Interval
|
||||
is modified internally to adjust for the time callback() takes to finish. If
|
||||
the time is beyond the defined interval, then callback() is called with a 0ms
|
||||
delay.
|
||||
If the interval is not passed, or is passed as 0, then the Loop will rely
|
||||
on the callback() to return an interval value.
|
||||
|
||||
Usage:
|
||||
new_loop = new CBDL.Loop(some_func, 50);
|
||||
new_loop.start(50); // calls some_func() after 50ms and repeats every 50ms.
|
||||
Or:
|
||||
some_func = function() { return(1000); };
|
||||
new_loop = new CBDL.Loop(some_func);
|
||||
new_loop.start(); // calls some_func() every 1000ms.
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
CBDL.Loop = function(callback, interval) {
|
||||
_Loop = this;
|
||||
this.callback = callback;
|
||||
this.interval = (interval ? interval : 0);
|
||||
|
||||
this.time_start = 0;
|
||||
this.time_end = 0;
|
||||
this.time_delay = this.interval;
|
||||
|
||||
this.running = false;
|
||||
this.start = function(delay) {
|
||||
this.running = true;
|
||||
|
||||
if (delay) {
|
||||
setTimeout(function() { _Loop.loop(); }, delay);
|
||||
} else {
|
||||
this.loop();
|
||||
}
|
||||
};
|
||||
this.stop = function() {
|
||||
this.running = false;
|
||||
};
|
||||
this.loop = function() {
|
||||
if (this.running) {
|
||||
if (this.interval == 0) { // acquire time_delay from callback return
|
||||
this.time_delay = this.callback();
|
||||
} else {
|
||||
this.time_start = new Date().getTime();
|
||||
this.callback(this.time_start-this.time_end); // return delta to callback
|
||||
this.time_end = new Date().getTime();
|
||||
this.time_delay = this.time_end-this.time_start;
|
||||
this.time_delay = (this.time_delay > this.interval ? 0 : this.interval - this.time_delay);
|
||||
}
|
||||
setTimeout(function() { _Loop.loop(); }, this.time_delay);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CBDL.newLoop(callback, interval)
|
||||
|
||||
Creates and returns a new Loop object. Once the Loop's start member is
|
||||
called, CBDL continually calls the callback at the specified interval
|
||||
times.
|
||||
|
||||
Usage:
|
||||
new_loop = CBDL.newLoop(function() { console.log("test"); }, 500);
|
||||
new_loop.start(); // begins printing "test" to the console every 500ms
|
||||
================
|
||||
*/
|
||||
CBDL.newLoop = function(callback, interval) {
|
||||
if (callback instanceof Object) {
|
||||
return new CBDL.Loop(callback, interval);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CBDL.useNamespace(object)
|
||||
|
||||
Sets the window parent to own all of the defined object's members. Functions
|
||||
in a fashion similar to setting the namespace in C++.
|
||||
|
||||
Usage:
|
||||
CBDL.dump(); // prints CBDL version
|
||||
CBDL.useNamespace(CBDL);
|
||||
dump(); // prints CBDL version
|
||||
================
|
||||
*/
|
||||
CBDL.useNamespace = function(object) {
|
||||
var parent = window;
|
||||
for (var member in object) {
|
||||
parent[member] = object[member];
|
||||
}
|
||||
}
|
||||
/*
|
||||
================
|
||||
CBDL.globalize(name, variable)
|
||||
|
||||
Adds the passed variable/function/object to a new var in the "global"
|
||||
namespace via the passed name.
|
||||
|
||||
Usage:
|
||||
some_function() {
|
||||
var foo = 'bar';
|
||||
}
|
||||
console.log(foo); // undefined
|
||||
some_function() {
|
||||
var foo = 'bar';
|
||||
CBDL.globalize("foo", foo); // window.foo = 'bar', effectively
|
||||
}
|
||||
console.log(foo); // prints 'bar'
|
||||
================
|
||||
*/
|
||||
CBDL.globalize = function(name, variable) {
|
||||
var parent = window;
|
||||
parent[name] = parent[name] || variable;
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
CBDL.Include(filename)
|
||||
|
||||
Internal object created by calls to CBDL.include(filename). Simply stores the
|
||||
filename and the current state via the 'state' member (bitmask).
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
CBDL.Include = function(filename) {
|
||||
this.filename = filename;
|
||||
this.state = 0x00;
|
||||
}
|
||||
|
||||
CBDL.current_include = 0;
|
||||
CBDL.includes = [];
|
||||
|
||||
CBDL.states = {OPEN: 0x00, LOADING:0x01, LOADED:0x02, FAILED:0x03};
|
||||
|
||||
/*
|
||||
================
|
||||
CBDL.include(filename)
|
||||
|
||||
Method which should be invoked by the program implementing CBDL. It provides
|
||||
a nice method for including new source files/libraries located within the same
|
||||
directory as the invoking script.
|
||||
|
||||
Usage:
|
||||
CBDL.include("CBDL_video.js"); // loads CBDL_video.js, making all objects/vars,
|
||||
etc. available to the calling script.
|
||||
|
||||
================
|
||||
*/
|
||||
CBDL.include = function(filename) {
|
||||
if (typeof filename == "string") {
|
||||
CBDL.includes.push(new CBDL.Include(filename));
|
||||
if (CBDL.includes[CBDL.current_include].state === CBDL.states.OPEN) {
|
||||
CBDL.loadInclude(CBDL.includes[CBDL.current_include]);
|
||||
//CBDL.nextInclude();
|
||||
}
|
||||
} else {
|
||||
for (var file in filename) {
|
||||
CBDL.include(filename[file]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CBDL.nextInclude()
|
||||
|
||||
Internal method that is called when an Include finishes loading, so as to
|
||||
load the next Include within the includes list.
|
||||
|
||||
Usage:
|
||||
CBDL.nextInclude();
|
||||
|
||||
================
|
||||
*/
|
||||
CBDL.nextInclude = function() {
|
||||
if (CBDL.includes[CBDL.current_include+1] instanceof CBDL.Include) {
|
||||
CBDL.current_include++;
|
||||
CBDL.loadInclude(CBDL.includes[CBDL.current_include]);
|
||||
} else {
|
||||
// CBDL.onReady();
|
||||
// CBDL.onReady = function(){};
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
================
|
||||
CBDL.loadInclude(Include)
|
||||
|
||||
Internal method that attempts to use XMLHttpRequest or otherwise to
|
||||
dynamically load the specified Include. Upon success, a new <script> is
|
||||
added to the page's <head> and the state of the Include is changed to LOADED.
|
||||
|
||||
Usage:
|
||||
new_include = new CBDL.Include("CBDL_net.js");
|
||||
CBDL.loadInclude(new_include);
|
||||
console.log("state is: " + new_include.state);
|
||||
================
|
||||
*/
|
||||
CBDL.loadInclude = function(include) {
|
||||
var element = document.createElement("script");
|
||||
element.type = "text/javascript";
|
||||
element.src = include.filename;
|
||||
element.onload = function() {
|
||||
include.state = CBDL.states.LOADED;
|
||||
CBDL.nextInclude();
|
||||
}
|
||||
document.getElementsByTagName('head')[0].appendChild(element);
|
||||
|
||||
// if (!/*@cc_on!@*/false) {
|
||||
/* var request = new XMLHttpRequest();
|
||||
} else {
|
||||
var request = new ActiveXObject("Microsoft.XMLHTTP");
|
||||
}
|
||||
request.onreadystatechange = function() {
|
||||
if (this.readyState == 4) { // done loading
|
||||
if (this.status == 200) {
|
||||
include.element = document.createElement("script");
|
||||
include.element.type = "text/javascript";
|
||||
include.element.text = this.responseText;
|
||||
document.getElementsByTagName('head')[0].appendChild(include.element);
|
||||
include.state = CBDL.states.LOADED;
|
||||
} else {
|
||||
include.state = CBDL.states.FAILED;
|
||||
}
|
||||
CBDL.nextInclude();
|
||||
} else if (this.readyState == 2) { // loading
|
||||
include.state = CBDL.states.LOADING;
|
||||
}
|
||||
}
|
||||
request.open('GET', include.filename, true);
|
||||
request.send(null);
|
||||
*/
|
||||
}
|
||||
|
||||
/* Event object which is used to poll a variety of events, as defined through
|
||||
* the passed event_masks bitmask, or by calling Event's addEvent method.
|
||||
*/
|
||||
CBDL.Event = function(event_masks) {
|
||||
this.pollEvents = function() {
|
||||
if (event_pool.isEmpty()) {
|
||||
return -1;
|
||||
} else {
|
||||
return event_pool.getNext();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
/* **** CBDL_net - Optional layer which adds network support
|
||||
*
|
||||
*
|
||||
* */
|
||||
CBDL.Events = CBDL.Events || {};
|
||||
CBDL.Events.version = 0.1;
|
||||
|
||||
/*
|
||||
================
|
||||
CBDL.Event = CBDL.Event || CBDL.Events.Event;
|
||||
|
||||
|
||||
================
|
||||
*/
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
CBDL.Events.Event
|
||||
|
||||
Event object that should be instantized with a flag mask specifying the
|
||||
events to listen for. Events should be iterated through by running the Event's
|
||||
Poll() function.
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
CBDL.Events.Event = function(events_flag, max_events) {
|
||||
var position; // Our current position in the events list.
|
||||
this.Poll = function() {
|
||||
position++;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,151 @@
|
|||
CBDL.include("excanvas.js");
|
||||
/* **** CBDL_net - Optional layer which adds network support
|
||||
*
|
||||
*
|
||||
* */
|
||||
CBDL.Graphics = CBDL.Graphics || {};
|
||||
CBDL.Graphics.version = 0.1;
|
||||
|
||||
CBDL.Graphics.DisplayType = {DIV: 0x00, CANVAS_2D:0x01};
|
||||
CBDL.Graphics.backends = {DIV: 0x00, CANVAS_2D:0x01};
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
CBDL.Graphics.DisplaySkeleton
|
||||
|
||||
Basic Display skeleton from which all Display backends should be derived
|
||||
from. It is through the backend detection that the Display backend, being named
|
||||
something similar to Html5Display, is assigned as Graphics.Display. This allows
|
||||
for backends to be used/sent according to browser capability, rather than via
|
||||
bulk.
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
CBDL.Graphics.DisplaySkeleton = function(video_mode, target_element) {
|
||||
};
|
||||
|
||||
CBDL.Graphics.DisplaySkeleton.prototype.Init = function() {
|
||||
|
||||
};
|
||||
CBDL.Graphics.DisplaySkeleton.prototype.Fill = function(red, green, blue) {
|
||||
|
||||
};
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
CBDL.Graphics.Display
|
||||
|
||||
Base window onto which various Graphics elements can be placed within.
|
||||
Effectively exists as a simple wrapper to a 'div' element. The first parameter
|
||||
passed is an instance of a VideoMode(width, height). The second optional
|
||||
parameter is a target HTML element. If ommitted, the Display adds its element
|
||||
to document.body.
|
||||
|
||||
Usage:
|
||||
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
CBDL.Graphics.Display = function(video_mode, target_element) {
|
||||
var _context = CBDL.Graphics;
|
||||
this.context = null;
|
||||
this.init = function() {
|
||||
try { this.element = document.createElement("canvas"); }
|
||||
catch (x) { this.element = null; }
|
||||
if (this.element == null) { // fallback to nasty divs
|
||||
this.element = document.createElement("div");
|
||||
this.element.style.width = this.videomode.width;
|
||||
this.element.style.height = this.videomode.height;
|
||||
this.element.style.backgroundColor = 'black';
|
||||
this.element.style.display = "block";
|
||||
this.type = _context.DisplayType.DIV;
|
||||
} else { // using HTML5 canvas
|
||||
if (typeof G_vmlCanvasManager != "undefined") { // IE8, bleh
|
||||
G_vmlCanvasManager.initElement(this.element);
|
||||
}
|
||||
this.type = _context.DisplayType.CANVAS_2D;
|
||||
this.element.style.width = this.videomode.width;
|
||||
this.element.style.height = this.videomode.height;
|
||||
try { this.context = this.element.getContext("2d"); }
|
||||
catch (x) { this.context = null; }
|
||||
if (this.context == null) {
|
||||
} else {
|
||||
this.context.fillStyle = "#000000";
|
||||
this.context.fillRect(0, 0, this.videomode.width, this.videomode.height);
|
||||
}
|
||||
}
|
||||
(target_element ? target_element : document.body).appendChild(this.element);
|
||||
};
|
||||
|
||||
this.Fill = function(red, green, blue) {
|
||||
var hex_value = ((1 << 24) + (red << 16) + (green << 8) + blue);
|
||||
var hex_string = hex_value.toString(16).substr(1);
|
||||
if (this.type == _context.DisplayType.DIV) {
|
||||
this.element.style.backgroundColor = '#'+hex_string;
|
||||
} else if (this.type == _context.DisplayType.CANVAS_2D) {
|
||||
//this.context.fillStyle = "#"+hex_string;
|
||||
//this.context.fillRect(0, 0, this.videomode.width, this.videomode.height);
|
||||
}
|
||||
};
|
||||
|
||||
if (video_mode instanceof CBDL.Graphics.VideoMode) {
|
||||
this.videomode = video_mode;
|
||||
this.init();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
CBDL.Graphics.VideoMode(width, height);
|
||||
|
||||
Object which should be passed as the first parameter to Graphics.Display,
|
||||
defining the width and height of the Display.
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
CBDL.Graphics.VideoMode = function(width, height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
CBDL.Graphics.Drawable(2dVector position, 2dVector scale, float rotation)
|
||||
|
||||
Base class for all objects that can be drawn to a Graphics.Display.
|
||||
|
||||
Usage:
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
|
||||
CBDL.Graphics.Drawable = function(position, scale, rotation) {
|
||||
|
||||
this.setPosition = function(x, y) {
|
||||
|
||||
}
|
||||
this.setScale = function(x, y) {
|
||||
|
||||
}
|
||||
this.setCenter = function(x, y) {
|
||||
|
||||
}
|
||||
this.setRotation = function(rotation) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
===============================================================================
|
||||
|
||||
===============================================================================
|
||||
*/
|
||||
CBDL._Graphics_ = function() {};
|
||||
CBDL._Graphics_.prototype.VideoMode = function(width, height) {
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
/* **** CBDL_net - Optional layer which adds network support
|
||||
*
|
||||
*
|
||||
* */
|
||||
CBDL.Net = CBDL.Net || {};
|
||||
CBDL.Net.version = 0.1;
|
|
@ -0,0 +1,34 @@
|
|||
<html>
|
||||
<head>
|
||||
<script type="text/javascript" src="CBDL.js"></script>
|
||||
<script type="text/javascript">
|
||||
myApp = function() {
|
||||
this.requires = ["CBDL_graphics.js"];
|
||||
var main_display; // private member
|
||||
var main_loop; // private member
|
||||
|
||||
this.Main = function() { // override parent's Main
|
||||
console.log("bleh");
|
||||
main_display = new CBDL.Graphics.Display(new CBDL.Graphics.VideoMode(800, 600));
|
||||
(main_loop = new CBDL.Loop(this.Loop)).start();
|
||||
};
|
||||
|
||||
this.Loop = function(delta) { // our custom Loop member :)
|
||||
main_display.Fill(0xFF, 0xF0, 0xF0);
|
||||
console.log(main_loop.time_delay+", delta: "+delta);
|
||||
return(1000);
|
||||
};
|
||||
|
||||
}; CBDL.extends(CBDL.App, myApp); // Don't forget this line! sets up prototypal inheritance
|
||||
|
||||
|
||||
init = function() {
|
||||
var app = new myApp();
|
||||
app.Go();
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<button onclick="init()">init()</button>
|
||||
</body>
|
||||
</html>
|
Loading…
Reference in New Issue