From d25fbc8bea6d9dacc8ea5987708ba920da21a34a Mon Sep 17 00:00:00 2001 From: Daisuke Date: Tue, 2 Jun 2020 17:37:50 -0500 Subject: [PATCH] 020620 general layout and css improvements. --- css/color.css | 2 +- css/mobile.css | 5 + css/style.css | 1 + include/functions.php | 1175 +++++++++++++++++-------------- js/scripts.js | 11 +- layout/header.php | 4 +- layout/sidebar.php | 4 - modules/timeline.php | 9 +- templates/replyform.txt | 4 +- themes/longhorn/css/poorly.html | 1138 ++++++++++++++++++++++++++++++ 10 files changed, 1802 insertions(+), 551 deletions(-) create mode 100644 themes/longhorn/css/poorly.html diff --git a/css/color.css b/css/color.css index 242f432..d7bfcff 100644 --- a/css/color.css +++ b/css/color.css @@ -84,7 +84,7 @@ input[type="checkbox"]:checked+label { } .side_element { - background-color: rgba(255, 255, 255, 0.1); + background-color: rgba(255, 255, 255, 0.0); /* box-shadow: 1px 1px 1px black; */ color: lightgray; } diff --git a/css/mobile.css b/css/mobile.css index ea7369f..1cc0f22 100644 --- a/css/mobile.css +++ b/css/mobile.css @@ -584,6 +584,11 @@ aside { border-bottom:1px dotted #aaa; } +.reply .postMenu { + margin-right:15px !important; + margin-top:1px !important; +} + .replyflex { flex: 6 !important; } diff --git a/css/style.css b/css/style.css index 505389a..86a54b5 100644 --- a/css/style.css +++ b/css/style.css @@ -542,6 +542,7 @@ aside { display: inline-block; margin:0px !important; text-align: left; + word-break: break-all; } .post_footer { diff --git a/include/functions.php b/include/functions.php index 9f95d84..ff3acb5 100644 --- a/include/functions.php +++ b/include/functions.php @@ -7,153 +7,18 @@ $srv = $user_settings['instance']; //$token = ($token != false ? msc($token,'d') : false); $token = ($token != false ? $token : false); -/* the purpose of this function is to encode the auth token - it is not used for now */ -function msc($string, $action = 'e') { - // you may change these values to your own - $secret_key = 'yAmfVhZwm0749FSY24dC'; - $secret_iv = 'm37uvAeKjYLKdI1lPkcJ'; - - $output = false; - $encrypt_method = "AES-256-CBC"; - $key = hash('sha256', $secret_key); - $iv = substr(hash('sha256', $secret_iv) , 0, 16); - - if ($action == 'e') { - $output = base64_encode(openssl_encrypt($string, $encrypt_method, $key, 0, $iv)); - } - else if ($action == 'd') { - $output = openssl_decrypt(base64_decode($string) , $encrypt_method, $key, 0, $iv); - } - - return $output; -} - -/* this function extracts the urls from a text and return them in an array */ -function get_urls($input) { - $pattern = '$(https?://[a-z0-9_./?=&-~]+)(?![^<>]*>)$i'; - if (preg_match_all($pattern, $input, $matches)) { - list($dummy, $links) = ($matches); - return $links; - } - return false; -} - -/* this is the same as above but is used on other places, like user bios and places where the -shortcodes and the emoji url is defined in the same place */ -function emojify($string, $emojis, $size = 40) { - foreach ($emojis as $emoji) { - $string = str_replace(":" . $emoji['shortcode'] . ":", "" . $emoji[", $string); - } - return $string; -} - -/* This function displays the emoji list of an instance based on a search - string given on $val */ -function emoji_list($val){ - $emojilist = api_get("/custom_emojis"); - $c = 0; - $return = ""; - foreach ($emojilist as $emoji){ - if (starts_with($emoji['shortcode'],$val) && $c < 50){ - $return .= ""; - $c++; - } - } - if ($c < 50){ - foreach ($emojilist as $emoji){ - if ((contains($emoji['shortcode'],$val) && !starts_with($emoji['shortcode'],$val)) && $c < 50){ - $return .= ""; - $c++; - } - } - } - - return $return; -} - -function contact_search($val){ - global $user_settings; - $return = ""; - $list = api_get("/accounts/search?q=".$val); - foreach ($list as $contact){ - $return .= "
-
-
- ".emojify($contact['display_name'], $contact['emojis'], 15)."
- ".$contact['acct']." -
-
"; - } - return $return; -} -/* general function to check if one strings starts with a given search */ -function starts_with($string,$search){ - if (substr(strtolower($string),0,strlen($search)) == strtolower($search)){ - return true; - } - return false; -} -/* general function to check if one strings contains with a given search */ -function contains($string,$search){ - if (is_numeric(strpos(strtolower($string),strtolower($search)))){ - return true; - } - return false; -} -/* this function fetches the context (the previous posts and replies) of a specified post - $post = ID of the post. -*/ -function context($post) { - global $srv; - global $token; - $curl = curl_init(); - curl_setopt($curl, CURLOPT_URL, "https://$srv/api/v1/statuses/$post/context"); - if (!is_null($token)) { - curl_setopt($curl, CURLOPT_HTTPHEADER, array( - 'Authorization: Bearer ' . $token - )); - } - curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); - $result = curl_exec($curl); - curl_close($curl); - return $result; -} +// FUNCTIONS THAT HAVE TO DO WITH INSTANCE COMMUNICATION AND RETRIEVAL -/* this function just reduces an image to a 1x1 pixel image to get the overall color. - */ -function averageColor($url) { - @$image = imagecreatefromstring(file_get_contents($url)); - - if (!$image) { - $mainColor = "CCCCCC"; - } - else { - $thumb = imagecreatetruecolor(1, 1); - imagecopyresampled($thumb, $image, 0, 0, 0, 0, 1, 1, imagesx($image) , imagesy($image)); - $mainColor = strtoupper(dechex(imagecolorat($thumb, 0, 0))); - } - - return $mainColor; -} - -/* this function fetches all the data from a profile (bio, username, relationships, etc) */ -function user_info($user) { - global $user_settings; - $info = api_get("accounts/" . $user); - $rel = api_get("accounts/relationships?id=" . $user); - return array( - $info, - $rel - ); -} - -/* a function to make an authenticated general GET api call to the logged-in instance */ +/* a function to make an authenticated general GET api call to the logged-in instance. + - $url is a string of an api call like "account/:id/statuses" + + - returns the array conversion of the json response + */ function api_get($url) { global $srv; global $token; @@ -190,7 +55,11 @@ function api_getv2($url) { return json_decode($result, true); } -/* a function to make an authenticated general POST api call to the logged-in instance */ +/* a function to make an authenticated general POST api call to the logged-in instance. + - $url is a string of an api call like "account/:id/statuses" + + - returns the array conversion of the json response + */ function api_post($url, $array) { global $srv; global $token; @@ -212,7 +81,11 @@ function api_post($url, $array) { return json_decode($result, true); } -/* a function to make general DELETE api calls to the logged-in instance*/ +/* a function to make an authenticated general DELETE api call to the logged-in instance. + - $url is a string of an api call like "statuses/delete/:id" + + - returns the array conversion of the json response + */ function api_delete($url, $array) { global $srv; global $token; @@ -235,7 +108,11 @@ function api_delete($url, $array) { return json_decode($result, true); } -/* a function to make general PATCH api calls to the logged-in instance*/ +/* a function to make an authenticated general PATCH api call to the logged-in instance. + - $url is a string of an api call like "account/:id/statuses" + + - returns the array conversion of the json response + */ function api_patch($url, $array) { global $srv; global $token; @@ -259,7 +136,10 @@ function api_patch($url, $array) { } /* Function to upload files to the profile of the authenticated user - $type can be 'avatar' or 'header'*/ + - $type can be 'avatar' or 'header' + + - returns the json of the api call + */ function upload_profile($file,$type){ global $srv; global $token; @@ -284,6 +164,511 @@ function upload_profile($file,$type){ return $result; } +/* this function fetches all the data from a profile (bio, username, relationships, etc) + - $user is the id of the queried user + + - returns the array conversion of the json response +*/ +function user_info($user) { + global $user_settings; + $info = api_get("accounts/" . $user); + $rel = api_get("accounts/relationships?id=" . $user); + return array( + $info, + $rel + ); +} + +/* this function fetches the context (the previous posts and replies) of a specified post + - $post = ID of the post queried. + + - returns an array conversion of the json response +*/ +function context($post) { + global $srv; + global $token; + + $curl = curl_init(); + curl_setopt($curl, CURLOPT_URL, "https://$srv/api/v1/statuses/$post/context"); + if (!is_null($token)) { + curl_setopt($curl, CURLOPT_HTTPHEADER, array( + 'Authorization: Bearer ' . $token + )); + } + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + $result = curl_exec($curl); + curl_close($curl); + return $result; +} + + +/* a function to fav or unfav a specified post + - $post = ID of the post queried. + - $mode can be true if is a fav or false if it's an unfav + + - returns the number of favs of the post after the specified action or "error" + if there was an error performing the action +*/ +function favourite($post, $mode) { + $result = api_post(($mode == true ? "statuses/$post/favourite" : "statuses/$post/unfavourite"),array()); + + if (isset($result['favourites_count'])) { + return $result['favourites_count']; + } + else { + return "error"; + } +} + + +/* a function to reblog or unreblog a specified post + - $post = ID of the post queried. + - $mode can be true if is a reblog or false if it's an unreblog + + - returns the number of reblogs of the post after the specified action or "error" + if there was an error performing the action +*/ +function reblog($post, $mode) { + $result = api_post(($mode == true ? "statuses/$post/reblog" : "statuses/$post/unreblog"),array()); + if (isset($result['reblog']['reblogs_count'])) { + return $result['reblog']['reblogs_count']; + } + elseif (isset($result['reblogs_count'])) { + return $result['reblogs_count']; + } + else { + return "error"; + } +} + + + +/* function to delete a post + - $id is the id of the post to delete + + - returns 1 if the post was deleted succesfully or 0 if there was an error +*/ +function delpost($id) { + global $srv; + global $token; + + if (!is_null($token)) { + $curl = curl_init(); + curl_setopt($curl, CURLOPT_HEADER, false); + curl_setopt($curl, CURLOPT_POST, 1); + curl_setopt($curl, CURLOPT_URL, "https://$srv/api/v1/statuses/$id"); + curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE"); + curl_setopt($curl, CURLOPT_HTTPHEADER, array( + 'Authorization: Bearer ' . $token + )); + + curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); + $result = curl_exec($curl); + curl_close($curl); + $result = json_decode($result, true); + if (empty($result)) { + return "1"; + } + else { + return "0"; + } + } +} + +/* function to issue a vote to a poll + - $id is the id of the poll to vote on + + - $choices is a comma separated string of the choices that will be voted. +*/ +function vote($poll, $choices) { + global $srv; + global $token; + if (!is_null($token)) { + $cSession = curl_init(); + curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/polls/$poll/votes"); + curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true); + curl_setopt($cSession, CURLOPT_POST, 1); + curl_setopt($cSession, CURLOPT_HTTPHEADER, array( + 'Authorization: Bearer ' . $token + )); + $query = ""; + $choicelist = explode(",",$choices); + foreach($choicelist as $choice){ + $query .= "choices[]=$choice&"; + } + + curl_setopt($cSession, CURLOPT_POSTFIELDS, $query); + $result = curl_exec($cSession); + curl_close($cSession); + return $result; + } + else { + return false; + } +} + + +/* function to send a new post to the logged in instance + - $text = the body of the message + - $media = array of uploaded media id's + - $reply = the id of a post being replied to, can be null. + - $markdown = specify if the post uses markdown (unused at this moment) + - $scope = the scope of the post (public, private, unlisted or direct) + - $sensitive = bool to specify if the post media is sensitive + - $spoiler = string of the title of the post + + - returns the json of the api response + */ +function sendpost($text, $media, $reply = null, $markdown = false, $scope = "public", $sensitive = false, $spoiler = false) { + global $srv; + global $token; + if (!is_null($token)) { + + $cSession = curl_init(); + curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/statuses"); + curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true); + curl_setopt($cSession, CURLOPT_POST, 1); + curl_setopt($cSession, CURLOPT_HTTPHEADER, array( + 'Authorization: Bearer ' . $token + )); + $query = ""; + $query .= "status=" . urlencode(html_entity_decode($text, ENT_QUOTES)) . "&visibility=" . $scope;; + if (!is_null($reply) && $reply != "null") { + $query .= "&in_reply_to_id=" . $reply; + } + if ($markdown == true) { + $query .= "&content_type=text/markdown"; + } + if ($sensitive == 'true') { + $query .= "&sensitive=true"; + } + if ($spoiler == true) { + $query .= "&spoiler_text=" . $spoiler; + } + + if (!is_null($media)) { + foreach ($media as $mid) { + $query .= "&media_ids[]=" . $mid; + } + } + + curl_setopt($cSession, CURLOPT_POSTFIELDS, $query); + $result = curl_exec($cSession); + curl_close($cSession); + return $result; + } + else { + return false; + } +} + +/* uploads a file to the logged in instance. + - $file = path of the file to upload on local storage + + - returns an array where: + 0: the ID of the uploaded file + 1: the url of the file (if the file isn't an image, returns a placeholder) + */ +function uploadpic($file) { + global $srv; + global $token; + if (!is_null($token)) { + $mime = get_mime($file); + $info = pathinfo($file); + $name = $info['basename']; + $output = new CURLFile($file, $mime, $name); + $cSession = curl_init(); + curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/media"); + curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true); + curl_setopt($cSession, CURLOPT_POST, 1); + curl_setopt($cSession, CURLOPT_HTTPHEADER, array( + 'Authorization: Bearer ' . $token + )); + curl_setopt($cSession, CURLOPT_POSTFIELDS, array( + 'file' => $output + )); + $result = curl_exec($cSession); + curl_close($cSession); + $array = json_decode($result, true); + + $ext = explode(".", $array['url']); + $ext = end($ext); + $ext = explode("?", $ext) [0]; + + if (in_array($ext, array( + 'jpg', + 'jpeg', + 'gif', + 'png', + 'svg' + ))) { + $file = $array['url']; + } + else { + $file = "img/doc.png"; + } + return json_encode(array( + $array['id'], + $file + )); + } + else { + return false; + } +} + +/* this is used to register DashFE as an application on an instance + mostly used when logging in + - $instance = the url of the instance where to log in + + - returns the array conversion of the json response + */ +function register_app($instance) { + global $setting; + + $cSession = curl_init(); + curl_setopt($cSession, CURLOPT_URL, "https://$instance/api/v1/apps"); + curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true); + curl_setopt($cSession, CURLOPT_HEADER, false); + curl_setopt($cSession, CURLOPT_POST, 1); + curl_setopt($cSession, CURLOPT_POSTFIELDS, http_build_query(array( + 'client_name' => $setting['appname'], + 'redirect_uris' => $setting['url'], + 'scopes' => 'read write follow' + ))); + $result = curl_exec($cSession); + curl_close($cSession); + + return json_decode($result, true); +} + +/* this function will get all the notes (reblogs and favs) that has an specified post + - $thread = the id of the post queried + + - returns an array with all the notes + each note is another array where: + 0 = type of note ("reb" reblog or "fab" favorite) + 1 = array of the details of the user, converted from the json of the api response. + */ +function getnotes($thread) { + global $user_settings; + global $token; + global $srv; + global $setting; + global $logedin; + @$reb = array( + json_decode(file_get_contents("https://$srv/api/v1/statuses/" . $thread . "/reblogged_by") , true) + ); + @$fab = array( + json_decode(file_get_contents("https://$srv/api/v1/statuses/" . $thread . "/favourited_by") , true) + ); + $limit = (count($reb[0]) > count($fab[0]) ? count($reb[0]) - 1 : count($fab[0]) - 1); + + $notes = array(); + $index = 0; + + for ($i = 0;$i <= $limit;$i++) { + if (isset($reb[0][$i])) { + $notes[$index][0] = "reb"; + $notes[$index][1] = $reb[0][$i]; + $index++; + } + if (isset($fab[0][$i])) { + $notes[$index][0] = "fav"; + $notes[$index][1] = $fab[0][$i]; + $index++; + } + } + + return $notes; +} + +/* this function will fetch replies of a post + - $thread = the id of the post from where to fetch replies + - $since = id of a post. If specified, the function will fetch the replies + only since the specified id. + + - returns an array with all the replies + each element is another array where: + 'mode' = type of the reply (ancestor or descendant) + 'content' = array of the contents of the reply, converted from the json of the api response. + */ +function getreplies($thread, $since = false) { + global $user_settings; + global $token; + global $srv; + global $setting; + global $logedin; + $context = json_decode(context($thread) , true); + $array = array(); + if (!empty($context['ancestors'])) { + if ($since == false) { + foreach ($context['ancestors'] as $elem) { + $elem['type'] = 'ancestor'; + $array[] = $elem; + } + } + } + $flag = 0; + if (!empty($context['descendants'])) { + foreach ($context['descendants'] as $elem) { + if (($since != false && $flag == 1) || $since == false) { + $elem['type'] = 'descendant'; + $array[] = $elem; + } + if ($since != false && $elem['id'] == $since) { + $flag = 1; + } + } + } + + $replies = array(); + + foreach ($array as $item) { + $reply['mode'] = ""; + if ($item['type'] == 'ancestor') { + $reply['mode'] = "ancestor"; + } + + $replies[] = array( + 'mode' => $reply['mode'], + 'content' => $item + ); + } + + return $replies; +} + + +/* this function takes some options from the init.php file and the user_settings cookie + and fetches all the posts of the appropiate timeline + - $query = an array with a set of elements generated by the file "include/init.php" + + - returns an array of the posts list fetched converted from the json response of the api. + */ +function timeline($query) { + global $token; + global $srv; + + $notes = ""; + + $hq = array(); + $hq['limit'] = 30; + $hq['only_media'] = ($query['text'] == "off" ? 'true' : 'false'); + if ($query['next']){ + $hq['max_id'] = $query['next']; + $next = $query['next']; + } elseif ($query['since']) { + $hq['since_id'] = $query['since']; + } + + switch ($query['mode']) { + case "home": + $array = api_get("timelines/home?".http_build_query($hq)); + break; + + case "federated": + $array = api_get("timelines/public?".http_build_query($hq)); + break; + + case "tag": + $array = api_get("timelines/tag/" . $query['tag'] . "?".http_build_query($hq)); + break; + + case "local": + $array = api_get("timelines/public?local=true&".http_build_query($hq)); + break; + + case "user": + $array = api_get("accounts/" . $query['user'] . "/statuses?".http_build_query($hq)); + break; + + case "thread": + $array = array( + api_get("statuses/" . $query['thread']) + ); + break; + + case "favourites": + $array = api_get("favourites?".http_build_query($hq)); + break; + + case "direct": + $array = api_get("timelines/direct?".http_build_query($hq)); + break; + + case "list": + $array = api_get("timelines/list/" . $query['list'] . "?".http_build_query($hq)); + break; + + case "bookmarks": + $array = api_get("bookmarks?".http_build_query($hq)); + break; + + case "search": + $array = api_getv2("search?limit=40&q=".$query['search']."{$next}")['statuses']; + break; + + case "account": + $info = api_get("accounts/verify_credentials"); + $array = api_get("accounts/" . $info['id'] . "/statuses?".http_build_query($hq)); + break; + + default: + $array = api_get("timelines/public?".http_build_query($hq)); + break; + } + + if (!is_array($array)) { + return false; + } + + $next = end($array) ['id']; + $thread = array(); + /* + foreach ($array as $elem) { + if ($query['replies'] == "on" || $query['mode'] == "thread") { + $thread[] = $elem; + } + else { + if ($elem['in_reply_to_id'] == null) { + $thread[] = $elem; + } + } + + }*/ + + foreach ($array as $elem) { + if ($query['replies'] == "on" || $query['mode'] == "thread") { + $thread[] = $elem; + } + else { + if ($elem['in_reply_to_id'] == null || $elem['in_reply_to_account_id'] == $query['uid']) { + $thread[] = $elem; + } else { + $rel = api_get("accounts/relationships?id=" . $elem['in_reply_to_account_id']); + if ($rel[0]['following']){ + $thread[] = $elem; + } + } + } + + } + + return $thread; +} + + + + + + + + + + +// FUNCTIONS THAT HAVE TO DO WITH RENDERING STUFF FOR THE PAGE + /* this function is used to generate the html code of a poll */ function renderPoll($elem) { global $logedin; @@ -410,204 +795,53 @@ function render_reply($item) { return $result; } -/* a function to fav or unfav a specified post */ -function favourite($post, $mode) { - $result = api_post(($mode == true ? "statuses/$post/favourite" : "statuses/$post/unfavourite"),array()); - - if (isset($result['favourites_count'])) { - return $result['favourites_count']; - } - else { - return "error"; +/* this is the same as above but is used on other places, like user bios and places where the +shortcodes and the emoji url is defined in the same place */ +function emojify($string, $emojis, $size = 40) { + foreach ($emojis as $emoji) { + $string = str_replace(":" . $emoji['shortcode'] . ":", "" . $emoji[", $string); } + return $string; } -/* a function to reblog or unreblog a specified post */ -function reblog($post, $mode) { - $result = api_post(($mode == true ? "statuses/$post/reblog" : "statuses/$post/unreblog"),array()); - if (isset($result['reblog']['reblogs_count'])) { - return $result['reblog']['reblogs_count']; - } - elseif (isset($result['reblogs_count'])) { - return $result['reblogs_count']; - } - else { - return "error"; - } -} - -/* function to delete a post */ -function delpost($id) { - global $srv; - global $token; - - if (!is_null($token)) { - $curl = curl_init(); - curl_setopt($curl, CURLOPT_HEADER, false); - curl_setopt($curl, CURLOPT_POST, 1); - curl_setopt($curl, CURLOPT_URL, "https://$srv/api/v1/statuses/$id"); - curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "DELETE"); - curl_setopt($curl, CURLOPT_HTTPHEADER, array( - 'Authorization: Bearer ' . $token - )); - - curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); - $result = curl_exec($curl); - curl_close($curl); - $result = json_decode($result, true); - if (empty($result)) { - return "1"; - } - else { - return "0"; +/* This function displays the emoji list of an instance based on a search + string given on $val */ +function emoji_list($val){ + $emojilist = api_get("/custom_emojis"); + $c = 0; + $return = ""; + foreach ($emojilist as $emoji){ + if (starts_with($emoji['shortcode'],$val) && $c < 50){ + $return .= ""; + $c++; } } -} - -function vote($poll, $choices) { - global $srv; - global $token; - if (!is_null($token)) { - $cSession = curl_init(); - curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/polls/$poll/votes"); - curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true); - curl_setopt($cSession, CURLOPT_POST, 1); - curl_setopt($cSession, CURLOPT_HTTPHEADER, array( - 'Authorization: Bearer ' . $token - )); - $query = ""; - $choicelist = explode(",",$choices); - foreach($choicelist as $choice){ - $query .= "choices[]=$choice&"; - } - - curl_setopt($cSession, CURLOPT_POSTFIELDS, $query); - $result = curl_exec($cSession); - curl_close($cSession); - return $result; - } - else { - return false; - } -} - - -/* function to send a new post to the logged in instance - it takes the media ids as an array */ -function sendpost($text, $media, $reply, $markdown = false, $scope, $sensitive, $spoiler = false) { - global $srv; - global $token; - if (!is_null($token)) { - - $cSession = curl_init(); - curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/statuses"); - curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true); - curl_setopt($cSession, CURLOPT_POST, 1); - curl_setopt($cSession, CURLOPT_HTTPHEADER, array( - 'Authorization: Bearer ' . $token - )); - $query = ""; - $query .= "status=" . urlencode(html_entity_decode($text, ENT_QUOTES)) . "&visibility=" . $scope;; - if (!is_null($reply) && $reply != "null") { - $query .= "&in_reply_to_id=" . $reply; - } - if ($markdown == true) { - $query .= "&content_type=text/markdown"; - } - if ($sensitive == 'true') { - $query .= "&sensitive=true"; - } - if ($spoiler == true) { - $query .= "&spoiler_text=" . $spoiler; - } - - if (!is_null($media)) { - foreach ($media as $mid) { - $query .= "&media_ids[]=" . $mid; + if ($c < 50){ + foreach ($emojilist as $emoji){ + if ((contains($emoji['shortcode'],$val) && !starts_with($emoji['shortcode'],$val)) && $c < 50){ + $return .= ""; + $c++; } } - - curl_setopt($cSession, CURLOPT_POSTFIELDS, $query); - $result = curl_exec($cSession); - curl_close($cSession); - return $result; - } - else { - return false; } + + return $return; } -/* uploads a file to the logged in instance. - the function takes the file from the local storage */ -function uploadpic($file) { - global $srv; - global $token; - if (!is_null($token)) { - $mime = get_mime($file); - $info = pathinfo($file); - $name = $info['basename']; - $output = new CURLFile($file, $mime, $name); - $cSession = curl_init(); - curl_setopt($cSession, CURLOPT_URL, "https://$srv/api/v1/media"); - curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true); - curl_setopt($cSession, CURLOPT_POST, 1); - curl_setopt($cSession, CURLOPT_HTTPHEADER, array( - 'Authorization: Bearer ' . $token - )); - curl_setopt($cSession, CURLOPT_POSTFIELDS, array( - 'file' => $output - )); - $result = curl_exec($cSession); - curl_close($cSession); - $array = json_decode($result, true); - - $ext = explode(".", $array['url']); - $ext = end($ext); - $ext = explode("?", $ext) [0]; - - if (in_array($ext, array( - 'jpg', - 'jpeg', - 'gif', - 'png', - 'svg' - ))) { - $file = $array['url']; - } - else { - $file = "img/doc.png"; - } - return json_encode(array( - $array['id'], - $file - )); +function contact_search($val){ + global $user_settings; + $return = ""; + $list = api_get("/accounts/search?q=".$val); + foreach ($list as $contact){ + $return .= "
+
+
+ ".emojify($contact['display_name'], $contact['emojis'], 15)."
+ ".$contact['acct']." +
+
"; } - else { - return false; - } -} - -/* this is used to register DashFE as an application on an instance - mostly used when logging in - */ - -function register_app($instance) { - global $setting; - - $cSession = curl_init(); - curl_setopt($cSession, CURLOPT_URL, "https://$instance/api/v1/apps"); - curl_setopt($cSession, CURLOPT_RETURNTRANSFER, true); - curl_setopt($cSession, CURLOPT_HEADER, false); - curl_setopt($cSession, CURLOPT_POST, 1); - curl_setopt($cSession, CURLOPT_POSTFIELDS, http_build_query(array( - 'client_name' => $setting['appname'], - 'redirect_uris' => $setting['url'], - 'scopes' => 'read write follow' - ))); - $result = curl_exec($cSession); - curl_close($cSession); - - return json_decode($result, true); + return $return; } /* This function will fetch and render all the notifications since an $id @@ -725,204 +959,6 @@ function getnotif($id = false, $max = false) { } } -/* this function will get all the notes (reblogs and favs) that has an specified post */ -function getnotes($thread) { - global $user_settings; - global $token; - global $srv; - global $setting; - global $logedin; - @$reb = array( - json_decode(file_get_contents("https://$srv/api/v1/statuses/" . $thread . "/reblogged_by") , true) - ); - @$fab = array( - json_decode(file_get_contents("https://$srv/api/v1/statuses/" . $thread . "/favourited_by") , true) - ); - $limit = (count($reb[0]) > count($fab[0]) ? count($reb[0]) - 1 : count($fab[0]) - 1); - - $notes = array(); - $index = 0; - - for ($i = 0;$i <= $limit;$i++) { - if (isset($reb[0][$i])) { - $notes[$index][0] = "reb"; - $notes[$index][1] = $reb[0][$i]; - $index++; - } - if (isset($fab[0][$i])) { - $notes[$index][0] = "fav"; - $notes[$index][1] = $fab[0][$i]; - $index++; - } - } - - return $notes; -} - -/* this function will fetch all the replies that a post have - or will fetch only the ones after a specified ($since) post id -*/ -function getreplies($thread, $since = false) { - global $user_settings; - global $token; - global $srv; - global $setting; - global $logedin; - $context = json_decode(context($thread) , true); - $array = array(); - if (!empty($context['ancestors'])) { - if ($since == false) { - foreach ($context['ancestors'] as $elem) { - $elem['type'] = 'ancestor'; - $array[] = $elem; - } - } - } - $flag = 0; - if (!empty($context['descendants'])) { - foreach ($context['descendants'] as $elem) { - if (($since != false && $flag == 1) || $since == false) { - $elem['type'] = 'descendant'; - $array[] = $elem; - } - if ($since != false && $elem['id'] == $since) { - $flag = 1; - } - } - } - - $replies = array(); - - foreach ($array as $item) { - $reply['mode'] = ""; - if ($item['type'] == 'ancestor') { - $reply['mode'] = "ancestor"; - } - - $replies[] = array( - 'mode' => $reply['mode'], - 'content' => $item - ); - } - - return $replies; -} - - -/* this function takes some options from the init.php file and the user_settings cookie - and fetches all the posts of the appropiate timeline */ -function timeline($query) { - global $token; - global $srv; - - $notes = ""; - - $hq = array(); - $hq['limit'] = 30; - $hq['only_media'] = ($query['text'] == "off" ? 'true' : 'false'); - if ($query['next']){ - $hq['max_id'] = $query['next']; - $next = $query['next']; - } elseif ($query['since']) { - $hq['since_id'] = $query['since']; - } - - switch ($query['mode']) { - case "home": - $array = api_get("timelines/home?".http_build_query($hq)); - break; - - case "federated": - $array = api_get("timelines/public?".http_build_query($hq)); - break; - - case "tag": - $array = api_get("timelines/tag/" . $query['tag'] . "?".http_build_query($hq)); - break; - - case "local": - $array = api_get("timelines/public?local=true&".http_build_query($hq)); - break; - - case "user": - $array = api_get("accounts/" . $query['user'] . "/statuses?".http_build_query($hq)); - break; - - case "thread": - $array = array( - api_get("statuses/" . $query['thread']) - ); - break; - - case "favourites": - $array = api_get("favourites?".http_build_query($hq)); - break; - - case "direct": - $array = api_get("timelines/direct?".http_build_query($hq)); - break; - - case "list": - $array = api_get("timelines/list/" . $query['list'] . "?".http_build_query($hq)); - break; - - case "bookmarks": - $array = api_get("bookmarks?".http_build_query($hq)); - break; - - case "search": - $array = api_getv2("search?limit=40&q=".$query['search']."{$next}")['statuses']; - break; - - case "account": - $info = api_get("accounts/verify_credentials"); - $array = api_get("accounts/" . $info['id'] . "/statuses?".http_build_query($hq)); - break; - - default: - $array = api_get("timelines/public?".http_build_query($hq)); - break; - } - - if (!is_array($array)) { - return false; - } - - $next = end($array) ['id']; - $thread = array(); - /* - foreach ($array as $elem) { - if ($query['replies'] == "on" || $query['mode'] == "thread") { - $thread[] = $elem; - } - else { - if ($elem['in_reply_to_id'] == null) { - $thread[] = $elem; - } - } - - }*/ - - foreach ($array as $elem) { - if ($query['replies'] == "on" || $query['mode'] == "thread") { - $thread[] = $elem; - } - else { - if ($elem['in_reply_to_id'] == null || $elem['in_reply_to_account_id'] == $query['uid']) { - $thread[] = $elem; - } else { - $rel = api_get("accounts/relationships?id=" . $elem['in_reply_to_account_id']); - if ($rel[0]['following']){ - $thread[] = $elem; - } - } - } - - } - - return $thread; -} - /* this function takes in a whole post entity and spits out the HTMLfied text of the post with the urls and @handles linkified and all the emojis replaced */ @@ -977,6 +1013,85 @@ function processText($elem) { } + + + + + + + + + + + + +// OTHER FUNCTIONS + +/* the purpose of this function is to encode the auth token + it is not used for now */ +function msc($string, $action = 'e') { + // you may change these values to your own + $secret_key = 'yAmfVhZwm0749FSY24dC'; + $secret_iv = 'm37uvAeKjYLKdI1lPkcJ'; + + $output = false; + $encrypt_method = "AES-256-CBC"; + $key = hash('sha256', $secret_key); + $iv = substr(hash('sha256', $secret_iv) , 0, 16); + + if ($action == 'e') { + $output = base64_encode(openssl_encrypt($string, $encrypt_method, $key, 0, $iv)); + } + else if ($action == 'd') { + $output = openssl_decrypt(base64_decode($string) , $encrypt_method, $key, 0, $iv); + } + + return $output; +} + +/* this function extracts the urls from a text and return them in an array */ +function get_urls($input) { + $pattern = '$(https?://[a-z0-9_./?=&-~]+)(?![^<>]*>)$i'; + if (preg_match_all($pattern, $input, $matches)) { + list($dummy, $links) = ($matches); + return $links; + } + return false; +} + +/* general function to check if one strings starts with a given search */ +function starts_with($string,$search){ + if (substr(strtolower($string),0,strlen($search)) == strtolower($search)){ + return true; + } + return false; +} + +/* general function to check if one strings contains with a given search */ +function contains($string,$search){ + if (is_numeric(strpos(strtolower($string),strtolower($search)))){ + return true; + } + return false; +} + +/* this function just reduces an image to a 1x1 pixel image to get the overall color. + */ +function averageColor($url) { + @$image = imagecreatefromstring(file_get_contents($url)); + + if (!$image) { + $mainColor = "CCCCCC"; + } + else { + $thumb = imagecreatetruecolor(1, 1); + imagecopyresampled($thumb, $image, 0, 0, 0, 0, 1, 1, imagesx($image) , imagesy($image)); + $mainColor = strtoupper(dechex(imagecolorat($thumb, 0, 0))); + } + + return $mainColor; +} + /* function used in the process of uploading a file */ function get_mime($filename) { $result = new finfo(); diff --git a/js/scripts.js b/js/scripts.js index d7f1782..39505be 100644 --- a/js/scripts.js +++ b/js/scripts.js @@ -18,14 +18,12 @@ $('body').on('click', '.toggleblur', function(e) { }); $('body').on('click', '#emoji', function(e) { - $(this).closest(".element").find('.picker').css("display","none"); - $(this).closest(".element").find('.picker').css("display","block"); + $(this).closest(".element").find('.picker').toggle(); $('#emojisearch').focus(); }); $('body').on('click', '#contact', function(e) { - $(this).closest(".element").find('.contactpicker').css("display","none"); - $(this).closest(".element").find('.contactpicker').css("display","block"); + $(this).closest(".element").find('.contactpicker').toggle(); $('#contactsearch').focus(); }); @@ -43,11 +41,6 @@ $('body').on('click', '.contact', function(e) { $(this).closest(".element").find("#status").val(boxtxt + code+" "); }); -$('body').on('click', '.close', function(e) { - $(this).next().html(""); - $(this).parent('div').css("display","none"); -}); - $(document).on("change paste keyup","#emojisearch",function () { var val = $(this).val(); var list = $(this).closest('.element').find('.emojilist'); diff --git a/layout/header.php b/layout/header.php index 1b23aa0..e766e5d 100644 --- a/layout/header.php +++ b/layout/header.php @@ -213,8 +213,8 @@ endif; ?> - - + +
diff --git a/layout/sidebar.php b/layout/sidebar.php index 0e99a15..b3792c5 100644 --- a/layout/sidebar.php +++ b/layout/sidebar.php @@ -11,10 +11,6 @@ if ($logedin || $tl['mode'] == 'user') {
-
-
-
-
- - + +
diff --git a/themes/longhorn/css/poorly.html b/themes/longhorn/css/poorly.html new file mode 100644 index 0000000..fbe5c9f --- /dev/null +++ b/themes/longhorn/css/poorly.html @@ -0,0 +1,1138 @@ +
  • A Smaller Version
  • +
  • Go-To Bird
  • +
  • Nothing Good
  • +
  • Some of the Locals
  • +
  • So Many Ideas
  • +
  • Not Good Not Bad
  • +
  • Where is Everyone
  • +
  • Turtle Sunset
  • +
  • Looking At Me
  • +
  • Don’t Know Much
  • +
  • Buried Treasure
  • +
  • Starting My Work
  • +
  • Cheese
  • +
  • Squirrel Who Stands On a Ball
  • +
  • Kevin’s Success
  • +
  • Here’s Where
  • +
  • Shit
  • +
  • Deep Trouble
  • +
  • Do Something
  • +
  • Nothing Scares Me
  • +
  • Snake Time
  • +
  • A Question
  • +
  • Am I
  • +
  • Trash Bird’s Success
  • +
  • Magic is Real
  • +
  • Just Ask
  • +
  • Impersonating Santa
  • +
  • Not Santa
  • +
  • After This
  • +
  • A Cookie
  • +
  • Here to Fight
  • +
  • Everything is Great
  • +
  • Don’t Need
  • +
  • New book!
  • +
  • Valuable
  • +
  • Tell You
  • +
  • Food Chain
  • +
  • Thrilled
  • +
  • A Good Day
  • +
  • Busy
  • +
  • Accessories
  • +
  • Picture of Me
  • +
  • Where
  • +
  • King of Ghosts
  • +
  • Okay
  • +
  • To Be Amazing
  • +
  • How Many Fish
  • +
  • Ghost and Vampire
  • +
  • Welcome to the Internet
  • +
  • Feeling Nostalgic
  • +
  • My Tree
  • +
  • A Word
  • +
  • Pond
  • +
  • Shades
  • +
  • Give Me
  • +
  • Those Sunglasses
  • +
  • Don’t
  • +
  • Please Rate
  • +
  • For Just Once
  • +
  • A Nice Day
  • +
  • Tired
  • +
  • Love Song
  • +
  • Sunglasses
  • +
  • Be Better
  • +
  • Discussion
  • +
  • Welcome
  • +
  • Ants Again
  • +
  • Delaying
  • +
  • Ideas
  • +
  • How Much
  • +
  • What I Do
  • +
  • Ghosts
  • +
  • Keeps You Going
  • +
  • Changed
  • +
  • Exchange
  • +
  • Top Three
  • +
  • Again
  • +
  • Mysteries
  • +
  • Nice Day
  • +
  • Look Like That
  • +
  • Learn
  • +
  • Best
  • +
  • Faking
  • +
  • Found the Ocean
  • +
  • My Ideas
  • +
  • Like Me
  • +
  • Seahorse
  • +
  • Probably the First
  • +
  • Hot Shit
  • +
  • Submarines
  • +
  • This Good
  • +
  • A Cool Guy
  • +
  • Roommate
  • +
  • Turtles
  • +
  • Reason
  • +
  • Being a Bird
  • +
  • Years
  • +
  • The Robot
  • +
  • Favorite Tree
  • +
  • Got Everything
  • +
  • Change
  • +
  • Easy
  • +
  • Future
  • +
  • A Plan
  • +
  • Self Employed
  • +
  • Time of My Life
  • +
  • Mr. Worm
  • +
  • A Dog
  • +
  • Serious Turtle
  • +
  • Nothing to Say
  • +
  • Next Level
  • +
  • Not Scared
  • +
  • The Spill
  • +
  • Eat You
  • +
  • Thinking
  • +
  • What Could Be
  • +
  • Been Saving
  • +
  • Would You
  • +
  • Never Seen
  • +
  • Advice
  • +
  • The Ocean
  • +
  • Thank You
  • +
  • Want it All
  • +
  • Plant
  • +
  • A Snail
  • +
  • Blocked
  • +
  • That Cool
  • +
  • My Thing
  • +
  • Turtle is Mad
  • +
  • Hunter
  • +
  • Never Said
  • +
  • Rabbit Village
  • +
  • Great
  • +
  • Warning
  • +
  • New Stuff
  • +
  • Mister Nice
  • +
  • Enough
  • +
  • Coin
  • +
  • Did You
  • +
  • Need
  • +
  • Should’ve
  • +
  • Your Help
  • +
  • Ready
  • +
  • Ants!
  • +
  • One Little Ant
  • +
  • Awesome
  • +
  • Anybody
  • +
  • The First
  • +
  • Turtle and Bird
  • +
  • Too cool
  • +
  • Internet Down
  • +
  • Beautiful Robot
  • +
  • Breakfast
  • +
  • Boo
  • +
  • Stand Here
  • +
  • That Bird
  • +
  • Website Bird
  • +
  • Hello
  • +
  • House
  • +
  • Wrong
  • +
  • New Stick
  • +
  • Scares
  • +
  • The Loop
  • +
  • Haunt
  • +
  • All Mad
  • +
  • Season
  • +
  • Good or Bad
  • +
  • Music
  • +
  • Too Much
  • +
  • Look at Me
  • +
  • Everything
  • +
  • The Park
  • +
  • Fortune
  • +
  • World of Imagination
  • +
  • Weird
  • +
  • Goals
  • +
  • Space Snake
  • +
  • Focus
  • +
  • My Curse
  • +
  • Leather
  • +
  • How Are You
  • +
  • Great Ideas
  • +
  • Can’t Stop
  • +
  • City Tree
  • +
  • Do it, Turtle
  • +
  • Dwell
  • +
  • Things and Stuff
  • +
  • Joy
  • +
  • Mouse in the Ocean
  • +
  • Sometimes
  • +
  • That Dog
  • +
  • This Song
  • +
  • It’s Been Nice
  • +
  • Be Cool
  • +
  • Ernesto Got Money
  • +
  • Pretty Cool
  • +
  • Days
  • +
  • Are You
  • +
  • Rocks
  • +
  • How High
  • +
  • We’re Fish
  • +
  • Something
  • +
  • Dreamed
  • +
  • This Turtle
  • +
  • Like
  • +
  • Oceans
  • +
  • Crab
  • +
  • Trees
  • +
  • Stone Age
  • +
  • Famous
  • +
  • Trash
  • +
  • Told You
  • +
  • Report
  • +
  • Make Changes
  • +
  • Robot Ghost
  • +
  • Hole
  • +
  • Skull
  • +
  • The Tunnel
  • +
  • Busy
  • +
  • Go to Hell
  • +
  • Good Morning
  • +
  • Estimates
  • +
  • Calm
  • +
  • Never Assume
  • +
  • My Problems
  • +
  • Mail
  • +
  • Statue
  • +
  • The Cave
  • +
  • Message for Kevin
  • +
  • The Horse
  • +
  • Pay Me
  • +
  • New Phone
  • +
  • New Things
  • +
  • Return to Nature
  • +
  • Phone
  • +
  • Stars
  • +
  • Big Dreams
  • +
  • Jump Rope
  • +
  • Old Friend
  • +
  • Yourself
  • +
  • Know
  • +
  • Grandeur
  • +
  • Robot Emotion
  • +
  • Findings
  • +
  • Trap III: Peace
  • +
  • Trap II: Mouse Problems
  • +
  • Trap
  • +
  • Unsure
  • +
  • Stone Cold
  • +
  • Ghost Money
  • +
  • Small for a Monster
  • +
  • Bird Police
  • +
  • Challenge
  • +
  • Go Big
  • +
  • Bee Computer
  • +
  • Sit Around
  • +
  • The Future
  • +
  • Pretty Brave
  • +
  • Circles
  • +
  • The Ghost Of
  • +
  • Sad Dude
  • +
  • Party
  • +
  • Enhance
  • +
  • Bird and Cage
  • +
  • Been There
  • +
  • Cat Food
  • +
  • Kevin the Magical Bird
  • +
  • Trying
  • +
  • Everyone
  • +
  • Not Worried
  • +
  • Credit
  • +
  • Meow Meow
  • +
  • Classic Novel
  • +
  • Simpler Times
  • +
  • With the Humans
  • +
  • Changes
  • +
  • Among Us
  • +
  • A Theory
  • +
  • Excited
  • +
  • Vampire Lessons
  • +
  • Rivals
  • +
  • Hyperbole
  • +
  • Poltergeist
  • +
  • First Star
  • +
  • Cousin Seagull
  • +
  • Fresh Start
  • +
  • About the Money
  • +
  • Scientists
  • +
  • Virtual Shapes
  • +
  • Nervous
  • +
  • Lord Chronos
  • +
  • Skateboard
  • +
  • The King
  • +
  • Ketchup
  • +
  • Cheep-Cheep
  • +
  • Old Habits
  • +
  • Nobody Worry
  • +
  • Ghost Club
  • +
  • Pigeon Noir
  • +
  • Squirrel Stores Food
  • +
  • Illusion
  • +
  • Basic Life Tutorial
  • +
  • The Tale of Moth Ghost
  • +
  • Kevin Lives the High Life
  • +
  • Attention
  • +
  • Never Assume
  • +
  • Not as Smart
  • +
  • A Common Type
  • +
  • A Volcano
  • +
  • Estimate
  • +
  • Not the Type
  • +
  • Ernesto’s Fear
  • +
  • Recent Ghost
  • +
  • Wouldn’t Say
  • +
  • Stress
  • +
  • An Idea
  • +
  • What Time
  • +
  • Ghosts Are Real
  • +
  • Out of Here
  • +
  • Philosophy
  • +
  • Confused
  • +
  • Ants
  • +
  • With Your Hands
  • +
  • Anti-Crime Dog
  • +
  • Welcome to Work
  • +
  • Lovable Yet Dangerous
  • +
  • Kevin’s Quest
  • +
  • Priorities
  • +
  • All the Ideas
  • +
  • A Monster
  • +
  • Don’t Know Things
  • +
  • The Woods
  • +
  • My Edge
  • +
  • Beach Town
  • +
  • Concerned
  • +
  • The Latest Thing
  • +
  • Computer Love
  • +
  • Mice in Space
  • +
  • Incredible Feeling
  • +
  • A Good Person
  • +
  • New Book! Comics for a Strange World
  • +
  • Exclusive Interview
  • +
  • All the Answers
  • +
  • Everyone Lives Forever
  • +
  • Kevin’s Sadness
  • +
  • Freak Out
  • +
  • Marmalade
  • +
  • Off the Ground
  • +
  • All Wrong
  • +
  • Poorly Drawn Lines + Patreon
  • +
  • Action
  • +
  • Kevin’s Project
  • +
  • Pet Now
  • +
  • News of Inventions
  • +
  • Rover
  • +
  • The Fun
  • +
  • Squirrel of Encouragement
  • +
  • Routine Checkup
  • +
  • A Quick Update
  • +
  • The Media
  • +
  • No Doors
  • +
  • Declutter
  • +
  • Realistic
  • +
  • Bad Mood Thesaurus
  • +
  • Ghost vs. World
  • +
  • Bank Card
  • +
  • Fun Robot
  • +
  • Request
  • +
  • Boat Question
  • +
  • Nothing New
  • +
  • Drill
  • +
  • What a Day
  • +
  • Grapefruit
  • +
  • Wrong With Dog
  • +
  • Naive Dramatic
  • +
  • Omen
  • +
  • Next Level
  • +
  • Bury Ourselves
  • +
  • Support
  • +
  • Mouse Problem
  • +
  • Bring Me Down
  • +
  • Smart
  • +
  • Great Pic
  • +
  • Rob
  • +
  • Soar
  • +
  • Not Mad
  • +
  • Young
  • +
  • Lucky Ant
  • +
  • Back on the Horse
  • +
  • Like a Mountain
  • +
  • All We Have
  • +
  • Ambitions
  • +
  • Catching Up
  • +
  • The Dream
  • +
  • Srahaaaj’s New Year
  • +
  • Srahaaaj and Kevin
  • +
  • Virtual
  • +
  • Knowledge
  • +
  • Gateway Beverage
  • +
  • Kingdom
  • +
  • Early Internet
  • +
  • Lobster
  • +
  • The Present
  • +
  • Surprise
  • +
  • Kloob
  • +
  • Ghost Business
  • +
  • Saying Things
  • +
  • A Reasonable Idea
  • +
  • Assume the Best
  • +
  • Tamagotchi
  • +
  • Feed the Animals
  • +
  • Doom
  • +
  • Kevin Finds Drugs
  • +
  • Shell
  • +
  • City Pigeon
  • +
  • Breakthrough
  • +
  • Earth
  • +
  • Dog and Human
  • +
  • Cool Vampire
  • +
  • Cheebo
  • +
  • Going Pro
  • +
  • Unfair to Spiders
  • +
  • Symbiosis
  • +
  • Like a Movie
  • +
  • English Muffin
  • +
  • Political Cartoon
  • +
  • SmartVac
  • +
  • Bird Government
  • +
  • Weaknesses
  • +
  • The Mountain
  • +
  • True Nature
  • +
  • Shakespeare
  • +
  • Junk Food
  • +
  • Raccoon Wants More
  • +
  • The Senator’s Plan
  • +
  • Computer Strap
  • +
  • Ghost Detective
  • +
  • Better Times
  • +
  • Spaceship Earth
  • +
  • Kim’s Crazy Idea
  • +
  • Man and Fly
  • +
  • Worried by Nature
  • +
  • Hopes for the Future
  • +
  • Duck on the Street
  • +
  • Autocorrect Tragedy
  • +
  • Time Traveled
  • +
  • Chess Computer
  • +
  • Campfire
  • +
  • Fun-Idea Felipe
  • +
  • First Historian
  • +
  • No Lie
  • +
  • Other Queen
  • +
  • A Good Thing
  • +
  • Old Gods
  • +
  • The Whales
  • +
  • Shapes Club
  • +
  • Snow Fox
  • +
  • The Truth
  • +
  • Stop Worrying
  • +
  • Mad
  • +
  • Boat Emergency
  • +
  • Rollerblade Boy
  • +
  • Picture Success
  • +
  • Checkup
  • +
  • Bike
  • +
  • Kevin and the Girl Bird
  • +
  • The Shop
  • +
  • Turn to History
  • +
  • Have it All
  • +
  • Bird Shopping
  • +
  • Not Much
  • +
  • The Flave
  • +
  • Ernesto’s Song
  • +
  • Fake People
  • +
  • Hiker
  • +
  • Motivational
  • +
  • Challenge Yourself
  • +
  • Dictionary Jones
  • +
  • Eat Your Carrots
  • +
  • Dumb Keith
  • +
  • Portal
  • +
  • Destiny
  • +
  • Magical Bus
  • +
  • Rule
  • +
  • Raccoon Trouble
  • +
  • Dr. Despair
  • +
  • Made Art
  • +
  • Knight
  • +
  • Much to Learn
  • +
  • The Villagers
  • +
  • Kevin’s Ideas
  • +
  • Appliances
  • +
  • Black and White
  • +
  • Stay In Bed
  • +
  • Old Days
  • +
  • Stove
  • +
  • Our Future In Space
  • +
  • The Sir Demands Drink
  • +
  • Mugged
  • +
  • Ideas
  • +
  • Pretty Town
  • +
  • The Order of Evil
  • +
  • Get You
  • +
  • Internet
  • +
  • Eggs
  • +
  • Kitty Man
  • +
  • The Lady
  • +
  • Fool
  • +
  • Pigeon
  • +
  • Operation Moon-Cow
  • +
  • Shark
  • +
  • The Package
  • +
  • Perhaps
  • +
  • Tickets
  • +
  • Just a Town
  • +
  • Feeding Time
  • +
  • The Research
  • +
  • Rally
  • +
  • At Home with Sue
  • +
  • Dentist
  • +
  • Napoleon
  • +
  • Pretty Bummed
  • +
  • Time Travel Boy
  • +
  • Destroy-Bot
  • +
  • Lost Touch
  • +
  • Anything
  • +
  • Undo
  • +
  • Secret Agent
  • +
  • Boundaries
  • +
  • The Snake
  • +
  • Hamsters
  • +
  • Get Me
  • +
  • The Case of the Traveling Taco
  • +
  • Money Trees
  • +
  • Keys On a Ledge
  • +
  • Guided
  • +
  • It’s the Future
  • +
  • So Then
  • +
  • Too Deep
  • +
  • Zoned Out
  • +
  • Seen Stuff
  • +
  • Being Human
  • +
  • Told Me
  • +
  • Smell That
  • +
  • Wise Hermit
  • +
  • Small Cat and the Asteroid
  • +
  • Spirit of Festivity
  • +
  • So Not Going To
  • +
  • Clouds
  • +
  • A Dream
  • +
  • Bradford Mouse
  • +
  • Jump
  • +
  • Heroes
  • +
  • Rainy Night
  • +
  • A Picture
  • +
  • Your Information
  • +
  • Still Locked in the Bathroom
  • +
  • GPS
  • +
  • Locked in the Bathroom
  • +
  • To Hell
  • +
  • Cool Hat
  • +
  • Helping
  • +
  • Finally
  • +
  • Question
  • +
  • Perfect
  • +
  • Dog Vid
  • +
  • Ants On the Moon
  • +
  • Treasure
  • +
  • Walk a Mile
  • +
  • Alan is a Man Now
  • +
  • Grow Up, Alan
  • +
  • Overload
  • +
  • Secretly Unemployed
  • +
  • My Fun Side
  • +
  • Don’t Talk
  • +
  • Don’t Talk to Me
  • +
  • My Friend Srahaaaj
  • +
  • Hello Bill
  • +
  • Poorly Drawn Lines: The Book – Get Your Copy!
  • +
  • Frown
  • +
  • Troboto Part III
  • +
  • Troboto Part II
  • +
  • Troboto
  • +
  • Look At Crag
  • +
  • Back Then
  • +
  • You Must
  • +
  • Motivated
  • +
  • The Road
  • +
  • Baby Pics
  • +
  • Texting
  • +
  • Very Important
  • +
  • Brunch
  • +
  • Work
  • +
  • Nice Glasses
  • +
  • Morning
  • +
  • The Time
  • +
  • Issues
  • +
  • Dog Astronaut
  • +
  • Alone Out Here
  • +
  • Lions
  • +
  • Tricia
  • +
  • Very Realistic Game
  • +
  • Slow Revenge of Worm
  • +
  • Good Joke
  • +
  • A Mouse
  • +
  • The Aging Rock
  • +
  • Kidnapped
  • +
  • Poorly Drawn Lines: THE BOOK
  • +
  • The Gods
  • +
  • The Moon
  • +
  • Not Moving
  • +
  • Begging
  • +
  • The Demon
  • +
  • Baby Drew a Picture
  • +
  • The Rain
  • +
  • Worm
  • +
  • How Cool
  • +
  • The Goose
  • +
  • Questions for the Ghost
  • +
  • Goat
  • +
  • Connections
  • +
  • Create
  • +
  • Pretend
  • +
  • Drama
  • +
  • Talk to Plants
  • +
  • What’s Wrong
  • +
  • Good Boy
  • +
  • To Better Understand
  • +
  • So Strong
  • +
  • The New Thing
  • +
  • Ant Problem
  • +
  • Dating Skills
  • +
  • Photo Shoot
  • +
  • Books
  • +
  • Stuff
  • +
  • Time
  • +
  • Secrets
  • +
  • Rent
  • +
  • The Big No
  • +
  • Hanging
  • +
  • Shadiest Robot
  • +
  • Narrator
  • +
  • Laugh
  • +
  • Good Lesson
  • +
  • Little Mysteries
  • +
  • Ghosts of the Dinosaurs
  • +
  • Good Times
  • +
  • Hospital
  • +
  • Emergency Time
  • +
  • Gets Me
  • +
  • The Beauty
  • +
  • Scarf
  • +
  • Young Man
  • +
  • The Children
  • +
  • Lanky Frog
  • +
  • The Same
  • +
  • Cobra
  • +
  • A Message
  • +
  • Authentic Present
  • +
  • Favorite
  • +
  • Real Magic
  • +
  • Pet Food Jokes
  • +
  • Party Baby
  • +
  • Too Far
  • +
  • Fit In
  • +
  • No Offense
  • +
  • Have to Tell Him
  • +
  • All the Good Things
  • +
  • Emotion Books
  • +
  • Pretty Different
  • +
  • Spring
  • +
  • Learn From Snail
  • +
  • Whole World
  • +
  • Mondays
  • +
  • Problems
  • +
  • Demons
  • +
  • Smoking
  • +
  • Subtle
  • +
  • Intelligent
  • +
  • Don’t Worry
  • +
  • Mouse Traps
  • +
  • Sing
  • +
  • Find Peace
  • +
  • Reality
  • +
  • Ingredients
  • +
  • Holidays
  • +
  • Special
  • +
  • Island
  • +
  • Nothing Day
  • +
  • Grandma’s Gift
  • +
  • Peacock
  • +
  • Jacket
  • +
  • Robo-Bird
  • +
  • Greed
  • +
  • Ears
  • +
  • Cheeseburger
  • +
  • Hide
  • +
  • Okay Magician
  • +
  • Centaur
  • +
  • Mother
  • +
  • Dinosaurs
  • +
  • Good With People
  • +
  • Winter
  • +
  • First Memory
  • +
  • Cultures
  • +
  • The Cycle
  • +
  • Box Face
  • +
  • Pets
  • +
  • Hope for the Best
  • +
  • Talking Smack
  • +
  • Hands
  • +
  • So-Deep Space
  • +
  • Past
  • +
  • Deception
  • +
  • Dreams and Fears
  • +
  • The Lamps of Charity
  • +
  • Serious
  • +
  • Standards
  • +
  • Better
  • +
  • Flower
  • +
  • The Best
  • +
  • Looking At the Internet
  • +
  • Computer Fly
  • +
  • Masked Hero
  • +
  • Too Small
  • +
  • Monster
  • +
  • Elements of Home Decor
  • +
  • Early Examples
  • +
  • Bunny Has a Book
  • +
  • Fashion Style
  • +
  • Rustle
  • +
  • A Question of Croissant
  • +
  • The Case of the Package of the Bees
  • +
  • Never Give Up
  • +
  • Other Planets
  • +
  • Momma
  • +
  • Medicine party
  • +
  • Wrong Crowd Small Cat
  • +
  • The Process
  • +
  • Ghost Friend
  • +
  • We Were Trees
  • +
  • Earth and Moon
  • +
  • Howard Carter Mummy Adventure
  • +
  • Fears
  • +
  • Potential
  • +
  • The Horses
  • +
  • Kevin’s Gang
  • +
  • Magic
  • +
  • Ted
  • +
  • Scout-Bot
  • +
  • Penguin
  • +
  • Adventure
  • +
  • Reader
  • +
  • Apple Slices
  • +
  • Big Ideas
  • +
  • Levitate
  • +
  • Juice Box
  • +
  • Wild Koala House
  • +
  • Died
  • +
  • Good With Bad
  • +
  • Garden Snake
  • +
  • Generic Advice Lady
  • +
  • Storage
  • +
  • Bird and Worm
  • +
  • The Data
  • +
  • Cool Small Cat
  • +
  • Scuba Tour
  • +
  • Death-Goose
  • +
  • What You Love
  • +
  • Carol
  • +
  • PDL: Coming Soon to a Book
  • +
  • Buffalo Facts
  • +
  • Just Another
  • +
  • Panda
  • +
  • Good Day
  • +
  • Point
  • +
  • Plum Tree
  • +
  • Friendship Town
  • +
  • Hard Drives
  • +
  • Casual Leon
  • +
  • Go Away
  • +
  • Small Business
  • +
  • Worms
  • +
  • Beginning
  • +
  • The World and Mr. Duck
  • +
  • Lost
  • +
  • Accepted
  • +
  • The City
  • +
  • 15th Century Dad
  • +
  • Snake
  • +
  • Carrots
  • +
  • Wanna Go
  • +
  • Struggles
  • +
  • Mountain
  • +
  • Friends
  • +
  • Water
  • +
  • Be Cool
  • +
  • Beauty
  • +
  • Day We Met
  • +
  • Bad Small Cat
  • +
  • Lies
  • +
  • Fire
  • +
  • Hamster
  • +
  • Suspicion
  • +
  • Not Until
  • +
  • The Last Man
  • +
  • South
  • +
  • Achieve
  • +
  • Reasons (Ernesto’s Bad Day)
  • +
  • Features
  • +
  • Baby
  • +
  • Emotions
  • +
  • New Shorts
  • +
  • Technology
  • +
  • Friend Request
  • +
  • Ernesto
  • +
  • Your Future
  • +
  • Become a Dog
  • +
  • The Void
  • +
  • Golden Rule
  • +
  • Robo-Friend
  • +
  • The Summer of Summer
  • +
  • Bird On a Plane
  • +
  • Sick
  • +
  • Mustache You
  • +
  • Pizza
  • +
  • Cousteau Lives
  • +
  • Sport Sandals
  • +
  • Ladybugs
  • +
  • Man of Snow
  • +
  • Common Rabbit
  • +
  • Sssnake
  • +
  • What Will You Be?
  • +
  • Mom
  • +
  • Handstand
  • +
  • Nostalgic
  • +
  • Bird and Hippo
  • +
  • Arroz
  • +
  • Cozy
  • +
  • 2056
  • +
  • Stereotype
  • +
  • Return of Cousteau
  • +
  • Odd Hat
  • +
  • Spirit Badger
  • +
  • Bear and Penguin
  • +
  • Ghosts
  • +
  • Feelings
  • +
  • Magical Rabbit
  • +
  • Jacques Cousteau
  • +
  • Art Duck
  • +
  • Grow
  • +
  • No Fly
  • +
  • That’s Not Batman
  • +
  • Child
  • +
  • Never
  • +
  • Terrible War
  • +
  • The Tree
  • +
  • Kids
  • +
  • Motivate
  • +
  • Hot Singles
  • +
  • Artifacts
  • +
  • Animal Friends
  • +
  • Creature
  • +
  • So Crazy
  • +
  • Apocalypse 253
  • +
  • Alone
  • +
  • Astronaut Bill IV
  • +
  • A Little Crazy
  • +
  • Gun Baby
  • +
  • The Youth
  • +
  • Trees Whispered
  • +
  • The Aliens
  • +
  • Argument
  • +
  • Don’t
  • +
  • Pigeon Talk
  • +
  • Poorly Drawn Playlist #2
  • +
  • Staring
  • +
  • Nostalgia
  • +
  • Share
  • +
  • Bully
  • +
  • Jean
  • +
  • Lewis
  • +
  • Jerk Dog
  • +
  • Safari Gear
  • +
  • Expression
  • +
  • Modern Grandma
  • +
  • Action Movie Balloon
  • +
  • Horse-bear
  • +
  • Chilla
  • +
  • The Heck
  • +
  • Animal Clothes
  • +
  • Dance Moves
  • +
  • What If
  • +
  • You Win
  • +
  • The Ghost
  • +
  • Competitive Dad
  • +
  • Forgetful
  • +
  • Great Pinks
  • +
  • Awful
  • +
  • Gatsby Party
  • +
  • Dog Love
  • +
  • Ironic
  • +
  • Connected
  • +
  • Cooking
  • +
  • Worm and Fly
  • +
  • Be Real
  • +
  • Shapes
  • +
  • Falcon-Man
  • +
  • Signs of Spring
  • +
  • Rare Beasts
  • +
  • Broccoli
  • +
  • Look Nice
  • +
  • Glasses
  • +
  • Robot
  • +
  • Thing
  • +
  • Evolved
  • +
  • Handy Tip
  • +
  • Jetpack
  • +
  • Small Drawings In Alphabetical Order
  • +
  • 4ever
  • +
  • Breakup
  • +
  • Handsome
  • +
  • Weather
  • +
  • Dating
  • +
  • Help
  • +
  • Wonderland
  • +
  • Imagination
  • +
  • So Cool
  • +
  • Chosen One
  • +
  • Anywhere
  • +
  • Poorly Drawn Poem: To Be a Beast
  • +
  • Fancy
  • +
  • Unremarkable
  • +
  • Comfort Food
  • +
  • Psycho Dan
  • +
  • Life Map
  • +
  • Depressed Snake
  • +
  • Amanda Cline
  • +
  • The Boy With the Flute
  • +
  • Snail
  • +
  • Space Whale
  • +
  • Turtle
  • +
  • Big Idea
  • +
  • Love Tree
  • +
  • Rebel Cat
  • +
  • Ghost
  • +
  • Little Duck
  • +
  • Evil Batman
  • +
  • Cartoonist
  • +
  • Telephone
  • +
  • Moon
  • +
  • New Mustache
  • +
  • Honesty
  • +
  • Comedy Dog
  • +
  • Space Commander
  • +
  • Fish
  • +
  • Bro
  • +
  • Vintage
  • +
  • Tacos
  • +
  • The Friendly Bird
  • +
  • Friendship Bracelets
  • +
  • Bomb Squad
  • +
  • Baby’s Insane
  • +
  • Gary
  • +
  • Cat
  • +
  • Yellowjacket
  • +
  • Babies
  • +
  • The Sword of Legends
  • +
  • Fairy Tales
  • +
  • Monsters
  • +
  • The Lady’s Advice
  • +
  • Beard
  • +
  • The Game
  • +
  • The Ostrich
  • +
  • Chill
  • +
  • Teen Classics
  • +
  • Supertree
  • +
  • Clues
  • +
  • Forever Wild
  • +
  • Eyes of a Child
  • +
  • Sounds of the Past
  • +
  • Loading
  • +
  • Fashionable
  • +
  • Ant
  • +
  • To-Do List
  • +
  • Headlights
  • +
  • Coffee Binge
  • +
  • Food Groups
  • +
  • Beauty and the Actual Beast
  • +
  • Career Distortions
  • +
  • Six Things
  • +
  • Mad Men
  • +
  • Fish and Bird
  • +
  • Poorly Drawn Playlist #1
  • +
  • Baby Clothes
  • +
  • When Suddenly
  • +
  • Smartphone
  • +
  • Roads
  • +
  • Compliments
  • +
  • Camera
  • +
  • Mustache
  • +
  • Talking Animals
  • +
  • Social Fear
  • +
  • Astronaut Bill III
  • +
  • Skinny Jeans
  • +
  • Love Bird
  • +
  • Honest Robot
  • +
  • Know a Man
  • +
  • Portable Privacy
  • +
  • Boombox
  • +
  • Bad Time
  • +
  • Tragedy
  • +
  • Proposed Street Sign
  • +
  • Facebook Alternative
  • +
  • Dubstep
  • +
  • Robots
  • +
  • Burritos
  • +
  • Sign Language Basics
  • +
  • Lists
  • +
  • When It’s Cold
  • +
  • Fantasy
  • +
  • Instructions
  • +
  • Dear Journal
  • +
  • Guns
  • +
  • The Attempt
  • +
  • Best Dad
  • +
  • Sushi
  • +
  • American Apparel
  • +
  • Headphones
  • +
  • Death of a Cartoonist
  • +
  • Nice Ass
  • +
  • Hard to Get
  • +
  • Dinner Schedule
  • +
  • Car
  • +
  • The Bright Side
  • +
  • What’s in the McRib?
  • +
  • Halloween Costume
  • +
  • Happiness
  • +
  • Quick Comics
  • +
  • The Difference
  • +
  • Sinks
  • +
  • Puss in Boots
  • +
  • Cassie the Teenage Cannibal
  • +
  • Bookshelf of the Future
  • +
  • Food
  • +
  • The Four Stages of Procrastination
  • +
  • Eating Naked
  • +
  • Types of Trash Bins
  • +
  • Bragging
  • +
  • Proposed Facebook Buttons
  • +
  • Taco Bell
  • +
  • Anatomy of a Grocery Store Receipt
  • +
  • My Other Car
  • +
  • Dream Feeling
  • +
  • Cash 4 Fold
  • +
  • Post-Traumatic-Cut Syndrome
  • +
  • Lesser-Known Power Rangers
  • +
  • Trouble
  • +
  • The Evolution of a Hangover
  • +
  • Remote
  • +
  • Sexy-saurus
  • +
  • Showdown
  • +
  • What’s Up
  • +
  • Invisible
  • +
  • Nature
  • +
  • They
  • +
  • Frying Pan
  • +
  • Metaphor
  • +
  • Alarm
  • +
  • Fear
  • +
  • Harrowing Adventures In Everyday Life
  • +
  • Origin Story
  • +
  • iPhone 5 Rumors
  • +
  • Laptop Battery
  • +
  • The Sickest Gift
  • +
  • Anatomy of a Latte
  • +
  • Signs You Might Be an Outsider
  • +
  • Trial
  • +
  • Games
  • +
  • Shy Batman
  • +
  • Toilet Paper
  • +
  • Gritty, Realistic
  • +
  • Astronaut Bill II
  • +
  • E-Reader
  • +
  • Holding Doors
  • +
  • Costco
  • +
  • Adulthood
  • +
  • History of Man
  • +
  • Ancient Aliens
  • +
  • Fashion Baby
  • +
  • Parking Problem
  • +
  • Interpersonal Offenses
  • +
  • Love
  • +
  • Astronaut Bill
  • +
  • Life Plan
  • +
  • Fun Activity
  • +
  • Bird Waiter
  • +
  • Talent Show
  • +
  • The Book
  • +
  • Something Different
  • +
  • Adventures In Super Science
  • +
  • The Week
  • +
  • Arrogant Stag
  • +
  • The Craziest Booby Traps
  • +
  • Kathy From the Bar
  • +
  • Inspirational Story
  • +
  • Bad News
  • +
  • Employment
  • +
  • Swim Party
  • +
  • Nine Uses For a Small Dog
  • +
  • Crazy Magic
  • +
  • Untrainable Cat
  • +
  • The Faces of Vegas
  • +
  • Quesadilla
  • +
  • Dreams
  • +
  • Startup Ideas
  • +
  • Part-Time Barista
  • +
  • Jell-O Shots
  • +
  • The Illustrated Guide to Common Greetings
  • +
  • Food Porn
  • +
  • Movie Poster
  • +
  • Weight Loss Solution
  • +
  • Animals
  • +
  • Discussion Topics
  • +
  • The Deepest of Thoughts
  • +
  • How I Killed the Spider
  • +
  • Conversation Advice
  • +
  • Apartment Living
  • +
  • Todd Explains Ambition
  • +
  • Everyday Tips
  • +
  • Fashion
  • +
  • Progression
  • +
  • Innovation
  • +
  • Existential Moments
  • +
  • It Came From the Deep
  • +
  • ScareBot 5000
  • +
  • Limited Connectivity
  • +
  • Hardly Essayists
  • \ No newline at end of file