First release,no database required,code style changes

This commit is contained in:
nipos 2018-03-11 12:07:57 +01:00
parent 6afd23142e
commit 5ccb63b8c5
136 changed files with 11598 additions and 16247 deletions

View File

@ -4,9 +4,6 @@ RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.(.+?)$
RewriteRule ^(.*)$ https://$1 [R=301,L]
RewriteCond %{SERVER_PORT} 80
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# Main
RewriteBase /
RewriteRule ^home/?$ / [R=301]

17
404.php Normal file
View File

@ -0,0 +1,17 @@
<!DOCTYPE HTML>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Halcyon / ?</title>
<link rel="shortcut icon" href="/assets/images/favicon.ico">
<link rel="stylesheet" href="/assets/css/404.css" media="all">
</head>
<body>
<article>
<h1>404</h1>
<h2>Sorry, something went wrong.</h2>
</article>
</body>
</html>

View File

@ -1,10 +1,13 @@
# Halcyon for Mastodon
A Mastodon web client that looks like Twitter
>The original author of this genious piece of software was inactive for a while and then shut down his demo instance and deleted this repository. I love Halcyon, it's the thing which makes Mastodon the best social network in the world. I took the Code from the Halcyon fork of cybre.space which still works but doesn't seem to get updates, too. I uploaded it here to make the original link work again and don't link into the big nothing. I'm working on much other stuff, too, what is why I won't be very active here in the near future but I do things which are required to keep it working and if I have much more time, I already know some nice features which could come. As for now I don't have an own demo instance but the one from cybre.space works pretty good and when I changed many things, I will open an own one.
>The original author of this genius piece of software was inactive for a while and then shut down his demo instance and deleted this repository. I love Halcyon, it's the thing which makes Mastodon the best social network in the world. I took the Code from the Halcyon fork of cybre.space which still works but doesn't seem to get updates, too. I uploaded it here to make the original link work again and don't link into the big nothing. I'm working on much other stuff, too, what is why I won't be very active here in the near future but I do things which are required to keep it working and if I have much more time, I already know some nice features which could come. As for now I don't have an own demo instance but the one from cybre.space works pretty good and when I changed many things, I will open an own one.
<img src="https://halcyon.cybre.space/login/assets/images/preview0.png">
## Blog
- [Release of Version 1.0.0](http://nikisoft.myblog.de/nikisoft/art/11264555/The-first-new-Halcyon-release-is-on-Github)
## Demo
https://halcyon.cybre.space/
Please note that this demo is not owned by me and I have not control over it. Currently I'm not providing an own demo so I link to that one. That may change in future.
@ -15,48 +18,13 @@ Please note that this demo is not owned by me and I have not control over it. Cu
- No tracking, No ads.
## Requirement
- Apache
- Apache/Nginx/lighttpd
- PHP
- MySQL
- No database needed anymore!
## Setup
I didn't prepared setup script so you have to setup manually...
### PDO MySQL
After installed PHP, run this.
```bash
sudo pecl install pdo_mysql
sudo vi php.ini
```
change to this
```php.ini
extension=mysqli.so
extension=pdo_mysql.so
```
### MySQL
After installed MySQL, create a user, run this.
```sql
CREATE DATABASE DATABASE_NAME DEFAULT CHARACTER SET utf8;
CREATE TABLE DATABASE_NAME.instances(domain varchar(261), client_id varchar(64), client_secret varchar(64));
```
and make file `/config.ini` like this
```config.ini
~~~ line 3 ~~~
api_client_name = <APP NAME>
api_client_website = <APP WEBSITE LINK>
~~~ line 8 ~~~
db_host = <DATABASE HOST DOMAIN>
db_user = <DATABASE USERNAME>
db_pass = <DATABASE PASSWORD>
db_name = <DATABASE NAME>
```
Upload it, edit config.ini and have fun!
## Credits
- [Kirschn/mastodon.js](https://github.com/Kirschn/mastodon.js)
- [yks118/Mastodon-api-php](https://github.com/yks118/Mastodon-api-php)

View File

@ -1,17 +0,0 @@
# Roadmap
These plans may change if I think something else is more important or if I consider something here as not that important!
## Next version
- [x] Oauth login
- [x] Support responsive
- [ ] Add meida attachment's next/prev button
## Future
- [ ] Multi account
- [ ] Emoji picker
- [ ] Native profile edit
- [ ] Dark theme
- [ ] Translation support
- [ ] Mobile devices support
- [ ] PHP PSR4 / Autoload

132
assets/css/404.css Normal file
View File

@ -0,0 +1,132 @@
@charset "utf-8";
* {
margin: 0;
padding: 0;
font-size: 100%;
}
a {
text-decoration: none;
word-break: break-all;
color: inherit;
}
a:hover {
text-decoration: underline;
}
ul, ol {
list-style: none;
padding: 0;
margin: 0;
}
img {
vertical-align: top;
border: 0;
max-width: 100%;
max-height: 100%;
}
button {
font-size: 100%;
}
.clear {
clear: both;
}
.red {
color: red!important;
}
.invisible {
display: none!important;
}
.no-events {
pointer-events: none;
}
.disallow_select {
user-select: none;
-ms-user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
}
.text_ellipsis {
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;
}
h1, h2, h3, h4, h5, h6 {
font-weight: normal;
line-height: 1;
margin: 0;
padding: 0;
}
blockquote, q {
quotes: none;
margin: 0;
}
blockquote * {
margin: 0;
word-break: break-all;
}
blockquote:before, blockquote:after,
q:before, q:after {
content:'';
content:none;
}
article,aside,details,figcaption,figure,
footer,header,hgroup,menu,nav,section {
display:block;
}
textarea {
width: 100%;
}
input {
max-width: 100%;
}
button, input[type="submit"], input[type="button"]{
background-color: transparent;
border: none;
cursor: pointer;
outline: none;
padding: 0;
appearance: none;
}
img.emoji {
height: 1em;
width: 1em;
margin: 0 .05em 0 .1em;
vertical-align: -0.1em;
}
.emoji_poss .auth_emoji {
display: inline-block;
height: 100%;
margin: auto;
margin-left: 4px;
}
html {
font-family : "Helvetica Neue",Helvetica,Arial,sans-serif;
font-size : 100%;
line-height : 1;
color: #333;
min-width: 100%;
min-height: 100%;
}
body {
margin: 0;
padding: 0;
min-width: 100%;
min-height: 100%;
word-wrap:break-word;
background-color: #189EFC;
}
article {
margin: auto;
padding: 24px;
}
h1 {
font-size: 248px;
text-align: center;
color: #fff;
font-weight: 600;
}
h2 {
font-size: 56px;
text-align: center;
color: #fff;
font-weight: 200;
}

2326
assets/css/style.css Normal file

File diff suppressed because it is too large Load Diff

View File

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 361 KiB

View File

Before

Width:  |  Height:  |  Size: 9.7 KiB

After

Width:  |  Height:  |  Size: 9.7 KiB

View File

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 24 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

BIN
assets/images/mastodon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.8 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="61.076954mm" height="65.47831mm" viewBox="0 0 216.4144 232.00976"><path d="M211.80734 139.0875c-3.18125 16.36625-28.4925 34.2775-57.5625 37.74875-15.15875 1.80875-30.08375 3.47125-45.99875 2.74125-26.0275-1.1925-46.565-6.2125-46.565-6.2125 0 2.53375.15625 4.94625.46875 7.2025 3.38375 25.68625 25.47 27.225 46.39125 27.9425 21.11625.7225 39.91875-5.20625 39.91875-5.20625l.8675 19.09s-14.77 7.93125-41.08125 9.39c-14.50875.7975-32.52375-.365-53.50625-5.91875C9.23234 213.82 1.40609 165.31125.20859 116.09125c-.365-14.61375-.14-28.39375-.14-39.91875 0-50.33 32.97625-65.0825 32.97625-65.0825C49.67234 3.45375 78.20359.2425 107.86484 0h.72875c29.66125.2425 58.21125 3.45375 74.8375 11.09 0 0 32.975 14.7525 32.975 65.0825 0 0 .41375 37.13375-4.59875 62.915" fill="#3088d4"/><path d="M177.50984 80.077v60.94125h-24.14375v-59.15c0-12.46875-5.24625-18.7975-15.74-18.7975-11.6025 0-17.4175 7.5075-17.4175 22.3525v32.37625H96.20734V85.42325c0-14.845-5.81625-22.3525-17.41875-22.3525-10.49375 0-15.74 6.32875-15.74 18.7975v59.15H38.90484V80.077c0-12.455 3.17125-22.3525 9.54125-29.675 6.56875-7.3225 15.17125-11.07625 25.85-11.07625 12.355 0 21.71125 4.74875 27.8975 14.2475l6.01375 10.08125 6.015-10.08125c6.185-9.49875 15.54125-14.2475 27.8975-14.2475 10.6775 0 19.28 3.75375 25.85 11.07625 6.36875 7.3225 9.54 17.22 9.54 29.675" fill="#fff"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

Before

Width:  |  Height:  |  Size: 126 B

After

Width:  |  Height:  |  Size: 126 B

View File

Before

Width:  |  Height:  |  Size: 126 B

After

Width:  |  Height:  |  Size: 126 B

View File

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,174 @@
function getLinkFromXHRHeader(xhrheaderstring) {
const re = xhrheaderstring.match(/link: <.+api\/v1\/(.+?)>; rel="(.+?)", <.+api\/v1\/(.+?)>; rel="(.+?)"/);
let di = new Object();
if(re){
di[re[2]] = re[1];
di[re[4]] = re[3];
}
return di;
}
function getRelativeURL(url, id, options) {
const array = url.split('/');
if ( array.length >= 4 ) {
if ( !options ) {
var options = ""
};
if (id) {
if (array[array.length-1].substr(0,1) === '@') {
const link= '/'+array[array.length-1]+'@'+array[2]+options+'?mid='+id+'&';
return link;
}
else {
const link= '/@'+array[array.length-1]+'@'+array[2]+options+'?mid='+id+'&';
return link;
}
}
else {
if (array[array.length-1].substr(0,1) === '@') {
const link= '/'+array[array.length-1]+'@'+array[2]+options;
return link;
} else {
const link= '/@'+array[array.length-1]+'@'+array[2]+options;
return link;
}
}
}
}
function replaceInternalLink(){
$(".h-card > a").each(function(i) {
$(this).attr('href',getRelativeURL($(this).attr('href')));
});
$(".toot_article a").each(function(i) {
const tags = $(this).attr('href').match(/https:\/\/.+..+\/tags\/(.+)\/?/);
if (tags) {
$(this).attr('href','/search?q='+tags[1]);
}
});
}
function getConversionedDate(key, value) {
if (value === null ||
value.constructor !== String ||
value.search(/^\d{4}-\d{2}-\d{2}/g) === -1)
return value;
return new Date(value);
}
function getRelativeDatetime(current_time, posted_time) {
const calendar = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
var posted_time_original = posted_time,
posted_time = getConversionedDate(null, posted_time_original).getTime(),
elapsedTime = Math.ceil((current_time-posted_time)/1000);
if (elapsedTime < 60) {
const datetime ="・" + elapsedTime + "s";
return datetime;
}
else if (elapsedTime < 120) {
const datetime ="・1m";
return datetime;
}
else if (elapsedTime < (60*60)) {
const datetime ="・" + (Math.floor(elapsedTime / 60) < 10 ? " " : "") + Math.floor(elapsedTime / 60) + "m";
return datetime;
}
else if (elapsedTime < (120*60)) {
const datetime ="・1h";
return datetime;
}
else if (elapsedTime < (24*60*60)) {
const datetime ="・" + (Math.floor(elapsedTime / 3600) < 10 ? " " : "") + Math.floor(elapsedTime / 3600) + "h";
return datetime;
}
else {
const datetime ="・" + calendar[posted_time_original.getMonth()] + " " + posted_time_original.getDate();
return datetime;
}
}
function resetApp() {
current_id = Number(localStorage.getItem("current_id"));
current_instance = localStorage.getItem("current_instance");
authtoken= localStorage.getItem("current_authtoken");
api = new MastodonAPI({
instance: 'https://'+current_instance,
api_user_token: authtoken
});
api.get("accounts/verify_credentials", function(AccountObj) {
localStorage.setItem("current_display_name", AccountObj["display_name"]);
localStorage.setItem("current_acct", AccountObj["acct"]);
localStorage.setItem("current_url", getRelativeURL(AccountObj["url"],AccountObj["id"]));
localStorage.setItem("current_header", AccountObj["header"]);
localStorage.setItem("current_avatar", AccountObj["avatar"]);
localStorage.setItem("current_statuses_count", AccountObj["statuses_count"]);
localStorage.setItem("current_following_count", AccountObj["following_count"]);
localStorage.setItem("current_followers_count", AccountObj["followers_count"]);
localStorage.setItem("current_statuses_count_link", getRelativeURL(AccountObj["url"],AccountObj["id"]));
localStorage.setItem("current_following_count_link", getRelativeURL(AccountObj["url"],AccountObj["id"],'/following'));
localStorage.setItem("current_followers_count_link", getRelativeURL(AccountObj["url"],AccountObj["id"],'/followers'));
localStorage.setItem("current_favourites_link", getRelativeURL(AccountObj["url"],AccountObj["id"],'/favourites'));
current_display_name = localStorage.getItem("current_display_name");
current_acct = localStorage.getItem("current_acct");
current_url = localStorage.getItem("current_url");
current_header = localStorage.getItem("current_header");
current_avatar = localStorage.getItem("current_avatar");
current_statuses_count = localStorage.getItem("current_statuses_count");
current_following_count = localStorage.getItem("current_following_count");
current_followers_count = localStorage.getItem("current_followers_count");
current_statuses_count_link = localStorage.getItem("current_statuses_count_link");
current_following_count_link = localStorage.getItem("current_following_count_link");
current_followers_count_link = localStorage.getItem("current_followers_count_link");
current_favourites_link = localStorage.getItem("current_favourites_link");
$(".js_current_profile_displayname").text(current_display_name);
$(".js_current_profile_username").text(current_acct);
$(".js_current_profile_link").attr('href', current_url);
$(".js_current_header_image").attr('src', current_header);
$(".js_current_profile_image").attr('src', current_avatar);
$(".js_current_toots_count").text(current_statuses_count);
$(".js_current_following_count").text(current_following_count);
$(".js_current_followers_count").text(current_followers_count);
$(".current_toots_count_link").attr('href', current_statuses_count_link);
$(".current_following_count_link").attr('href', current_following_count_link);
$(".current_followers_count_link").attr('href', current_followers_count_link);
replace_emoji();
});
$.cookie("session", "true", { path: '/' });
}
function refreshApp() {
current_id = Number(localStorage.getItem("current_id"));
current_instance = localStorage.getItem("current_instance");
authtoken= localStorage.getItem("current_authtoken");
api = new MastodonAPI({
instance: "https://"+current_instance,
api_user_token: authtoken
});
current_display_name = localStorage.getItem("current_display_name");
current_acct = localStorage.getItem("current_acct");
current_url= localStorage.getItem("current_url");
current_header = localStorage.getItem("current_header");
current_avatar = localStorage.getItem("current_avatar");
current_statuses_count = localStorage.getItem("current_statuses_count");
current_following_count= localStorage.getItem("current_following_count");
current_followers_count= localStorage.getItem("current_followers_count");
current_statuses_count_link= localStorage.getItem("current_statuses_count_link");
current_following_count_link = localStorage.getItem("current_following_count_link");
current_followers_count_link = localStorage.getItem("current_followers_count_link");
current_favourites_link= localStorage.getItem("current_favourites_link");
}
function setCurrentProfile() {
$(".js_current_profile_displayname").text(current_display_name);
$(".js_current_profile_username").text(current_acct);
$(".js_current_profile_link").attr("href", current_url);
$(".js_current_header_image").attr("src", current_header);
$(".js_current_profile_image").attr("src", current_avatar);
$(".js_current_toots_count").text(current_statuses_count);
$(".js_current_following_count").text(current_following_count);
$(".js_current_followers_count").text(current_followers_count);
$(".current_toots_count_link").attr("href", current_statuses_count_link);
$(".current_following_count_link").attr("href", current_following_count_link);
$(".current_followers_count_link").attr("href", current_followers_count_link);
replace_emoji();
}
function putMessage(Message) {
$('#overlay_message').addClass('view');
$('#overlay_message section span').text(Message);
setTimeout(function(){
$("#overlay_message").removeClass("view");
},3000);
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,78 @@
/*!
* jQuery Cookie Plugin v1.4.1
* https://github.com/carhartl/jquery-cookie
*
* Copyright 2006, 2014 Klaus Hartl
* Released under the MIT license
*/
(function (factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof exports === 'object') {
module.exports = factory(require('jquery'));
} else {
factory(jQuery);
}
}(function ($) {
var pluses = /\+/g;
function encode(s) {
return config.raw ? s : encodeURIComponent(s);
}
function decode(s) {
return config.raw ? s : decodeURIComponent(s);
}
function stringifyCookieValue(value) {
return encode(config.json ? JSON.stringify(value) : String(value));
}
function parseCookieValue(s) {
if (s.indexOf('"') === 0) {
s = s.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\');
}
try {
s = decodeURIComponent(s.replace(pluses, ' '));
return config.json ? JSON.parse(s) : s;
} catch(e) {}
}
function read(s, converter) {
var value = config.raw ? s : parseCookieValue(s);
return $.isFunction(converter) ? converter(value) : value;
}
var config = $.cookie = function (key, value, options) {
if (arguments.length > 1 && !$.isFunction(value)) {
options = $.extend({}, config.defaults, options);
if (typeof options.expires === 'number') {
var days = options.expires, t = options.expires = new Date();
t.setMilliseconds(t.getMilliseconds() + days * 864e+5);
}
return (document.cookie = [
encode(key), '=', stringifyCookieValue(value),
options.expires ? '; expires=' + options.expires.toUTCString() : '',
options.path ? '; path=' + options.path : '',
options.domain ? '; domain=' + options.domain : '',
options.secure ? '; secure' : ''
].join(''));
}
var result = key ? undefined : {},
cookies = document.cookie ? document.cookie.split('; ') : [],
i = 0,
l = cookies.length;
for (; i < l; i++) {
var parts = cookies[i].split('='),
name = decode(parts.shift()),
cookie = parts.join('=');
if (key === name) {
result = read(cookie, value);
break;
}
if (!key && (cookie = read(cookie)) !== undefined) {
result[name] = cookie;
}
}
return result;
};
config.defaults = {};
$.removeCookie = function (key, options) {
$.cookie(key, '', $.extend({}, options, { expires: -1 }));
return !$.cookie(key);
};
}));

View File

@ -0,0 +1,207 @@
// mastodon javascript lib
// by @kirschn@pleasehug.me 2017
// no fucking copyright
// do whatever you want with it
// but please don't hurt it (and keep this header)
var MastodonAPI = function(config) {
var apiBase = config.instance + "/api/v1/";
return {
setConfig: function (key, value) {
config[key] = value;
},
getConfig: function(key) {
return config[key];
},
get: function (endpoint) {
var queryData, callback,
queryStringAppend = "?";
if (typeof arguments[1] === "function") {
queryData = {};
callback = arguments[1];
} else {
queryData = arguments[1];
callback = arguments[2];
}
for (var i in queryData) {
if (queryData.hasOwnProperty(i)) {
if (typeof queryData[i] === "string") {
queryStringAppend += queryData[i] + "&";
} else if (typeof queryData[i] === "object") {
queryStringAppend += queryData[i].name + "="+ queryData[i].data + "&";
}
}
}
$.ajax({
url: apiBase + endpoint + queryStringAppend,
type: "GET",
headers: {"Authorization": "Bearer " + config.api_user_token},
success: function(data, textStatus, xhr) {
console.log("Successful GET API request to " +apiBase+endpoint);
responce_headers = xhr.getAllResponseHeaders();
callback(data,textStatus);
},
error: function(xhr, textStatus, errorThrown) {
putMessage(`[${xhr.status}] ${xhr.responseJSON['error']}`);
if ( xhr.status === 401 ) {
location.href = "/logout"
}
}
});
},
getArray: function (endpoint) {
var queryData, callback,
queryStringAppend = "?";
if (typeof arguments[1] === "function") {
queryData = {};
callback = arguments[1];
} else {
queryData = arguments[1];
callback = arguments[2];
}
for (var i in queryData) {
if (queryData.hasOwnProperty(i)) {
if (typeof queryData[i] === "string") {
queryStringAppend += queryData[i] + "&";
} else if (typeof queryData[i] === "object") {
for ( var j in queryData[i].data ){
queryStringAppend += queryData[i].name + "[]="+ queryData[i].data[j] + "&";
}
}
}
}
$.ajax({
url: apiBase + endpoint + queryStringAppend,
type: "GET",
headers: {"Authorization": "Bearer " + config.api_user_token},
success: function(data, textStatus, xhr) {
console.log("Successful GET API request to " +apiBase+endpoint);
responce_headers = xhr.getAllResponseHeaders();
callback(data,textStatus);
},
error: function(xhr, textStatus, errorThrown) {
putMessage(`[${xhr.status}] ${xhr.responseJSON['error']}`);
if ( xhr.status === 401 ) {
location.href = "/logout"
}
}
});
},
getOther: function (domainAndEndpoint) {
var queryData, callback,
queryStringAppend = "?";
if (typeof arguments[1] === "function") {
queryData = {};
callback = arguments[1];
} else {
queryData = arguments[1];
callback = arguments[2];
}
for (var i in queryData) {
if (queryData.hasOwnProperty(i)) {
if (typeof queryData[i] === "string") {
queryStringAppend += queryData[i] + "&";
} else if (typeof queryData[i] === "object") {
queryStringAppend += queryData[i].name + "="+ queryData[i].data + "&";
}
}
}
$.ajax({
url: domainAndEndpoint + queryStringAppend,
type: "GET",
success: function(data, textStatus, xhr) {
console.log("Successful GET API request to " +domainAndEndpoint);
responce_headers = xhr.getAllResponseHeaders();
callback(data,textStatus);
},
error: function(xhr, textStatus, errorThrown) {
putMessage(`[${xhr.status}] ${xhr.responseJSON['error']}`);
if ( xhr.status === 401 ) {
location.href = "/logout"
}
}
});
},
post: function (endpoint) {
var postData, callback;
if (typeof arguments[1] === "function") {
postData = {};
callback = arguments[1];
} else {
postData = arguments[1];
callback = arguments[2];
}
$.ajax({
url: apiBase + endpoint,
type: "POST",
data: postData,
headers: {"Authorization": "Bearer " + config.api_user_token},
success: function(data, textStatus) {
console.log("Successful POST API request to " +apiBase+endpoint);
callback(data,textStatus)
},
error: function(xhr, textStatus, errorThrown) {
putMessage(`[${xhr.status}] ${xhr.responseJSON['error']}`);
if ( xhr.status === 401 ) {
location.href = "/logout"
}
}
});
},
postMedia: function (endpoint) {
var postData, callback;
if (typeof arguments[1] === "function") {
postData = {};
callback = arguments[1];
} else {
postData = arguments[1];
callback = arguments[2];
}
$.ajax({
url: apiBase + endpoint,
type: "POST",
data: postData,
contentType: false,
processData: false,
headers: {"Authorization": "Bearer " + config.api_user_token},
success: function(data, textStatus) {
console.log("Successful POST API request to " +apiBase+endpoint);
callback(data,textStatus)
},
error: function(xhr, textStatus, errorThrown) {
putMessage(`[${xhr.status}] ${xhr.responseJSON['error']}`);
if ( xhr.status === 401 ) {
location.href = "/logout"
}
}
});
},
delete: function (endpoint, callback) {
$.ajax({
url: apiBase + endpoint,
type: "DELETE",
headers: {"Authorization": "Bearer " + config.api_user_token},
success: function(data, textStatus) {
console.log("Successful DELETE API request to " +apiBase+endpoint);
callback(data,textStatus)
},
error: function(xhr, textStatus, errorThrown) {
putMessage(`[${xhr.status}] ${xhr.responseJSON['error']}`);
if ( xhr.status === 401 ) {
location.href = "/logout"
}
}
});
},
stream: function (streamType, onData) {
var es = new WebSocket("wss://" + apiBase.substr(8)
+"streaming?access_token=" + config.api_user_token + "&stream=" + streamType);
var listener = function (event) {
console.log("Got Data from Stream " + streamType);
event = JSON.parse(event.data);
event.payload = JSON.parse(event.payload);
onData(event);
};
es.onmessage = listener;
}
};
};

3473
assets/js/replace_emoji.js Normal file

File diff suppressed because it is too large Load Diff

186
assets/js/shortcut.js Normal file
View File

@ -0,0 +1,186 @@
/**
* http://www.openjs.com/scripts/events/keyboard_shortcuts/
* Version : 2.01.B
* By Binny V A
* License : BSD
*/
shortcut = {
'all_shortcuts':{},
'add': function(shortcut_combination,callback,opt) {
var default_options = {
'type':'keydown',
'propagate':false,
'disable_in_input':false,
'target':document,
'keycode':false
}
if(!opt) opt = default_options;
else {
for(var dfo in default_options) {
if(typeof opt[dfo] == 'undefined') opt[dfo] = default_options[dfo];
}
}
var ele = opt.target;
if(typeof opt.target == 'string') ele = document.getElementById(opt.target);
var ths = this;
shortcut_combination = shortcut_combination.toLowerCase();
var func = function(e) {
e = e || window.event;
if(opt['disable_in_input']) {
var element;
if(e.target) element=e.target;
else if(e.srcElement) element=e.srcElement;
if(element.nodeType==3) element=element.parentNode;
if(element.tagName == 'INPUT' || element.tagName == 'TEXTAREA') return;
}
if (e.keyCode) code = e.keyCode;
else if (e.which) code = e.which;
var character = String.fromCharCode(code).toLowerCase();
if(code == 188) character=",";
if(code == 190) character=".";
var keys = shortcut_combination.split("+");
var kp = 0;
var shift_nums = {
"`":"~",
"1":"!",
"2":"@",
"3":"#",
"4":"$",
"5":"%",
"6":"^",
"7":"&",
"8":"*",
"9":"(",
"0":")",
"-":"_",
"=":"+",
";":":",
"'":"\"",
",":"<",
".":">",
"/":"?",
"\\":"|"
}
var special_keys = {
'esc':27,
'escape':27,
'tab':9,
'space':32,
'return':13,
'enter':13,
'backspace':8,
'scrolllock':145,
'scroll_lock':145,
'scroll':145,
'capslock':20,
'caps_lock':20,
'caps':20,
'numlock':144,
'num_lock':144,
'num':144,
'pause':19,
'break':19,
'insert':45,
'home':36,
'delete':46,
'end':35,
'pageup':33,
'page_up':33,
'pu':33,
'pagedown':34,
'page_down':34,
'pd':34,
'left':37,
'up':38,
'right':39,
'down':40,
'f1':112,
'f2':113,
'f3':114,
'f4':115,
'f5':116,
'f6':117,
'f7':118,
'f8':119,
'f9':120,
'f10':121,
'f11':122,
'f12':123
}
var modifiers = {
shift: { wanted:false, pressed:false},
ctrl : { wanted:false, pressed:false},
alt : { wanted:false, pressed:false},
meta : { wanted:false, pressed:false}
};
if(e.ctrlKey)modifiers.ctrl.pressed = true;
if(e.shiftKey)modifiers.shift.pressed = true;
if(e.altKey)modifiers.alt.pressed = true;
if(e.metaKey) modifiers.meta.pressed = true;
for(var i=0; k=keys[i],i<keys.length; i++) {
//Modifiers
if(k == 'ctrl' || k == 'control') {
kp++;
modifiers.ctrl.wanted = true;
} else if(k == 'shift') {
kp++;
modifiers.shift.wanted = true;
} else if(k == 'alt') {
kp++;
modifiers.alt.wanted = true;
} else if(k == 'meta') {
kp++;
modifiers.meta.wanted = true;
} else if(k.length > 1) {
if(special_keys[k] == code) kp++;
} else if(opt['keycode']) {
if(opt['keycode'] == code) kp++;
} else {
if(character == k) kp++;
else {
if(shift_nums[character] && e.shiftKey) {
character = shift_nums[character];
if(character == k) kp++;
}
}
}
}
if(kp == keys.length &&
modifiers.ctrl.pressed == modifiers.ctrl.wanted &&
modifiers.shift.pressed == modifiers.shift.wanted &&
modifiers.alt.pressed == modifiers.alt.wanted &&
modifiers.meta.pressed == modifiers.meta.wanted) {
callback(e);
if(!opt['propagate']) {
e.cancelBubble = true;
e.returnValue = false;
if (e.stopPropagation) {
e.stopPropagation();
e.preventDefault();
}
return false;
}
}
}
this.all_shortcuts[shortcut_combination] = {
'callback':func,
'target':ele,
'event': opt['type']
};
if(ele.addEventListener) ele.addEventListener(opt['type'], func, false);
else if(ele.attachEvent) ele.attachEvent('on'+opt['type'], func);
else ele['on'+opt['type']] = func;
},
'remove':function(shortcut_combination) {
shortcut_combination = shortcut_combination.toLowerCase();
var binding = this.all_shortcuts[shortcut_combination];
delete(this.all_shortcuts[shortcut_combination])
if(!binding) return;
var type = binding['event'];
var ele = binding['target'];
var callback = binding['callback'];
if(ele.detachEvent) ele.detachEvent('on'+type, callback);
else if(ele.removeEventListener) ele.removeEventListener(type, callback, false);
else ele['on'+type] = false;
}
}

1
authorize/.htaccess Normal file
View File

@ -0,0 +1 @@
deny from all

View File

@ -1,970 +0,0 @@
<?php
// defined('BASEPATH') OR exit('No direct script access allowed');
/**
* Class Mastodon_api
*
* PHP version 7.1
*
* Mastodon https://mastodon.social/
* API LIST https://github.com/tootsuite/documentation/blob/master/Using-the-API/API.md
*
* @author KwangSeon Yun <middleyks@hanmail.net>
* @copyright KwangSeon Yun
* @license https://raw.githubusercontent.com/yks118/Mastodon-api-php/master/LICENSE MIT License
* @link https://github.com/yks118/Mastodon-api-php
*/
class Mastodon_api {
private $mastodon_url = '';
private $client_id = '';
private $client_secret = '';
private $token = array();
private $scopes = array();
public function __construct () {}
public function __destruct () {}
/**
* _post
*
* curl post
*
* @param string $url
* @param array $data
*
* @return array $response
*/
private function _post ($url,$data = array()) {
$parameters = array();
$parameters[CURLOPT_POST] = 1;
// set access_token
if (isset($this->token['access_token'])) {
$data['access_token'] = $this->token['access_token'];
}
if (count($data)) {
$parameters[CURLOPT_POSTFIELDS] = http_build_query($data);
}
$url = $this->mastodon_url.$url;
$response = $this->get_content_curl($url,$parameters);
return $response;
}
/**
* _get
*
* @param string $url
* @param array $data
*
* @return array $response
*/
private function _get ($url,$data = array()) {
$parameters = array();
// set authorization bearer
if (isset($this->token['access_token'])) {
$authorization = 'Authorization: '.$this->token['token_type'].' '.$this->token['access_token'];
$parameters[CURLOPT_HTTPHEADER] = array('Content-Type: application/json',$authorization);
}
$url = $this->mastodon_url.$url;
if (count($data)) {
$url .= '?'.http_build_query($data);
}
$response = $this->get_content_curl($url,$parameters);
return $response;
}
/**
* _patch
*
* @param string $url
* @param array $data
*
* @return array $parameters
*/
private function _patch ($url,$data = array()) {
$parameters = array();
$parameters[CURLOPT_CUSTOMREQUEST] = 'PATCH';
// set authorization bearer
if (isset($this->token['access_token'])) {
$authorization = 'Authorization: '.$this->token['token_type'].' '.$this->token['access_token'];
$parameters[CURLOPT_HTTPHEADER] = array('Content-Type: application/json',$authorization);
}
if (count($data)) {
$parameters[CURLOPT_POSTFIELDS] = json_encode($data);
}
$url = $this->mastodon_url.$url;
$response = $this->get_content_curl($url,$parameters);
return $response;
}
/**
* _delete
*
* @param string $url
*
* @return array $response
*/
private function _delete ($url) {
$parameters = array();
$parameters[CURLOPT_CUSTOMREQUEST] = 'DELETE';
// set authorization bearer
if (isset($this->token['access_token'])) {
$authorization = 'Authorization: '.$this->token['token_type'].' '.$this->token['access_token'];
$parameters[CURLOPT_HTTPHEADER] = array('Content-Type: application/json',$authorization);
}
$url = $this->mastodon_url.$url;
$response = $this->get_content_curl($url,$parameters);
return $response;
}
/**
* get_content_curl
*
* @param string $url
* @param array $parameters
*
* @return array $data
*/
protected function get_content_curl ($url,$parameters = array()) {
$data = array();
// set CURLOPT_USERAGENT
if (!isset($parameters[CURLOPT_USERAGENT])) {
if (isset($_SERVER['HTTP_USER_AGENT'])) {
$parameters[CURLOPT_USERAGENT] = $_SERVER['HTTP_USER_AGENT'];
} else {
// default IE11
$parameters[CURLOPT_USERAGENT] = 'Mozilla/5.0 (Windows NT 10.0; WOW64; Trident/7.0; rv:11.0) like Gecko';
}
}
// check curl_init
if (function_exists('curl_init')) {
$ch = curl_init();
// url 설정
curl_setopt($ch,CURLOPT_URL,$url);
foreach ($parameters as $key => $value) {
curl_setopt($ch,$key,$value);
}
// https
if (!isset($parameters[CURLOPT_SSL_VERIFYPEER])) {
curl_setopt($ch,CURLOPT_SSL_VERIFYPEER,FALSE);
}
if (!isset($parameters[CURLOPT_SSLVERSION])) {
curl_setopt($ch,CURLOPT_SSLVERSION,6);
}
// no header
if (!isset($parameters[CURLOPT_HEADER])) {
curl_setopt($ch,CURLOPT_HEADER,0);
}
// POST / GET (default : GET)
if (!isset($parameters[CURLOPT_POST]) && !isset($parameters[CURLOPT_CUSTOMREQUEST])) {
curl_setopt($ch,CURLOPT_POST,0);
}
// response get php value
if (!isset($parameters[CURLOPT_RETURNTRANSFER])) {
curl_setopt($ch,CURLOPT_RETURNTRANSFER,1);
}
// HTTP2
if (!isset($parameters[CURLOPT_HTTP_VERSION])) {
curl_setopt($ch,CURLOPT_HTTP_VERSION,3);
}
if (!isset($parameters[CURLINFO_HEADER_OUT])) {
curl_setopt($ch,CURLINFO_HEADER_OUT,TRUE);
}
$data['html'] = json_decode(curl_exec($ch),true);
$data['response'] = curl_getinfo($ch);
curl_close($ch);
}
return $data;
}
/**
* set_url
*
* @param string $path
*/
public function set_url ($path) {
$this->mastodon_url = $path;
}
/**
* set_client
*
* @param string $id
* @param string $secret
*/
public function set_client ($id,$secret) {
$this->client_id = $id;
$this->client_secret = $secret;
}
/**
* set_token
*
* @param string $token
* @param string $type
*/
public function set_token ($token,$type) {
$this->token['access_token'] = $token;
$this->token['token_type'] = $type;
}
/**
* set_scopes
*
* @param array $scopes read / write / follow
*/
public function set_scopes ($scopes) {
$this->scopes = $scopes;
}
/**
* create_app
*
* @param string $client_name
* @param array $scopes read / write / follow
* @param string $redirect_uris
* @param string $website
*
* @return array $response
* int $response['id']
* string $response['redirect_uri']
* string $response['client_id']
* string $response['client_secret']
*/
public function create_app ($client_name,$scopes = array(),$redirect_uris = '',$website = '') {
$parameters = array();
if (count($scopes) == 0) {
if (count($this->scopes) == 0) {
$scopes = array('read','write','follow');
} else {
$scopes = $this->scopes;
}
}
$parameters['client_name'] = $client_name;
$parameters['scopes'] = implode(' ',$scopes);
if (empty($redirect_uris)) {
$parameters['redirect_uris'] = 'urn:ietf:wg:oauth:2.0:oob';
} else {
$parameters['redirect_uris'] = $redirect_uris;
}
if ($website) {
$parameters['website'] = $website;
}
$response = $this->_post('/api/v1/apps',$parameters);
if (isset($response['html']['client_id'])) {
$this->client_id = $response['html']['client_id'];
$this->client_secret = $response['html']['client_secret'];
}
return $response;
}
/**
* login
*
* @param string $id E-mail Address
* @param string $password Password
*
* @return array $response
* string $response['access_token']
* string $response['token_type'] bearer
* string $response['scope'] read
* int $response['created_at'] time
*/
public function login ($id,$password) {
$parameters = array();
$parameters['client_id'] = $this->client_id;
$parameters['client_secret'] = $this->client_secret;
$parameters['grant_type'] = 'password';
$parameters['username'] = $id;
$parameters['password'] = $password;
if (count($this->scopes) == 0) {
$parameters['scope'] = implode(' ',array('read','write','follow'));
} else {
$parameters['scope'] = implode(' ',$this->scopes);
}
$response = $this->_post('/oauth/token',$parameters);
if (isset($response['html']["access_token"])) {
$this->token['access_token'] = $response['html']['access_token'];
$this->token['token_type'] = $response['html']['token_type'];
}
return $response;
}
public function get_access_token ($redirect_uri,$code) {
$parameters = array();
$parameters['grant_type'] = 'authorization_code';
$parameters['redirect_uri'] = $redirect_uri;
$parameters['client_id'] = $this->client_id;
$parameters['client_secret'] = $this->client_secret;
$parameters['code'] = $code;
$response = $this->_post('/oauth/token',$parameters);
if (isset($response['html']["access_token"])) {
$this->token['access_token'] = $response['html']['access_token'];
$this->token['token_type'] = $response['html']['token_type'];
}
return $response;
}
/**
* accounts
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
*
* @return array $response
* int $response['id']
* string $response['username']
* string $response['acct']
* string $response['display_name'] The name to display in the user's profile
* bool $response['locked']
* string $response['created_at']
* int $response['followers_count']
* int $response['following_count']
* int $response['statuses_count']
* string $response['note'] A new biography for the user
* string $response['url']
* string $response['avatar'] A base64 encoded image to display as the user's avatar
* string $response['avatar_static']
* string $response['header'] A base64 encoded image to display as the user's header image
* string $response['header_static']
*/
public function accounts ($id) {
$response = $this->_get('/api/v1/accounts/'.$id);
return $response;
}
/**
* accounts_verify_credentials
*
* Getting the current user
*
* @return array $response
*/
public function accounts_verify_credentials () {
$response = $this->_get('/api/v1/accounts/verify_credentials');
return $response;
}
/**
* accounts_update_credentials
*
* Updating the current user
*
* @param array $parameters
* string $parameters['display_name'] The name to display in the user's profile
* string $parameters['note'] A new biography for the user
* string $parameters['avatar'] A base64 encoded image to display as the user's avatar
* string $parameters['header'] A base64 encoded image to display as the user's header image
*
* @return array $response
*/
public function accounts_update_credentials ($parameters) {
$response = $this->_patch('/api/v1/accounts/update_credentials',$parameters);
return $response;
}
/**
* accounts_followers
*
* @see https://your-domain/web/accounts/:id
*
* @param int $id
*
* @return array $response
*/
public function accounts_followers ($id) {