Browse Source

Merged lainchan with vichan master at 25/9/2016 a4b619ae5e @czaks czaks committed on GitHub 2 days ago

pull/40/head
Benjamin Southall 8 years ago
parent
commit
a3b9c4405c
  1. 6
      .gitmodules
  2. 8
      inc/api.php
  3. 2
      inc/bans.php
  4. 202
      inc/config.php
  5. 108
      inc/controller.php
  6. 58
      inc/display.php
  7. 277
      inc/functions.php
  8. 1
      inc/lib/Twig/Extensions/Extension/Tinyboard.php
  9. 39
      inc/lock.php
  10. 143
      inc/mod/auth.php
  11. 277
      inc/mod/pages.php
  12. 152
      inc/nntpchan/nntpchan.php
  13. 30
      inc/nntpchan/tests.php
  14. 28
      inc/polyfill.php
  15. 49
      inc/queue.php
  16. 65
      inc/route.php
  17. 144
      install.php
  18. 43
      install.sql
  19. 27
      js/auto-scroll.js
  20. 15
      js/catalog-search.js
  21. 35
      js/catalog.js
  22. 383
      js/comment-toolbar.js
  23. 14
      js/expand-all-images.js
  24. 2
      js/expand-too-long.js
  25. 2
      js/favorites.js
  26. 3
      js/file-selector.js
  27. 67
      js/fix-report-delete-submit.js
  28. 72
      js/forced-anon.js
  29. 2
      js/hide-images.js
  30. 2
      js/hide-threads.js
  31. 16
      js/id_colors.js
  32. 1
      js/image-hover.js
  33. 89
      js/infinite-scroll.js
  34. 206
      js/inline-expanding.js
  35. 8
      js/inline.js
  36. 106
      js/jquery.mixitup.min.js
  37. 80
      js/local-time.js
  38. 23
      js/no-animated-gif.js
  39. 72
      js/options/fav.js
  40. 2
      js/options/user-css.js
  41. 2
      js/options/user-js.js
  42. 852
      js/post-filter.js
  43. 5
      js/post-hover.js
  44. 211
      js/post-menu.js
  45. 2
      js/quick-reply.js
  46. 2
      js/show-own-posts.js
  47. 42
      js/thread-watcher.js
  48. 2
      js/toggle-images.js
  49. 24
      log.php
  50. 35
      mod.php
  51. 534
      post.php
  52. 19
      report.php
  53. 162
      smart_build.php
  54. 168
      stylesheets/font-awesome/css/font-awesome.css
  55. 4
      stylesheets/font-awesome/css/font-awesome.min.css
  56. BIN
      stylesheets/font-awesome/fonts/FontAwesome.otf
  57. BIN
      stylesheets/font-awesome/fonts/fontawesome-webfont.eot
  58. 99
      stylesheets/font-awesome/fonts/fontawesome-webfont.svg
  59. BIN
      stylesheets/font-awesome/fonts/fontawesome-webfont.ttf
  60. BIN
      stylesheets/font-awesome/fonts/fontawesome-webfont.woff
  61. BIN
      stylesheets/font-awesome/fonts/fontawesome-webfont.woff2
  62. 3
      stylesheets/font-awesome/less/font-awesome.less
  63. 47
      stylesheets/font-awesome/less/icons.less
  64. 38
      stylesheets/font-awesome/less/mixins.less
  65. 2
      stylesheets/font-awesome/less/path.less
  66. 5
      stylesheets/font-awesome/less/screen-reader.less
  67. 51
      stylesheets/font-awesome/less/variables.less
  68. 47
      stylesheets/font-awesome/scss/_icons.scss
  69. 38
      stylesheets/font-awesome/scss/_mixins.scss
  70. 5
      stylesheets/font-awesome/scss/_screen-reader.scss
  71. 51
      stylesheets/font-awesome/scss/_variables.scss
  72. 3
      stylesheets/font-awesome/scss/font-awesome.scss
  73. 249
      stylesheets/northboard_cb.css
  74. 57
      stylesheets/style.css
  75. 10
      templates/generic_page.html
  76. 21
      templates/index.html
  77. 3
      templates/installer/config.html
  78. 53
      templates/main.js
  79. 2
      templates/mod/ban_form.html
  80. 121
      templates/mod/dashboard.html
  81. 29
      templates/mod/edit_page.html
  82. 16
      templates/mod/log.html
  83. 2
      templates/mod/news.html
  84. 34
      templates/mod/pages.html
  85. 6
      templates/mod/rebuilt.html
  86. 2
      templates/mod/view_ip.html
  87. 14
      templates/page.html
  88. 2
      templates/post/flag.html
  89. 4
      templates/post/name.html
  90. 8
      templates/post/post_controls.html
  91. 4
      templates/post/poster_id.html
  92. 15
      templates/post_thread.html
  93. 1
      templates/posts.sql
  94. 22
      templates/report.html
  95. 21
      templates/report_delete.html
  96. 10
      templates/themes/basic/index.html
  97. 28
      templates/themes/catalog/catalog.html
  98. 19
      templates/themes/catalog/index.rss
  99. 40
      templates/themes/catalog/theme.php
  100. 8
      templates/themes/categories/news.html

6
.gitmodules

@ -1,3 +1,9 @@
[submodule "js/wPaint"]
path = js/wPaint
url = https://github.com/vichan-devel/wPaint.git
branch = master
[submodule "inc/lib/parsedown"]
path = inc/lib/parsedown
url = https://github.com/vichan-devel/parsedown
branch = master

8
inc/api.php

@ -32,6 +32,7 @@ class Api {
'images' => 'images',
'sticky' => 'sticky',
'locked' => 'locked',
'cycle' => 'cyclical',
'bump' => 'last_modified',
'embed' => 'embed',
);
@ -92,7 +93,12 @@ class Api {
$dotPos = strrpos($file->file, '.');
$apiPost['ext'] = substr($file->file, $dotPos);
$apiPost['tim'] = substr($file->file, 0, $dotPos);
$apiPost['md5'] = base64_encode(hex2bin($post->filehash));
if (isset ($file->hash) && $file->hash) {
$apiPost['md5'] = base64_encode(hex2bin($file->hash));
}
else if (isset ($post->filehash) && $post->filehash) {
$apiPost['md5'] = base64_encode(hex2bin($post->filehash));
}
}
private function translatePost($post, $threadsPage = false) {

2
inc/bans.php

@ -174,7 +174,7 @@ class Bans {
if ($ban['post'] && !$hide_message) {
$post = json_decode($ban['post']);
$ban['message'] = $post->body;
$ban['message'] = isset($post->body) ? $post->body : 0;
}
unset($ban['ipstart'], $ban['ipend'], $ban['post'], $ban['creator']);

202
inc/config.php

@ -103,7 +103,7 @@
/*
* ====================
* Cache settings
* Cache, lock and queue settings
* ====================
*/
@ -120,6 +120,7 @@
// $config['cache']['enabled'] = 'apc';
// $config['cache']['enabled'] = 'memcached';
// $config['cache']['enabled'] = 'redis';
// $config['cache']['enabled'] = 'fs';
// Timeout for cached objects such as posts and HTML.
$config['cache']['timeout'] = 60 * 60 * 48; // 48 hours
@ -142,6 +143,12 @@
// (this file will be explicitly loaded during cache hit, but not during cache miss).
$config['cache_config'] = false;
// Define a lock driver.
$config['lock']['enabled'] = 'fs';
// Define a queue driver.
$config['queue']['enabled'] = 'fs'; // xD
/*
* ====================
* Cookie settings
@ -511,6 +518,13 @@
// The timeout for the above, in seconds.
$config['upload_by_url_timeout'] = 15;
// Enable early 404? With default settings, a thread would 404 if it was to leave page 3, if it had less
// than 3 replies.
$config['early_404'] = false;
$config['early_404_page'] = 3;
$config['early_404_replies'] = 5;
// A wordfilter (sometimes referred to as just a "filter" or "censor") automatically scans users’ posts
// as they are submitted and changes or censors particular words or phrases.
@ -550,6 +564,9 @@
// When true, the sage won't be displayed
$config['hide_sage'] = false;
// Don't display user's email when it's not "sage"
$config['hide_email'] = false;
// Attach country flags to posts.
$config['country_flags'] = false;
@ -763,7 +780,7 @@
// Location of thumbnail to use for spoiler images.
$config['spoiler_image'] = 'static/spoiler.png';
// Location of thumbnail to use for deleted images.
// $config['image_deleted'] = 'static/deleted.png';
$config['image_deleted'] = 'static/deleted.png';
// When a thumbnailed image is going to be the same (in dimension), just copy the entire file and use
// that as a thumbnail instead of resizing/redrawing.
@ -804,8 +821,17 @@
// Set this to true if you're using a BSD
$config['bsd_md5'] = false;
// Set this to true if you're having problems with image duplicated error and bsd_md5 doesn't help.
$config['php_md5'] = false;
// Set this to true if you're using Linux and you can execute `md5sum` binary.
$config['gnu_md5'] = false;
// Use Tesseract OCR to retrieve text from images, so you can use it as a spamfilter.
$config['tesseract_ocr'] = false;
// Tesseract parameters
$config['tesseract_params'] = '';
// Tesseract preprocess command
$config['tesseract_preprocess_command'] = 'convert -monochrome %s -';
// Number of posts in a "View Last X Posts" page
$config['noko50_count'] = 50;
@ -928,8 +954,8 @@
// Show page navigation links at the top as well.
$config['page_nav_top'] = false;
// Show "Catalog" link in page navigation. Use with the Catalog theme.
// $config['catalog_link'] = 'catalog.html';
// Show "Catalog" link in page navigation. Use with the Catalog theme. Set to false to disable.
$config['catalog_link'] = 'catalog.html';
// Board categories. Only used in the "Categories" theme.
// $config['categories'] = array(
@ -998,6 +1024,10 @@
// Minify Javascript using http://code.google.com/p/minify/.
$config['minify_js'] = false;
// Dispatch thumbnail loading and image configuration with JavaScript. It will need a certain javascript
// code to work.
$config['javascript_image_dispatch'] = false;
/*
* ====================
* Video embedding
@ -1193,16 +1223,74 @@
// Try not to build pages when we shouldn't have to.
$config['try_smarter'] = true;
// EXPERIMENTAL: Defer static HTML building to a moment, when a given file is actually accessed.
// Warning: This option won't run out of the box. You need to tell your webserver, that a file
// for serving 403 and 404 pages is /smart_build.php. Also, you need to turn off indexes.
/*
* ====================
* Advanced build
* ====================
*/
// Strategies for file generation. Also known as an "advanced build". If you don't have performance
// issues, you can safely ignore that part, because it's hard to configure and won't even work on
// your free webhosting.
//
// A strategy is a function, that given the PHP environment and ($fun, $array) variable pair, returns
// an $action array or false.
//
// $fun - a controller function name, see inc/controller.php. This is named after functions, so that
// we can generate the files in daemon.
//
// $array - arguments to be passed
//
// $action - action to be taken. It's an array, and the first element of it is one of the following:
// * "immediate" - generate the page immediately
// * "defer" - defer page generation to a moment a worker daemon gets to build it (serving a stale
// page in the meantime). The remaining arguments are daemon-specific. Daemon isn't
// implemented yet :DDDD inb4 while(true) { generate(Queue::Get()) }; (which is probably it).
// * "build_on_load" - defer page generation to a moment, when the user actually accesses the page.
// This is a smart_build behaviour. You shouldn't use this one too much, if you
// use it for active boards, the server may choke due to a possible race condition.
// See my blog post: https://engine.vichan.net/blog/res/2.html
//
// So, let's assume we want to build a thread 1324 on board /b/, because a new post appeared there.
// We try the first strategy, giving it arguments: 'sb_thread', array('b', 1324). The strategy will
// now return a value $action, denoting an action to do. If $action is false, we try another strategy.
//
// As I said, configuration isn't easy.
//
// 1. chmod 0777 directories: tmp/locks/ and tmp/queue/.
// 2. serve 403 and 404 requests to go thru smart_build.php
// for nginx, this blog post contains config snippets: https://engine.vichan.net/blog/res/2.html
// 3. disable indexes in your webserver
// 4. launch any number of daemons (eg. twice your number of threads?) using the command:
// $ tools/worker.php
// You don't need to do that step if you are not going to use the "defer" option.
// 5. enable smart_build_helper (see below)
// 6. edit the strategies (see inc/functions.php for the builtin ones). You can use lambdas. I will test
// various ones and include one that works best for me.
$config['generation_strategies'] = array();
// Add a sane strategy. It forces to immediately generate a page user is about to land on. Otherwise,
// it has no opinion, so it needs a fallback strategy.
$config['generation_strategies'][] = 'strategy_sane';
// Add an immediate catch-all strategy. This is the default function of imageboards: generate all pages
// on post time.
$config['generation_strategies'][] = 'strategy_immediate';
// NOT RECOMMENDED: Instead of an all-"immediate" strategy, you can use an all-"build_on_load" one (used
// to be initialized using $config['smart_build']; ) for all pages instead of those to be build
// immediately. A rebuild done in this mode should remove all your static files
// $config['generation_strategies'][1] = 'strategy_smart_build';
// Deprecated. Leave it false. See above.
$config['smart_build'] = false;
// Smart build related: when a file doesn't exist, where should we redirect?
// Use smart_build.php for dispatching missing requests. It may be useful without smart_build or advanced
// build, for example it will regenerate the missing files.
$config['smart_build_helper'] = true;
// smart_build.php: when a file doesn't exist, where should we redirect?
$config['page_404'] = '/404.html';
// Smart build related: extra entrypoints.
$config['smart_build_entrypoints'] = array();
// Extra controller entrypoints. Controller is used only by smart_build and advanced build.
$config['controller_entrypoints'] = array();
/*
* ====================
@ -1235,6 +1323,8 @@
$config['mod']['link_bumpunlock'] = '[-Sage]';
$config['mod']['link_editpost'] = '[Edit]';
$config['mod']['link_move'] = '[Move]';
$config['mod']['link_cycle'] = '[Cycle]';
$config['mod']['link_uncycle'] = '[-Cycle]';
// Moderator capcodes.
$config['capcode'] = ' <span class="capcode">## %s</span>';
@ -1378,6 +1468,9 @@
$config['mod']['deletebyip_global'] = ADMIN;
// Sticky a thread
$config['mod']['sticky'] = MOD;
// Cycle a thread
$config['mod']['cycle'] = MOD;
$config['cycle_limit'] = &$config['reply_limit'];
// Lock a thread
$config['mod']['lock'] = MOD;
// Post in a locked thread
@ -1488,6 +1581,9 @@
$config['mod']['ban_appeals'] = MOD;
// View the recent posts page
$config['mod']['recent'] = MOD;
// Create pages
$config['mod']['edit_pages'] = MOD;
$config['pages_max'] = 10;
// Config editor permissions
$config['mod']['config'] = array();
@ -1534,25 +1630,30 @@
/*
* ====================
* Public post search
* Public pages
* ====================
*/
// Public post search settings
$config['search'] = array();
// Enable the search form
$config['search']['enable'] = false;
// Maximal number of queries per IP address per minutes
$config['search']['queries_per_minutes'] = Array(15, 2);
$config['search']['queries_per_minutes'] = Array(15, 2);
// Global maximal number of queries per minutes
$config['search']['queries_per_minutes_all'] = Array(50, 2);
$config['search']['queries_per_minutes_all'] = Array(50, 2);
// Limit of search results
$config['search']['search_limit'] = 100;
$config['search']['search_limit'] = 100;
// Boards for searching
//$config['search']['boards'] = array('a', 'b', 'c', 'd', 'e');
//$config['search']['boards'] = array('a', 'b', 'c', 'd', 'e');
// Enable public logs? 0: NO, 1: YES, 2: YES, but drop names
$config['public_logs'] = 0;
/*
* ====================
@ -1588,6 +1689,45 @@
// Example: Adding the pre-markup post body to the API as "com_nomarkup".
// $config['api']['extra_fields'] = array('body_nomarkup' => 'com_nomarkup');
/*
* ==================
* NNTPChan settings
* ==================
*/
/*
* Please keep in mind that NNTPChan support in vichan isn't finished yet / is in an experimental
* state. Please join #nntpchan on Rizon in order to peer with someone.
*/
$config['nntpchan'] = array();
// Enable NNTPChan integration
$config['nntpchan']['enabled'] = false;
// NNTP server
$config['nntpchan']['server'] = "localhost:1119";
// Global dispatch array. Add your boards to it to enable them. Please make
// sure that this setting is set in a global context.
$config['nntpchan']['dispatch'] = array(); // 'overchan.test' => 'test'
// Trusted peer - an IP address of your NNTPChan instance. This peer will have
// increased capabilities, eg.: will evade spamfilter.
$config['nntpchan']['trusted_peer'] = '127.0.0.1';
// Salt for message ID generation. Keep it long and secure.
$config['nntpchan']['salt'] = 'change_me+please';
// A local message ID domain. Make sure to change it.
$config['nntpchan']['domain'] = 'example.vichan.net';
// An NNTPChan group name.
// Please set this setting in your board/config.php, not globally.
$config['nntpchan']['group'] = false; // eg. 'overchan.test'
/*
* ====================
* Other/uncategorized
@ -1670,8 +1810,28 @@
'<img style="width:360px;height:270px;" src="//img.youtube.com/vi/$2/0.jpg" class="post-image"/>'.
'</a></div>';
// Slack Report Notification
$config['slack'] = true;
$config['slack_channel'] = "reports";
$config['slack_incoming_webhook_endpoint'] = "https://hooks.slack.com/services/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";
// Slack Report Notification
$config['slack'] = true;
$config['slack_channel'] = "reports";
$config['slack_incoming_webhook_endpoint'] = "https://hooks.slack.com/services/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";
// Password hashing function
//
// $5$ <- SHA256
// $6$ <- SHA512
//
// 25000 rounds make for ~0.05s on my 2015 Core i3 computer.
//
// https://secure.php.net/manual/en/function.crypt.php
$config['password_crypt'] = '$6$rounds=25000$';
// Password hashing method version
// If set to 0, it won't upgrade hashes using old password encryption schema, only create new.
// You can set it to a higher value, to further migrate to other password hashing function.
$config['password_crypt_version'] = 1;
// Use CAPTCHA for reports?
$config['report_captcha'] = false;
// Allowed HTML tags in ?/edit_pages.
$config['allowed_html'] = 'a[href|title],p,br,li,ol,ul,strong,em,u,h2,b,i,tt,div,img[src|alt|title],hr';

108
inc/controller.php

@ -0,0 +1,108 @@
<?php
// This file contains the controller part of vichan
// don't bother with that unless you use smart build or advanced build
// you can use those parts for your own implementations though :^)
defined('TINYBOARD') or exit;
function sb_board($b, $page = 1) { global $config, $build_pages; $page = (int)$page;
if ($page < 1) return false;
if (!openBoard($b)) return false;
if ($page > $config['max_pages']) return false;
$config['try_smarter'] = true;
$build_pages = array($page);
buildIndex("skip");
return true;
}
function sb_api_board($b, $page = 0) { $page = (int)$page;
return sb_board($b, $page + 1);
}
function sb_thread($b, $thread, $slugcheck = false) { global $config; $thread = (int)$thread;
if ($thread < 1) return false;
if (!preg_match('/^'.$config['board_regex'].'$/u', $b)) return false;
if (Cache::get("thread_exists_".$b."_".$thread) == "no") return false;
$query = prepare(sprintf("SELECT MAX(`id`) AS `max` FROM ``posts_%s``", $b));
if (!$query->execute()) return false;
$s = $query->fetch(PDO::FETCH_ASSOC);
$max = $s['max'];
if ($thread > $max) return false;
$query = prepare(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL", $b));
$query->bindValue(':id', $thread);
if (!$query->execute() || !$query->fetch(PDO::FETCH_ASSOC) ) {
Cache::set("thread_exists_".$b."_".$thread, "no", 3600);
return false;
}
if ($slugcheck && $config['slugify']) {
global $request;
$link = link_for(array("id" => $thread), $slugcheck === 50, array("uri" => $b));
$link = "/".$b."/".$config['dir']['res'].$link;
if ($link != $request) {
header("Location: $link", true, 301);
die();
}
}
if ($slugcheck == 50) { // Should we really generate +50 page? Maybe there are not enough posts anyway
global $request;
$r = str_replace("+50", "", $request);
$r = substr($r, 1); // Cut the slash
if (file_exists($r)) return false;
}
if (!openBoard($b)) return false;
buildThread($thread);
return true;
}
function sb_thread_slugcheck($b, $thread) {
return sb_thread($b, $thread, true);
}
function sb_thread_slugcheck50($b, $thread) {
return sb_thread($b, $thread, 50);
}
function sb_api($b) { global $config, $build_pages;
if (!openBoard($b)) return false;
$config['try_smarter'] = true;
$build_pages = array(-1);
buildIndex();
return true;
}
function sb_ukko() {
rebuildTheme("ukko", "post-thread");
return true;
}
function sb_catalog($b) {
if (!openBoard($b)) return false;
rebuildTheme("catalog", "post-thread", $b);
return true;
}
function sb_recent() {
rebuildTheme("recent", "post-thread");
return true;
}
function sb_sitemap() {
rebuildTheme("sitemap", "all");
return true;
}

58
inc/display.php

@ -71,6 +71,64 @@ function createBoardlist($mod=false) {
);
}
function error($message, $priority = true, $debug_stuff = false) {
global $board, $mod, $config, $db_error;
if ($config['syslog'] && $priority !== false) {
// Use LOG_NOTICE instead of LOG_ERR or LOG_WARNING because most error message are not significant.
_syslog($priority !== true ? $priority : LOG_NOTICE, $message);
}
if (defined('STDIN')) {
// Running from CLI
echo('Error: ' . $message . "\n");
debug_print_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS);
die();
}
if ($config['debug'] && isset($db_error)) {
$debug_stuff = array_combine(array('SQLSTATE', 'Error code', 'Error message'), $db_error);
}
if ($config['debug']) {
$debug_stuff['backtrace'] = debug_backtrace();
}
if (isset($_POST['json_response'])) {
header('Content-Type: text/json; charset=utf-8');
die(json_encode(array(
'error' => $message
)));
}
else {
header($_SERVER['SERVER_PROTOCOL'] . ' 400 Bad Request');
}
$pw = $config['db']['password'];
$debug_callback = function(&$item) use (&$debug_callback, $pw) {
if (is_array($item)) {
$item = array_filter($item, $debug_callback);
}
return ($item !== $pw || !$pw);
};
if ($debug_stuff)
$debug_stuff = array_filter($debug_stuff, $debug_callback);
die(Element('page.html', array(
'config' => $config,
'title' => _('Error'),
'subtitle' => _('An error has occured.'),
'body' => Element('error.html', array(
'config' => $config,
'message' => $message,
'mod' => $mod,
'board' => isset($board) ? $board : false,
'debug' => is_array($debug_stuff) ? str_replace("\n", '&#10;', utf8tohtml(print_r($debug_stuff, true))) : utf8tohtml($debug_stuff)
))
)));
}
function loginForm($error=false, $username=false, $redirect=false) {
global $config;

277
inc/functions.php

@ -19,6 +19,12 @@ require_once 'inc/template.php';
require_once 'inc/database.php';
require_once 'inc/events.php';
require_once 'inc/api.php';
require_once 'inc/mod/auth.php';
require_once 'inc/lock.php';
require_once 'inc/queue.php';
require_once 'inc/polyfill.php';
@include_once 'inc/lib/parsedown/Parsedown.php'; // fail silently, this isn't a critical piece of code
if (!extension_loaded('gettext')) {
require_once 'inc/lib/gettext/gettext.inc';
}
@ -86,6 +92,8 @@ function loadConfig() {
'db',
'api',
'cache',
'lock',
'queue',
'cookies',
'error',
'dir',
@ -123,7 +131,7 @@ function loadConfig() {
// So, we may store the locale in a tmp/ filesystem.
if (file_exists($fn = 'tmp/cache/locale_' . $boardsuffix ) ) {
$config['locale'] = file_get_contents($fn);
$config['locale'] = @file_get_contents($fn);
}
else {
$config['locale'] = 'en';
@ -134,13 +142,13 @@ function loadConfig() {
$configstr .= file_get_contents($board['dir'] . '/config.php');
}
$matches = array();
preg_match_all('/[^\/*#]\$config\s*\[\s*[\'"]locale[\'"]\s*\]\s*=\s*([\'"])(.*?)\1/', $configstr, $matches);
preg_match_all('/[^\/#*]\$config\s*\[\s*[\'"]locale[\'"]\s*\]\s*=\s*([\'"])(.*?)\1/', $configstr, $matches);
if ($matches && isset ($matches[2]) && $matches[2]) {
$matches = $matches[2];
$config['locale'] = $matches[count($matches)-1];
}
file_put_contents($fn, $config['locale']);
@file_put_contents($fn, $config['locale']);
}
if ($config['locale'] != $current_locale) {
@ -480,7 +488,8 @@ function setupBoard($array) {
$board = array(
'uri' => $array['uri'],
'title' => $array['title'],
'subtitle' => $array['subtitle']
'subtitle' => $array['subtitle'],
#'indexed' => $array['indexed'],
);
// older versions
@ -505,14 +514,19 @@ function setupBoard($array) {
}
function openBoard($uri) {
global $config, $build_pages;
global $config, $build_pages, $board;
if ($config['try_smarter'])
$build_pages = array();
$board = getBoardInfo($uri);
if ($board) {
setupBoard($board);
// And what if we don't really need to change a board we have opened?
if (isset ($board) && isset ($board['uri']) && $board['uri'] == $uri) {
return true;
}
$b = getBoardInfo($uri);
if ($b) {
setupBoard($b);
if (function_exists('after_open_board')) {
after_open_board();
@ -836,7 +850,7 @@ function displayBan($ban) {
Element('page.html', array(
'title' => _('Banned!'),
'config' => $config,
'nojavascript' => true,
'boardlist' => createBoardlist(isset($mod) ? $mod : false),
'body' => Element('banned.html', array(
'config' => $config,
'ban' => $ban,
@ -974,7 +988,7 @@ function insertFloodPost(array $post) {
function post(array $post) {
global $pdo, $board;
$query = prepare(sprintf("INSERT INTO ``posts_%s`` VALUES ( NULL, :thread, :subject, :email, :name, :trip, :capcode, :body, :body_nomarkup, :time, :time, :files, :num_files, :filehash, :password, :ip, :sticky, :locked, 0, :embed, :slug)", $board['uri']));
$query = prepare(sprintf("INSERT INTO ``posts_%s`` VALUES ( NULL, :thread, :subject, :email, :name, :trip, :capcode, :body, :body_nomarkup, :time, :time, :files, :num_files, :filehash, :password, :ip, :sticky, :locked, :cycle, 0, :embed, :slug)", $board['uri']));
// Basic stuff
if (!empty($post['subject'])) {
@ -1014,6 +1028,12 @@ function post(array $post) {
$query->bindValue(':locked', false, PDO::PARAM_INT);
}
if ($post['op'] && $post['mod'] && isset($post['cycle']) && $post['cycle']) {
$query->bindValue(':cycle', true, PDO::PARAM_INT);
} else {
$query->bindValue(':cycle', false, PDO::PARAM_INT);
}
if ($post['mod'] && isset($post['capcode']) && $post['capcode']) {
$query->bindValue(':capcode', $post['capcode'], PDO::PARAM_INT);
} else {
@ -1086,6 +1106,8 @@ function deleteFile($id, $remove_entirely_if_already=true, $file=null) {
$files = json_decode($post['files']);
$file_to_delete = $file !== false ? $files[(int)$file] : (object)array('file' => false);
if (!$files[0]) error(_('That post has no files.'));
if ($files[0]->file == 'deleted' && $post['num_files'] == 1 && !$post['thread'])
return; // Can't delete OP's image completely.
@ -1097,8 +1119,10 @@ function deleteFile($id, $remove_entirely_if_already=true, $file=null) {
foreach ($files as $i => $f) {
if (($file !== false && $i == $file) || $file === null) {
// Delete thumbnail
file_unlink($board['dir'] . $config['dir']['thumb'] . $f->thumb);
unset($files[$i]->thumb);
if (isset ($f->thumb) && $f->thumb) {
file_unlink($board['dir'] . $config['dir']['thumb'] . $f->thumb);
unset($files[$i]->thumb);
}
// Delete file
file_unlink($board['dir'] . $config['dir']['img'] . $f->file);
@ -1120,19 +1144,22 @@ function deleteFile($id, $remove_entirely_if_already=true, $file=null) {
// rebuild post (markup)
function rebuildPost($id) {
global $board;
global $board, $mod;
$query = prepare(sprintf("SELECT `body_nomarkup`, `thread` FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
$query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
$query->bindValue(':id', $id, PDO::PARAM_INT);
$query->execute() or error(db_error($query));
if ((!$post = $query->fetch(PDO::FETCH_ASSOC)) || !$post['body_nomarkup'])
return false;
markup($body = &$post['body_nomarkup']);
markup($post['body'] = &$post['body_nomarkup']);
$post = (object)$post;
event('rebuildpost', $post);
$post = (array)$post;
$query = prepare(sprintf("UPDATE ``posts_%s`` SET `body` = :body WHERE `id` = :id", $board['uri']));
$query->bindValue(':body', $body);
$query->bindValue(':body', $post['body']);
$query->bindValue(':id', $id, PDO::PARAM_INT);
$query->execute() or error(db_error($query));
@ -1221,7 +1248,7 @@ function deletePost($id, $error_if_doesnt_exist=true, $rebuild_after=true) {
return true;
}
function clean() {
function clean($pid = false) {
global $board, $config;
$offset = round($config['max_pages']*$config['threads_per_page']);
@ -1232,6 +1259,22 @@ function clean() {
$query->execute() or error(db_error($query));
while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
deletePost($post['id'], false, false);
if ($pid) modLog("Automatically deleting thread #{$post['id']} due to new thread #{$pid}");
}
// Bump off threads with X replies earlier, spam prevention method
if ($config['early_404']) {
$offset = round($config['early_404_page']*$config['threads_per_page']);
$query = prepare(sprintf("SELECT `id` AS `thread_id`, (SELECT COUNT(`id`) FROM ``posts_%s`` WHERE `thread` = `thread_id`) AS `reply_count` FROM ``posts_%s`` WHERE `thread` IS NULL ORDER BY `sticky` DESC, `bump` DESC LIMIT :offset, 9001", $board['uri'], $board['uri']));
$query->bindValue(':offset', $offset, PDO::PARAM_INT);
$query->execute() or error(db_error($query));
while ($post = $query->fetch(PDO::FETCH_ASSOC)) {
if ($post['reply_count'] < $config['early_404_replies']) {
deletePost($post['thread_id'], false, false);
if ($pid) modLog("Automatically deleting thread #{$post['thread_id']} due to new thread #{$pid} (early 404 is set, #{$post['thread_id']} had {$post['reply_count']} replies)");
}
}
}
}
@ -1245,7 +1288,8 @@ function thread_find_page($thread) {
return floor(($config['threads_per_page'] + $index) / $config['threads_per_page']);
}
function index($page, $mod=false) {
// $brief means that we won't need to generate anything yet
function index($page, $mod=false, $brief = false) {
global $board, $config, $debug;
$body = '';
@ -1276,6 +1320,7 @@ function index($page, $mod=false) {
unset($cached);
}
}
if (!isset($cached)) {
$posts = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE `thread` = :id ORDER BY `id` DESC LIMIT :limit", $board['uri']));
$posts->bindValue(':id', $th['id']);
@ -1315,7 +1360,10 @@ function index($page, $mod=false) {
}
$threads[] = $thread;
$body .= $thread->build(true);
if (!$brief) {
$body .= $thread->build(true);
}
}
if ($config['file_board']) {
@ -1536,27 +1584,28 @@ function checkMute() {
function buildIndex($global_api = "yes") {
global $board, $config, $build_pages;
if (!$config['smart_build']) {
$pages = getPages();
if (!$config['try_smarter'])
$antibot = create_antibot($board['uri']);
$catalog_api_action = generation_strategy('sb_api', array($board['uri']));
if ($config['api']['enabled']) {
$api = new Api();
$catalog = array();
}
$pages = null;
$antibot = null;
if ($config['api']['enabled']) {
$api = new Api();
$catalog = array();
}
for ($page = 1; $page <= $config['max_pages']; $page++) {
$filename = $board['dir'] . ($page == 1 ? $config['file_index'] : sprintf($config['file_page'], $page));
$jsonFilename = $board['dir'] . ($page - 1) . '.json'; // pages should start from 0
if ((!$config['api']['enabled'] || $global_api == "skip" || $config['smart_build']) && $config['try_smarter']
&& isset($build_pages) && !empty($build_pages) && !in_array($page, $build_pages) )
$wont_build_this_page = $config['try_smarter'] && isset($build_pages) && !empty($build_pages) && !in_array($page, $build_pages);
if ((!$config['api']['enabled'] || $global_api == "skip") && $wont_build_this_page)
continue;
if (!$config['smart_build']) {
$content = index($page);
$action = generation_strategy('sb_board', array($board['uri'], $page));
if ($action == 'rebuild' || $catalog_api_action == 'rebuild') {
$content = index($page, false, $wont_build_this_page);
if (!$content)
break;
@ -1567,17 +1616,21 @@ function buildIndex($global_api = "yes") {
file_write($jsonFilename, $json);
$catalog[$page-1] = $threads;
}
if ($config['api']['enabled'] && $global_api != "skip" && $config['try_smarter'] && isset($build_pages)
&& !empty($build_pages) && !in_array($page, $build_pages) )
continue;
if ($wont_build_this_page) continue;
}
if ($config['try_smarter']) {
$antibot = create_antibot($board['uri'], 0 - $page);
$content['current_page'] = $page;
}
elseif (!$antibot) {
$antibot = create_antibot($board['uri']);
}
$antibot->reset();
if (!$pages) {
$pages = getPages();
}
$content['pages'] = $pages;
$content['pages'][$page-1]['selected'] = true;
$content['btn'] = getPageButtons($content['pages']);
@ -1585,13 +1638,14 @@ function buildIndex($global_api = "yes") {
file_write($filename, Element('index.html', $content));
}
else {
elseif ($action == 'delete' || $catalog_api_action == 'delete') {
file_unlink($filename);
file_unlink($jsonFilename);
}
}
if (!$config['smart_build'] && $page < $config['max_pages']) {
// $action is an action for our last page
if (($catalog_api_action == 'rebuild' || $action == 'rebuild' || $action == 'delete') && $page < $config['max_pages']) {
for (;$page<=$config['max_pages'];$page++) {
$filename = $board['dir'] . ($page==1 ? $config['file_index'] : sprintf($config['file_page'], $page));
file_unlink($filename);
@ -1605,13 +1659,13 @@ function buildIndex($global_api = "yes") {
// json api catalog
if ($config['api']['enabled'] && $global_api != "skip") {
if ($config['smart_build']) {
if ($catalog_api_action == 'delete') {
$jsonFilename = $board['dir'] . 'catalog.json';
file_unlink($jsonFilename);
$jsonFilename = $board['dir'] . 'threads.json';
file_unlink($jsonFilename);
}
else {
elseif ($catalog_api_action == 'rebuild') {
$json = json_encode($api->translateCatalog($catalog));
$jsonFilename = $board['dir'] . 'catalog.json';
file_write($jsonFilename, $json);
@ -1672,13 +1726,15 @@ function buildJavascript() {
function checkDNSBL() {
global $config;
if (isIPv6())
return; // No IPv6 support yet.
if (!isset($_SERVER['REMOTE_ADDR']))
return; // Fix your web server configuration
if (preg_match("/^(::(ffff:)?)?(127\.|192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.|0\.|255\.)/", $_SERVER['REMOTE_ADDR']))
return; // It's pointless to check for local IP addresses in dnsbls, isn't it?
if (in_array($_SERVER['REMOTE_ADDR'], $config['dnsbl_exceptions']))
return;
@ -1811,7 +1867,11 @@ function extract_modifiers($body) {
return $modifiers;
}
function markup(&$body, $track_cites = false) {
function remove_modifiers($body) {
return preg_replace('@<tinyboard ([\w\s]+)>(.+?)</tinyboard>@usm', '', $body);
}
function markup(&$body, $track_cites = false, $op = false) {
global $board, $config, $markup_urls;
$modifiers = extract_modifiers($body);
@ -1907,7 +1967,7 @@ function markup(&$body, $track_cites = false) {
}
if (isset($cited_posts[$cite])) {
$replacement = '<a onclick="highlightReply(\''.$cite.'\');" href="' .
$replacement = '<a onclick="highlightReply(\''.$cite.'\', event);" href="' .
$config['root'] . $board['dir'] . $config['dir']['res'] .
link_for(array('id' => $cite, 'thread' => $cited_posts[$cite])) . '#' . $cite . '">' .
'&gt;&gt;' . $cite .
@ -2006,7 +2066,7 @@ function markup(&$body, $track_cites = false) {
$replacement = '<a ' .
($_board == $board['uri'] ?
'onclick="highlightReply(\''.$cite.'\');" '
'onclick="highlightReply(\''.$cite.'\', event);" '
: '') . 'href="' . $link . '">' .
'&gt;&gt;&gt;/' . $_board . '/' . $cite .
'</a>';
@ -2112,16 +2172,7 @@ function strip_combining_chars($str) {
$o = 0;
$ord = ordutf8($char, $o);
if ($ord >= 768 && $ord <= 879)
continue;
if ($ord >= 7616 && $ord <= 7679)
continue;
if ($ord >= 8400 && $ord <= 8447)
continue;
if ($ord >= 65056 && $ord <= 65071)
if ( ($ord >= 768 && $ord <= 879) || ($ord >= 1536 && $ord <= 1791) || ($ord >= 3655 && $ord <= 3659) || ($ord >= 7616 && $ord <= 7679) || ($ord >= 8400 && $ord <= 8447) || ($ord >= 65056 && $ord <= 65071))
continue;
$str .= $char;
@ -2145,7 +2196,9 @@ function buildThread($id, $return = false, $mod = false) {
if ($config['try_smarter'] && !$mod)
$build_pages[] = thread_find_page($id);
if (!$config['smart_build'] || $return || $mod) {
$action = generation_strategy('sb_thread', array($board['uri'], $id));
if ($action == 'rebuild' || $return || $mod) {
$query = prepare(sprintf("SELECT * FROM ``posts_%s`` WHERE (`thread` IS NULL AND `id` = :id) OR `thread` = :id ORDER BY `thread`,`id`", $board['uri']));
$query->bindValue(':id', $id, PDO::PARAM_INT);
$query->execute() or error(db_error($query));
@ -2180,26 +2233,26 @@ function buildThread($id, $return = false, $mod = false) {
));
// json api
if ($config['api']['enabled']) {
if ($config['api']['enabled'] && !$mod) {
$api = new Api();
$json = json_encode($api->translateThread($thread));
$jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json';
file_write($jsonFilename, $json);
}
}
else {
elseif($action == 'delete') {
$jsonFilename = $board['dir'] . $config['dir']['res'] . $id . '.json';
file_unlink($jsonFilename);
}
if ($config['smart_build'] && !$return && !$mod) {
if ($action == 'delete' && !$return && !$mod) {
$noko50fn = $board['dir'] . $config['dir']['res'] . link_for(array('id' => $id), true);
file_unlink($noko50fn);
file_unlink($board['dir'] . $config['dir']['res'] . link_for(array('id' => $id)));
} else if ($return) {
} elseif ($return) {
return $body;
} else {
} elseif ($action == 'rebuild') {
$noko50fn = $board['dir'] . $config['dir']['res'] . link_for($thread, true);
if ($hasnoko50 || file_exists($noko50fn)) {
buildThread50($id, $return, $mod, $thread, $antibot);
@ -2340,7 +2393,7 @@ function generate_tripcode($name) {
if (isset($config['custom_tripcode']["##{$trip}"]))
$trip = $config['custom_tripcode']["##{$trip}"];
else
$trip = '!!' . substr(crypt($trip, '_..A.' . substr(base64_encode(sha1($trip . $config['secure_trip_salt'], true)), 0, 4)), -10);
$trip = '!!' . substr(crypt($trip, str_replace('+', '.', '_..A.' . substr(base64_encode(sha1($trip . $config['secure_trip_salt'], true)), 0, 4))), -10);
} else {
if (isset($config['custom_tripcode']["#{$trip}"]))
$trip = $config['custom_tripcode']["#{$trip}"];
@ -2429,7 +2482,7 @@ function rDNS($ip_addr) {
if (!$config['dns_system']) {
$host = gethostbyaddr($ip_addr);
} else {
$resp = shell_exec_error('host -W 1 ' . $ip_addr);
$resp = shell_exec_error('host -W 3 ' . $ip_addr);
if (preg_match('/domain name pointer ([^\s]+)$/', $resp, $m))
$host = $m[1];
else
@ -2565,7 +2618,7 @@ function slugify($post) {
elseif (isset ($post['body_nomarkup']) && $post['body_nomarkup'])
$slug = $post['body_nomarkup'];
elseif (isset ($post['body']) && $post['body'])
$slug = strip_html($post['body']);
$slug = strip_tags($post['body']);
// Fix UTF-8 first
$slug = mb_convert_encoding($slug, "UTF-8", "UTF-8");
@ -2640,3 +2693,103 @@ function link_for($post, $page50 = false, $foreignlink = false, $thread = false)
return sprintf($tpl, $id, $slug);
}
function prettify_textarea($s){
return str_replace("\t", '&#09;', str_replace("\n", '&#13;&#10;', htmlentities($s)));
}
/*class HTMLPurifier_URIFilter_NoExternalImages extends HTMLPurifier_URIFilter {
public $name = 'NoExternalImages';
public function filter(&$uri, $c, $context) {
global $config;
$ct = $context->get('CurrentToken');
if (!$ct || $ct->name !== 'img') return true;
if (!isset($uri->host) && !isset($uri->scheme)) return true;
if (!in_array($uri->scheme . '://' . $uri->host . '/', $config['allowed_offsite_urls'])) {
error('No off-site links in board announcement images.');
}
return true;
}
}*/
function purify_html($s) {
global $config;
$c = HTMLPurifier_Config::createDefault();
$c->set('HTML.Allowed', $config['allowed_html']);
$uri = $c->getDefinition('URI');
$uri->addFilter(new HTMLPurifier_URIFilter_NoExternalImages(), $c);
$purifier = new HTMLPurifier($c);
$clean_html = $purifier->purify($s);
return $clean_html;
}
function markdown($s) {
$pd = new Parsedown();
$pd->setMarkupEscaped(true);
$pd->setimagesEnabled(false);
return $pd->text($s);
}
function generation_strategy($fun, $array=array()) { global $config;
$action = false;
foreach ($config['generation_strategies'] as $s) {
if ($action = $s($fun, $array)) {
break;
}
}
switch ($action[0]) {
case 'immediate':
return 'rebuild';
case 'defer':
// Ok, it gets interesting here :)
get_queue('generate')->push(serialize(array('build', $fun, $array, $action)));
return 'ignore';
case 'build_on_load':
return 'delete';
}
}
function strategy_immediate($fun, $array) {
return array('immediate');
}
function strategy_smart_build($fun, $array) {
return array('build_on_load');
}
function strategy_sane($fun, $array) { global $config;
if (php_sapi_name() == 'cli') return false;
else if (isset($_POST['mod'])) return false;
// Thread needs to be done instantly. Same with a board page, but only if posting a new thread.
else if ($fun == 'sb_thread' || ($fun == 'sb_board' && $array[1] == 1 && isset ($_POST['page']))) return array('immediate');
else return false;
}
// My first, test strategy.
function strategy_first($fun, $array) {
switch ($fun) {
case 'sb_thread':
return array('defer');
case 'sb_board':
if ($array[1] > 8) return array('build_on_load');
else return array('defer');
case 'sb_api':
return array('defer');
case 'sb_catalog':
return array('defer');
case 'sb_recent':
return array('build_on_load');
case 'sb_sitemap':
return array('build_on_load');
case 'sb_ukko':
return array('defer');
}
}

1
inc/lib/Twig/Extensions/Extension/Tinyboard.php

@ -17,6 +17,7 @@ class Twig_Extensions_Extension_Tinyboard extends Twig_Extension
new Twig_SimpleFilter('extension', 'twig_extension_filter'),
new Twig_SimpleFilter('sprintf', 'sprintf'),
new Twig_SimpleFilter('capcode', 'capcode'),
new Twig_SimpleFilter('remove_modifiers', 'remove_modifiers'),
new Twig_SimpleFilter('hasPermission', 'twig_hasPermission_filter'),
new Twig_SimpleFilter('date', 'twig_date_filter'),
new Twig_SimpleFilter('poster_id', 'poster_id'),

39
inc/lock.php

@ -0,0 +1,39 @@
<?php
class Lock {
function __construct($key) { global $config;
if ($config['lock']['enabled'] == 'fs') {
$key = str_replace('/', '::', $key);
$key = str_replace("\0", '', $key);
$this->f = fopen("tmp/locks/$key", "w");
}
}
// Get a shared lock
function get($nonblock = false) { global $config;
if ($config['lock']['enabled'] == 'fs') {
$wouldblock = false;
flock($this->f, LOCK_SH | ($nonblock ? LOCK_NB : 0), $wouldblock);
if ($nonblock && $wouldblock) return false;
}
return $this;
}
// Get an exclusive lock
function get_ex($nonblock = false) { global $config;
if ($config['lock']['enabled'] == 'fs') {
$wouldblock = false;
flock($this->f, LOCK_EX | ($nonblock ? LOCK_NB : 0), $wouldblock);
if ($nonblock && $wouldblock) return false;
}
return $this;
}
// Free a lock
function free() { global $config;
if ($config['lock']['enabled'] == 'fs') {
flock($this->f, LOCK_UN);
}
return $this;
}
}

143
inc/mod/auth.php

@ -18,7 +18,18 @@ function mkhash($username, $password, $salt = false) {
}
// generate hash (method is not important as long as it's strong)
$hash = substr(base64_encode(md5($username . $config['cookies']['salt'] . sha1($username . $password . $salt . ($config['mod']['lock_ip'] ? $_SERVER['REMOTE_ADDR'] : ''), true), true)), 0, 20);
$hash = substr(
base64_encode(
md5(
$username . $config['cookies']['salt'] . sha1(
$username . $password . $salt . (
$config['mod']['lock_ip'] ? $_SERVER['REMOTE_ADDR'] : ''
), true
) . sha1($config['password_crypt_version']) // Log out users being logged in with older password encryption schema
, true
)
), 0, 20
);
if (isset($generated_salt))
return array($hash, $salt);
@ -26,25 +37,63 @@ function mkhash($username, $password, $salt = false) {
return $hash;
}
function generate_salt() {
mt_srand(microtime(true) * 100000 + memory_get_usage(true));
return md5(uniqid(mt_rand(), true));
function crypt_password_old($password) {
$salt = generate_salt();
$password = hash('sha256', $salt . sha1($password));
return array($salt, $password);
}
function login($username, $password, $makehash=true) {
global $mod;
// SHA1 password
if ($makehash) {
$password = sha1($password);
function crypt_password($password) {
global $config;
// `salt` database field is reused as a version value. We don't want it to be 0.
$version = $config['password_crypt_version'] ? $config['password_crypt_version'] : 1;
$new_salt = generate_salt();
$password = crypt($password, $config['password_crypt'] . $new_salt . "$");
return array($version, $password);
}
function test_password($password, $salt, $test) {
global $config;
// Version = 0 denotes an old password hashing schema. In the same column, the
// password hash was kept previously
$version = (strlen($salt) <= 8) ? (int) $salt : 0;
if ($version == 0) {
$comp = hash('sha256', $salt . sha1($test));
}
else {
$comp = crypt($test, $password);
}
return array($version, hash_equals($password, $comp));
}
function generate_salt() {
// 128 bits of entropy
return strtr(base64_encode(mcrypt_create_iv(16, MCRYPT_DEV_URANDOM)), '+', '.');
}
function login($username, $password) {
global $mod, $config;
$query = prepare("SELECT `id`, `type`, `boards`, `password`, `salt` FROM ``mods`` WHERE `username` = :username");
$query = prepare("SELECT `id`, `type`, `boards`, `password`, `version` FROM ``mods`` WHERE BINARY `username` = :username");
$query->bindValue(':username', $username);
$query->execute() or error(db_error($query));
if ($user = $query->fetch(PDO::FETCH_ASSOC)) {
if ($user['password'] === hash('sha256', $user['salt'] . $password)) {
list($version, $ok) = test_password($user['password'], $user['version'], $password);
if ($ok) {
if ($config['password_crypt_version'] > $version) {
// It's time to upgrade the password hashing method!
list ($user['version'], $user['password']) = crypt_password($password);
$query = prepare("UPDATE ``mods`` SET `password` = :password, `version` = :version WHERE `id` = :id");
$query->bindValue(':password', $user['password']);
$query->bindValue(':version', $user['version']);
$query->bindValue(':id', $user['id']);
$query->execute() or error(db_error($query));
}
return $mod = array(
'id' => $user['id'],
'type' => $user['type'],
@ -81,7 +130,7 @@ function destroyCookies() {
function modLog($action, $_board=null) {
global $mod, $board, $config;
$query = prepare("INSERT INTO ``modlogs`` VALUES (:id, :ip, :board, :time, :text)");
$query->bindValue(':id', $mod['id'], PDO::PARAM_INT);
$query->bindValue(':id', (isset($mod['id']) ? $mod['id'] : -1), PDO::PARAM_INT);
$query->bindValue(':ip', $_SERVER['REMOTE_ADDR']);
$query->bindValue(':time', time(), PDO::PARAM_INT);
$query->bindValue(':text', $action);
@ -97,39 +146,6 @@ function modLog($action, $_board=null) {
_syslog(LOG_INFO, '[mod/' . $mod['username'] . ']: ' . $action);
}
// Validate session
if (isset($_COOKIE[$config['cookies']['mod']])) {
// Should be username:hash:salt
$cookie = explode(':', $_COOKIE[$config['cookies']['mod']]);
if (count($cookie) != 3) {
// Malformed cookies
destroyCookies();
mod_login();
exit;
}
$query = prepare("SELECT `id`, `type`, `boards`, `password` FROM ``mods`` WHERE `username` = :username");
$query->bindValue(':username', $cookie[0]);
$query->execute() or error(db_error($query));
$user = $query->fetch(PDO::FETCH_ASSOC);
// validate password hash
if ($cookie[1] !== mkhash($cookie[0], $user['password'], $cookie[2])) {
// Malformed cookies
destroyCookies();
mod_login();
exit;
}
$mod = array(
'id' => $user['id'],
'type' => $user['type'],
'username' => $cookie[0],
'boards' => explode(',', $user['boards'])
);
}
function create_pm_header() {
global $mod, $config;
@ -163,4 +179,37 @@ function make_secure_link_token($uri) {
return substr(sha1($config['cookies']['salt'] . '-' . $uri . '-' . $mod['id']), 0, 8);
}
function check_login($prompt = false) {
global $config, $mod;
// Validate session
if (isset($_COOKIE[$config['cookies']['mod']])) {
// Should be username:hash:salt
$cookie = explode(':', $_COOKIE[$config['cookies']['mod']]);
if (count($cookie) != 3) {
// Malformed cookies
destroyCookies();
if ($prompt) mod_login();
exit;
}
$query = prepare("SELECT `id`, `type`, `boards`, `password` FROM ``mods`` WHERE `username` = :username");
$query->bindValue(':username', $cookie[0]);
$query->execute() or error(db_error($query));
$user = $query->fetch(PDO::FETCH_ASSOC);
// validate password hash
if ($cookie[1] !== mkhash($cookie[0], $user['password'], $cookie[2])) {
// Malformed cookies
destroyCookies();
if ($prompt) mod_login();
exit;
}
$mod = array(
'id' => $user['id'],
'type' => $user['type'],
'username' => $cookie[0],
'boards' => explode(',', $user['boards'])
);
}
}

277
inc/mod/pages.php

@ -15,7 +15,7 @@ function mod_page($title, $template, $args, $subtitle = false) {
'hide_dashboard_link' => $template == 'mod/dashboard.html',
'title' => $title,
'subtitle' => $subtitle,
'nojavascript' => true,
'boardlist' => createBoardlist($mod),
'body' => Element($template,
array_merge(
array('config' => $config, 'mod' => $mod),
@ -612,7 +612,7 @@ function mod_news($page_no = 1) {
rebuildThemes('news');
header('Location: ?/news#' . $pdo->lastInsertId(), true, $config['redirect_http']);
header('Location: ?/edit_news#' . $pdo->lastInsertId(), true, $config['redirect_http']);
}
$query = prepare("SELECT * FROM ``news`` ORDER BY `id` DESC LIMIT :offset, :limit");
@ -625,14 +625,14 @@ function mod_news($page_no = 1) {
error($config['error']['404']);
foreach ($news as &$entry) {
$entry['delete_token'] = make_secure_link_token('news/delete/' . $entry['id']);
$entry['delete_token'] = make_secure_link_token('edit_news/delete/' . $entry['id']);
}
$query = prepare("SELECT COUNT(*) FROM ``news``");
$query->execute() or error(db_error($query));
$count = $query->fetchColumn();
mod_page(_('News'), 'mod/news.html', array('news' => $news, 'count' => $count, 'token' => make_secure_link_token('news')));
mod_page(_('News'), 'mod/news.html', array('news' => $news, 'count' => $count, 'token' => make_secure_link_token('edit_news')));
}
function mod_news_delete($id) {
@ -647,7 +647,7 @@ function mod_news_delete($id) {
modLog('Deleted a news entry');
header('Location: ?/news', true, $config['redirect_http']);
header('Location: ?/edit_news', true, $config['redirect_http']);
}
function mod_log($page_no = 1) {
@ -702,6 +702,42 @@ function mod_user_log($username, $page_no = 1) {
mod_page(_('Moderation log'), 'mod/log.html', array('logs' => $logs, 'count' => $count, 'username' => $username));
}
function mod_board_log($board, $page_no = 1, $hide_names = false, $public = false) {
global $config;
if ($page_no < 1)
error($config['error']['404']);
if (!hasPermission($config['mod']['mod_board_log'], $board) && !$public)
error($config['error']['noaccess']);
$query = prepare("SELECT `username`, `mod`, `ip`, `board`, `time`, `text` FROM ``modlogs`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `board` = :board ORDER BY `time` DESC LIMIT :offset, :limit");
$query->bindValue(':board', $board);
$query->bindValue(':limit', $config['mod']['modlog_page'], PDO::PARAM_INT);
$query->bindValue(':offset', ($page_no - 1) * $config['mod']['modlog_page'], PDO::PARAM_INT);
$query->execute() or error(db_error($query));
$logs = $query->fetchAll(PDO::FETCH_ASSOC);
if (empty($logs) && $page_no > 1)
error($config['error']['404']);
if (!hasPermission($config['mod']['show_ip'])) {
// Supports ipv4 only!
foreach ($logs as $i => &$log) {
$log['text'] = preg_replace_callback('/(?:<a href="\?\/IP\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}">)?(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(?:<\/a>)?/', function($matches) {
return "xxxx";//less_ip($matches[1]);
}, $log['text']);
}
}
$query = prepare("SELECT COUNT(*) FROM ``modlogs`` LEFT JOIN ``mods`` ON `mod` = ``mods``.`id` WHERE `board` = :board");
$query->bindValue(':board', $board);
$query->execute() or error(db_error($query));
$count = $query->fetchColumn();
mod_page(_('Board log'), 'mod/log.html', array('logs' => $logs, 'count' => $count, 'board' => $board, 'hide_names' => $hide_names, 'public' => $public));
}
function mod_view_board($boardName, $page_no = 1) {
global $config, $mod;
@ -850,7 +886,7 @@ function mod_page_ip($ip) {
$args['security_token'] = make_secure_link_token('IP/' . $ip);
mod_page(sprintf('%s: %s', _('IP'), $ip), 'mod/view_ip.html', $args, $args['hostname']);
mod_page(sprintf('%s: %s', _('IP'), htmlspecialchars($ip)), 'mod/view_ip.html', $args, $args['hostname']);
}
function mod_ban() {
@ -1057,6 +1093,28 @@ function mod_sticky($board, $unsticky, $post) {
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['file_index'], true, $config['redirect_http']);
}
function mod_cycle($board, $uncycle, $post) {
global $config;
if (!openBoard($board))
error($config['error']['noboard']);
if (!hasPermission($config['mod']['cycle'], $board))
error($config['error']['noaccess']);
$query = prepare(sprintf('UPDATE ``posts_%s`` SET `cycle` = :cycle WHERE `id` = :id AND `thread` IS NULL', $board));
$query->bindValue(':id', $post);
$query->bindValue(':cycle', $uncycle ? 0 : 1);
$query->execute() or error(db_error($query));
if ($query->rowCount()) {
modLog(($uncycle ? 'Made not cyclical' : 'Made cyclical') . " thread #{$post}");
buildThread($post);
buildIndex();
}
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['file_index'], true, $config['redirect_http']);
}
function mod_bumplock($board, $unbumplock, $post) {
global $config;
@ -1488,6 +1546,15 @@ function mod_edit_post($board, $edit_raw_html, $postID) {
error($config['error']['404']);
if (isset($_POST['name'], $_POST['email'], $_POST['subject'], $_POST['body'])) {
// Remove any modifiers they may have put in
$_POST['body'] = remove_modifiers($_POST['body']);
// Add back modifiers in the original post
$modifiers = extract_modifiers($post['body_nomarkup']);
foreach ($modifiers as $key => $value) {
$_POST['body'] .= "<tinyboard $key>$value</tinyboard>";
}
if ($edit_raw_html)
$query = prepare(sprintf('UPDATE ``posts_%s`` SET `name` = :name, `email` = :email, `subject` = :subject, `body` = :body, `body_nomarkup` = :body_nomarkup WHERE `id` = :id', $board));
else
@ -1516,15 +1583,20 @@ function mod_edit_post($board, $edit_raw_html, $postID) {
header('Location: ?/' . sprintf($config['board_path'], $board) . $config['dir']['res'] . link_for($post) . '#' . $postID, true, $config['redirect_http']);
} else {
// Remove modifiers
$post['body_nomarkup'] = remove_modifiers($post['body_nomarkup']);
$post['body_nomarkup'] = utf8tohtml($post['body_nomarkup']);
$post['body'] = utf8tohtml($post['body']);
if ($config['minify_html']) {
$post['body_nomarkup'] = str_replace("\n", '&#010;', utf8tohtml($post['body_nomarkup']));
$post['body'] = str_replace("\n", '&#010;', utf8tohtml($post['body']));
$post['body_nomarkup'] = str_replace("\n", '&#010;', $post['body_nomarkup']);
$post['body'] = str_replace("\n", '&#010;', $post['body']);
$post['body_nomarkup'] = str_replace("\r", '', $post['body_nomarkup']);
$post['body'] = str_replace("\r", '', $post['body']);
$post['body_nomarkup'] = str_replace("\t", '&#09;', $post['body_nomarkup']);
$post['body'] = str_replace("\t", '&#09;', $post['body']);
}
mod_page(_('Edit post'), 'mod/edit_post_form.html', array('token' => $security_token, 'board' => $board, 'raw' => $edit_raw_html, 'post' => $post));
}
}
@ -1668,6 +1740,8 @@ function mod_deletebyip($boardName, $post, $global = false) {
deletePost($post['id'], false, false);
rebuildThemes('post-delete', $board['uri']);
buildIndex();
if ($post['thread'])
$threads_to_rebuild[$post['board']][$post['thread']] = true;
@ -1753,13 +1827,12 @@ function mod_user($uid) {
}
if ($_POST['password'] != '') {
$salt = generate_salt();
$password = hash('sha256', $salt . sha1($_POST['password']));
$query = prepare('UPDATE ``mods`` SET `password` = :password, `salt` = :salt WHERE `id` = :id');
list($version, $password) = crypt_password($_POST['password']);
$query = prepare('UPDATE ``mods`` SET `password` = :password, `version` = :version WHERE `id` = :id');
$query->bindValue(':id', $uid);
$query->bindValue(':password', $password);
$query->bindValue(':salt', $salt);
$query->bindValue(':version', $version);
$query->execute() or error(db_error($query));
modLog('Changed password for ' . utf8tohtml($_POST['username']) . ' <small>(#' . $user['id'] . ')</small>');
@ -1780,13 +1853,12 @@ function mod_user($uid) {
if (hasPermission($config['mod']['change_password']) && $uid == $mod['id'] && isset($_POST['password'])) {
if ($_POST['password'] != '') {
$salt = generate_salt();
$password = hash('sha256', $salt . sha1($_POST['password']));
list($version, $password) = crypt_password($_POST['password']);
$query = prepare('UPDATE ``mods`` SET `password` = :password, `salt` = :salt WHERE `id` = :id');
$query = prepare('UPDATE ``mods`` SET `password` = :password, `version` = :version WHERE `id` = :id');
$query->bindValue(':id', $uid);
$query->bindValue(':password', $password);
$query->bindValue(':salt', $salt);
$query->bindValue(':version', $version);
$query->execute() or error(db_error($query));
modLog('Changed own password');
@ -1853,13 +1925,12 @@ function mod_user_new() {
if (!isset($config['mod']['groups'][$type]) || $type == DISABLED)
error(sprintf($config['error']['invalidfield'], 'type'));
$salt = generate_salt();
$password = hash('sha256', $salt . sha1($_POST['password']));
list($version, $password) = crypt_password($_POST['password']);
$query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :salt, :type, :boards)');
$query = prepare('INSERT INTO ``mods`` VALUES (NULL, :username, :password, :version, :type, :boards)');
$query->bindValue(':username', $_POST['username']);
$query->bindValue(':password', $password);
$query->bindValue(':salt', $salt);
$query->bindValue(':version', $version);
$query->bindValue(':type', $type);
$query->bindValue(':boards', implode(',', $boards));
$query->execute() or error(db_error($query));
@ -2600,6 +2671,167 @@ function mod_theme_rebuild($theme_name) {
));
}
// This needs to be done for `secure` CSRF prevention compatibility, otherwise the $board will be read in as the token if editing global pages.
function delete_page_base($page = '', $board = false) {
global $config, $mod;
if (empty($board))
$board = false;
if (!$board && $mod['boards'][0] !== '*')
error($config['error']['noaccess']);
if (!hasPermission($config['mod']['edit_pages'], $board))
error($config['error']['noaccess']);
if ($board !== FALSE && !openBoard($board))
error($config['error']['noboard']);
if ($board) {
$query = prepare('DELETE FROM ``pages`` WHERE `board` = :board AND `name` = :name');
$query->bindValue(':board', ($board ? $board : NULL));
} else {
$query = prepare('DELETE FROM ``pages`` WHERE `board` IS NULL AND `name` = :name');
}
$query->bindValue(':name', $page);
$query->execute() or error(db_error($query));
header('Location: ?/edit_pages' . ($board ? ('/' . $board) : ''), true, $config['redirect_http']);
}
function mod_delete_page($page = '') {
delete_page_base($page);
}
function mod_delete_page_board($page = '', $board = false) {
delete_page_base($page, $board);
}
function mod_edit_page($id) {
global $config, $mod, $board;
$query = prepare('SELECT * FROM ``pages`` WHERE `id` = :id');
$query->bindValue(':id', $id);
$query->execute() or error(db_error($query));
$page = $query->fetch();
if (!$page)
error(_('Could not find the page you are trying to edit.'));
if (!$page['board'] && $mod['boards'][0] !== '*')
error($config['error']['noaccess']);
if (!hasPermission($config['mod']['edit_pages'], $page['board']))
error($config['error']['noaccess']);
if ($page['board'] && !openBoard($page['board']))
error($config['error']['noboard']);
if (isset($_POST['method'], $_POST['content'])) {
$content = $_POST['content'];
$method = $_POST['method'];
$page['type'] = $method;
if (!in_array($method, array('markdown', 'html', 'infinity')))
error(_('Unrecognized page markup method.'));
switch ($method) {
case 'markdown':
$write = markdown($content);
break;
case 'html':
if (hasPermission($config['mod']['rawhtml'])) {
$write = $content;
} else {
$write = purify_html($content);
}
break;
case 'infinity':
$c = $content;
markup($content);
$write = $content;
$content = $c;
}
if (!isset($write) or !$write)
error(_('Failed to mark up your input for some reason...'));
$query = prepare('UPDATE ``pages`` SET `type` = :method, `content` = :content WHERE `id` = :id');
$query->bindValue(':method', $method);
$query->bindValue(':content', $content);
$query->bindValue(':id', $id);
$query->execute() or error(db_error($query));
$fn = ($board['uri'] ? ($board['uri'] . '/') : '') . $page['name'] . '.html';
$body = "<div class='ban'>$write</div>";
$html = Element('page.html', array('config' => $config, 'body' => $body, 'title' => utf8tohtml($page['title'])));
file_write($fn, $html);
}
if (!isset($content)) {
$query = prepare('SELECT `content` FROM ``pages`` WHERE `id` = :id');
$query->bindValue(':id', $id);
$query->execute() or error(db_error($query));
$content = $query->fetchColumn();
}
mod_page(sprintf(_('Editing static page: %s'), $page['name']), 'mod/edit_page.html', array('page' => $page, 'token' => make_secure_link_token("edit_page/$id"), 'content' => prettify_textarea($content), 'board' => $board));
}
function mod_pages($board = false) {
global $config, $mod, $pdo;
if (empty($board))
$board = false;
if (!$board && $mod['boards'][0] !== '*')
error($config['error']['noaccess']);
if (!hasPermission($config['mod']['edit_pages'], $board))
error($config['error']['noaccess']);
if ($board !== FALSE && !openBoard($board))
error($config['error']['noboard']);
if ($board) {
$query = prepare('SELECT * FROM ``pages`` WHERE `board` = :board');
$query->bindValue(':board', $board);
} else {
$query = query('SELECT * FROM ``pages`` WHERE `board` IS NULL');
}
$query->execute() or error(db_error($query));
$pages = $query->fetchAll(PDO::FETCH_ASSOC);
if (isset($_POST['page'])) {
if ($board and sizeof($pages) > $config['pages_max'])
error(sprintf(_('Sorry, this site only allows %d pages per board.'), $config['pages_max']));
if (!preg_match('/^[a-z0-9]{1,255}$/', $_POST['page']))
error(_('Page names must be < 255 chars and may only contain lowercase letters A-Z and digits 1-9.'));
foreach ($pages as $i => $p) {
if ($_POST['page'] === $p['name'])
error(_('Refusing to create a new page with the same name as an existing one.'));
}
$title = ($_POST['title'] ? $_POST['title'] : NULL);
$query = prepare('INSERT INTO ``pages``(board, title, name) VALUES(:board, :title, :name)');
$query->bindValue(':board', ($board ? $board : NULL));
$query->bindValue(':title', $title);
$query->bindValue(':name', $_POST['page']);
$query->execute() or error(db_error($query));
$pages[] = array('id' => $pdo->lastInsertId(), 'name' => $_POST['page'], 'board' => $board, 'title' => $title);
}
foreach ($pages as $i => &$p) {
$p['delete_token'] = make_secure_link_token('edit_pages/delete/' . $p['name'] . ($board ? ('/' . $board) : ''));
}
mod_page(_('Pages'), 'mod/pages.html', array('pages' => $pages, 'token' => make_secure_link_token('edit_pages' . ($board ? ('/' . $board) : '')), 'board' => $board));
}
function mod_debug_antispam() {
global $pdo, $config;
@ -2716,3 +2948,4 @@ function mod_debug_apc() {
mod_page(_('Debug: APC'), 'mod/debug/apc.html', array('cached_vars' => $cached_vars));
}

152
inc/nntpchan/nntpchan.php

@ -0,0 +1,152 @@
<?php
/*
* Copyright (c) 2016 vichan-devel
*/
defined('TINYBOARD') or exit;
function gen_msgid($board, $id) {
global $config;
$b = preg_replace("/[^0-9a-zA-Z$]/", 'x', $board);
$salt = sha1($board . "|" . $id . "|" . $config['nntpchan']['salt']);
$salt = substr($salt, 0, 7);
$salt = base_convert($salt, 16, 36);
return "<$b.$id.$salt@".$config['nntpchan']['domain'].">";
}
function gen_nntp($headers, $files) {
if (count($files) == 0) {
}
else if (count($files) == 1 && $files[0]['type'] == 'text/plain') {
$content = $files[0]['text'] . "\r\n";
$headers['Content-Type'] = "text/plain; charset=UTF-8";
}
else {
$boundary = sha1($headers['Message-Id']);
$content = "";
$headers['Content-Type'] = "multipart/mixed; boundary=$boundary";
foreach ($files as $file) {
$content .= "--$boundary\r\n";
if (isset($file['name'])) {
$file['name'] = preg_replace('/[\r\n\0"]/', '', $file['name']);
$content .= "Content-Disposition: form-data; filename=\"$file[name]\"; name=\"attachment\"\r\n";
}
$type = explode('/', $file['type'])[0];
if ($type == 'text') {
$file['type'] .= '; charset=UTF-8';
}
$content .= "Content-Type: $file[type]\r\n";
if ($type != 'text' && $type != 'message') {
$file['text'] = base64_encode($file['text']);
$content .= "Content-Transfer-Encoding: base64\r\n";
}
$content .= "\r\n";
$content .= $file['text'];
$content .= "\r\n";
}
$content .= "--$boundary--\r\n";
$headers['Mime-Version'] = '1.0';
}
//$headers['Content-Length'] = strlen($content);
$headers['Date'] = date('r', $headers['Date']);
$out = "";
foreach ($headers as $id => $val) {
$val = str_replace("\n", "\n\t", $val);
$out .= "$id: $val\r\n";
}
$out .= "\r\n";
$out .= $content;
return $out;
}
function nntp_publish($msg, $id) {
global $config;
$server = $config["nntpchan"]["server"];
$s = fsockopen("tcp://$server");
fgets($s);
fputs($s, "MODE STREAM\r\n");
fgets($s);
fputs($s, "TAKETHIS $id\r\n");
fputs($s, $msg);
fputs($s, "\r\n.\r\n");
fgets($s);
fputs($s, "QUIT\r\n");
fclose($s);
}
function post2nntp($post, $msgid) {
global $config;
$headers = array();
$files = array();
$headers['Message-Id'] = $msgid;
$headers['Newsgroups'] = $config['nntpchan']['group'];
$headers['Date'] = time();
$headers['Subject'] = $post['subject'] ? $post['subject'] : "None";
$headers['From'] = $post['name'] . " <poster@" . $config['nntpchan']['domain'] . ">";
if ($post['email'] == 'sage') {
$headers['X-Sage'] = true;
}
if (!$post['op']) {
// Get muh parent
$query = prepare("SELECT `message_id` FROM ``nntp_references`` WHERE `board` = :board AND `id` = :id");
$query->bindValue(':board', $post['board']);
$query->bindValue(':id', $post['thread']);
$query->execute() or error(db_error($query));
if ($result = $query->fetch(PDO::FETCH_ASSOC)) {
$headers['References'] = $result['message_id'];
}
else {
return false; // We don't have OP. Discarding.
}
}
// Let's parse the body a bit.
$body = trim($post['body_nomarkup']);
$body = preg_replace('/\r?\n/', "\r\n", $body);
$body = preg_replace_callback('@>>(>/([a-zA-Z0-9_+-]+)/)?([0-9]+)@', function($o) use ($post) {
if ($o[1]) {
$board = $o[2];
}
else {
$board = $post['board'];
}
$id = $o[3];
$query = prepare("SELECT `message_id_digest` FROM ``nntp_references`` WHERE `board` = :board AND `id` = :id");
$query->bindValue(':board', $board);
$query->bindValue(':id', $id);
$query->execute() or error(db_error($query));
if ($result = $query->fetch(PDO::FETCH_ASSOC)) {
return ">>".substr($result['message_id_digest'], 0, 18);
}
else {
return $o[0]; // Should send URL imo
}
}, $body);
$body = preg_replace('/>>>>([0-9a-fA-F])+/', '>>\1', $body);
$files[] = array('type' => 'text/plain', 'text' => $body);
foreach ($post['files'] as $id => $file) {
$fc = array();
$fc['type'] = $file['type'];
$fc['text'] = file_get_contents($file['file_path']);
$fc['name'] = $file['name'];
$files[] = $fc;
}
return array($headers, $files);
}

30
inc/nntpchan/tests.php

@ -0,0 +1,30 @@
<?php
define('TINYBOARD', 'fuck yeah');
require_once('nntpchan.php');
die();
$time = time();
echo "\n@@@@ Thread:\n";
echo $m0 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.0000.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None"],
[['type' => 'text/plain', 'text' => "THIS IS A NEW TEST THREAD"]]);
echo "\n@@@@ Single msg:\n";
echo $m1 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.1234.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"],
[['type' => 'text/plain', 'text' => "hello world, with no image :("]]);
echo "\n@@@@ Single msg and pseudoimage:\n";
echo $m2 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.2137.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"],
[['type' => 'text/plain', 'text' => "hello world, now with an image!"],
['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif.gif"]]);
echo "\n@@@@ Single msg and two pseudoimages:\n";
echo $m3 = gennntp(["From" => "czaks <marcin@6irc.net>", "Message-Id" => "<1234.1488.".$time."@example.vichan.net>", "Newsgroups" => "overchan.test", "Date" => time(), "Subject" => "None", "References" => "<1234.0000.".$time."@example.vichan.net>"],
[['type' => 'text/plain', 'text' => "hello world, now WITH TWO IMAGES!!!"],
['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif.gif"],
['type' => 'image/gif', 'text' => base64_decode("R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs="), 'name' => "urgif2.gif"]]);
shoveitup($m0, "<1234.0000.".$time."@example.vichan.net>");
sleep(1);
shoveitup($m1, "<1234.1234.".$time."@example.vichan.net>");
sleep(1);
shoveitup($m2, "<1234.2137.".$time."@example.vichan.net>");
shoveitup($m3, "<1234.1488.".$time."@example.vichan.net>");

28
inc/polyfill.php

@ -0,0 +1,28 @@
<?php
// PHP 5.4
if (!function_exists('hex2bin')) {
function hex2bin($data) {
return pack("H*" , $hex_string);
}
}
// PHP 5.6
if (!function_exists('hash_equals')) {
function hash_equals($ours, $theirs) {
$ours = (string)$ours;
$theirs = (string)$theirs;
$tlen = strlen($theirs);
$olen = strlen($ours);
$answer = 0;
for ($i = 0; $i < $tlen; $i++) {
$answer |= ord($ours[$olen > $i ? $i : 0]) ^ ord($theirs[$i]);
}
return $answer === 0 && $olen === $tlen;
}
}

49
inc/queue.php

@ -0,0 +1,49 @@
<?php
class Queue {
function __construct($key) { global $config;
if ($config['queue']['enabled'] == 'fs') {
$this->lock = new Lock($key);
$key = str_replace('/', '::', $key);
$key = str_replace("\0", '', $key);
$this->key = "tmp/queue/$key/";
}
}
function push($str) { global $config;
if ($config['queue']['enabled'] == 'fs') {
$this->lock->get_ex();
file_put_contents($this->key.microtime(true), $str);
$this->lock->free();
}
return $this;
}
function pop($n = 1) { global $config;
if ($config['queue']['enabled'] == 'fs') {
$this->lock->get_ex();
$dir = opendir($this->key);
$paths = array();
while ($n > 0) {
$path = readdir($dir);
if ($path === FALSE) break;
elseif ($path == '.' || $path == '..') continue;
else { $paths[] = $path; $n--; }
}
$out = array();
foreach ($paths as $v) {
$out []= file_get_contents($this->key.$v);
unlink($this->key.$v);
}
$this->lock->free();
return $out;
}
}
}
// Don't use the constructor. Use the get_queue function.
$queues = array();
function get_queue($name) { global $queues;
return $queues[$name] = isset ($queues[$name]) ? $queues[$name] : new Queue($name);
}

65
inc/route.php

@ -0,0 +1,65 @@
<?php
// vichan's routing mechanism
// don't bother with that unless you use smart build or advanced build
// you can use those parts for your own implementations though :^)
defined('TINYBOARD') or exit;
function route($path) { global $config;
$entrypoints = array();
$entrypoints['/%b/'] = 'sb_board';
$entrypoints['/%b/'.$config['file_index']] = 'sb_board';
$entrypoints['/%b/'.$config['file_page']] = 'sb_board';
$entrypoints['/%b/%d.json'] = 'sb_api_board';
if ($config['api']['enabled']) {
$entrypoints['/%b/threads.json'] = 'sb_api';
$entrypoints['/%b/catalog.json'] = 'sb_api';
}
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page']] = 'sb_thread_slugcheck';
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page50']] = 'sb_thread_slugcheck50';
if ($config['slugify']) {
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page_slug']] = 'sb_thread_slugcheck';
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page50_slug']] = 'sb_thread_slugcheck50';
}
if ($config['api']['enabled']) {
$entrypoints['/%b/'.$config['dir']['res'].'%d.json'] = 'sb_thread';
}
$entrypoints['/*/'] = 'sb_ukko';
$entrypoints['/*/index.html'] = 'sb_ukko';
$entrypoints['/recent.html'] = 'sb_recent';
$entrypoints['/%b/catalog.html'] = 'sb_catalog';
$entrypoints['/%b/index.rss'] = 'sb_catalog';
$entrypoints['/sitemap.xml'] = 'sb_sitemap';
$entrypoints = array_merge($entrypoints, $config['controller_entrypoints']);
$reached = false;
list($request) = explode('?', $path);
foreach ($entrypoints as $id => $fun) {
$id = '@^' . preg_quote($id, '@') . '$@u';
$id = str_replace('%b', '('.$config['board_regex'].')', $id);
$id = str_replace('%d', '([0-9]+)', $id);
$id = str_replace('%s', '[a-zA-Z0-9-]+', $id);
$matches = null;
if (preg_match ($id, $request, $matches)) {
array_shift($matches);
$reached = array($fun, $matches);
break;
}
}
return $reached;
}

144
install.php

@ -1,7 +1,7 @@
<?php
// Installation/upgrade file
define('VERSION', '4.9.93');
define('VERSION', '5.1.3');
if (fopen('inc/instance-config.php' , 'a') === false) {
print('install.php does not have permission to write to /inc/, without permission the installer cannot continue');
@ -10,6 +10,8 @@ if (fopen('inc/instance-config.php' , 'a') === false) {
require 'inc/functions.php';
loadConfig();
$step = isset($_GET['step']) ? round($_GET['step']) : 0;
$page = array(
'config' => $config,
@ -556,6 +558,41 @@ if (file_exists($config['has_installed'])) {
foreach ($boards as &$board) {
query(sprintf('ALTER TABLE ``posts_%s`` ADD `slug` VARCHAR(255) DEFAULT NULL AFTER `embed`;', $board['uri'])) or error(db_error());
}
case '4.9.93':
query('ALTER TABLE ``mods`` CHANGE `password` `password` VARCHAR(255) NOT NULL;') or error(db_error());
query('ALTER TABLE ``mods`` CHANGE `salt` `salt` VARCHAR(64) NOT NULL;') or error(db_error());
case '5.0.0':
query('ALTER TABLE ``mods`` CHANGE `salt` `version` VARCHAR(64) NOT NULL;') or error(db_error());
case '5.0.1':
case '5.1.0':
query('CREATE TABLE ``pages`` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`board` varchar(255) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`title` varchar(255) DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
`content` text,
PRIMARY KEY (`id`),
UNIQUE KEY `u_pages` (`name`,`board`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;') or error(db_error());
case '5.1.1':
foreach ($boards as &$board) {
query(sprintf("ALTER TABLE ``posts_%s`` ADD `cycle` int(1) NOT NULL AFTER `locked`", $board['uri'])) or error(db_error());
}
case '5.1.2':
query('CREATE TABLE ``nntp_references`` (
`board` varchar(60) NOT NULL,
`id` int(11) unsigned NOT NULL,
`message_id` varchar(255) CHARACTER SET ascii NOT NULL,
`message_id_digest` varchar(40) CHARACTER SET ascii NOT NULL,
`own` tinyint(1) NOT NULL,
`headers` text,
PRIMARY KEY (`message_id_digest`),
UNIQUE KEY `message_id` (`message_id`),
UNIQUE KEY `u_board_id` (`board`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
') or error(db_error());
case false:
// TODO: enhance Tinyboard -> vichan upgrade path.
query("CREATE TABLE IF NOT EXISTS ``search_queries`` ( `ip` varchar(39) NOT NULL, `time` int(11) NOT NULL, `query` text NOT NULL) ENGINE=MyISAM DEFAULT CHARSET=utf8;") or error(db_error());
@ -579,6 +616,27 @@ if (file_exists($config['has_installed'])) {
die(Element('page.html', $page));
}
function create_config_from_array(&$instance_config, &$array, $prefix = '') {
foreach ($array as $name => $value) {
if (is_array($value)) {
$instance_config .= "\n";
create_config_from_array($instance_config, $value, $prefix . '[\'' . addslashes($name) . '\']');
$instance_config .= "\n";
} else {
$instance_config .= ' $config' . $prefix . '[\'' . addslashes($name) . '\'] = ';
if (is_numeric($value))
$instance_config .= $value;
else
$instance_config .= "'" . addslashes($value) . "'";
$instance_config .= ";\n";
}
}
}
session_start();
if ($step == 0) {
// Agreeement
$page['body'] = '
@ -612,7 +670,7 @@ if ($step == 0) {
'installed' => extension_loaded('pdo'),
'required' => true
),
'PDO' => array(
'GD' => array(
'installed' => extension_loaded('gd'),
'required' => true
),
@ -625,17 +683,17 @@ if ($step == 0) {
$tests = array(
array(
'category' => 'PHP',
'name' => 'PHP &ge; 5.3',
'result' => PHP_VERSION_ID >= 50300,
'name' => 'PHP &ge; 5.4',
'result' => PHP_VERSION_ID >= 50400,
'required' => true,
'message' => 'vichan requires PHP 5.3 or better.',
'message' => 'vichan requires PHP 5.4 or better.',
),
array(
'category' => 'PHP',
'name' => 'PHP &ge; 5.4',
'result' => PHP_VERSION_ID >= 50400,
'name' => 'PHP &ge; 5.6',
'result' => PHP_VERSION_ID >= 50600,
'required' => false,
'message' => 'vichan works best on PHP 5.4 or better.',
'message' => 'vichan works best on PHP 5.6 or better.',
),
array(
'category' => 'PHP',
@ -692,6 +750,7 @@ if ($step == 0) {
'result' => $can_exec && shell_exec('which convert'),
'required' => false,
'message' => '(Optional) `convert` was not found or executable; command-line ImageMagick image processing cannot be enabled.',
'effect' => function (&$config) { $config['thumb_method'] = 'convert'; },
),
array(
'category' => 'Image processing',
@ -706,6 +765,7 @@ if ($step == 0) {
'result' => $can_exec && shell_exec('which gm'),
'required' => false,
'message' => '(Optional) `gm` was not found or executable; command-line GraphicsMagick (faster than ImageMagick) cannot be enabled.',
'effect' => function (&$config) { $config['thumb_method'] = 'gm'; },
),
array(
'category' => 'Image processing',
@ -713,13 +773,25 @@ if ($step == 0) {
'result' => $can_exec && shell_exec('which gifsicle'),
'required' => false,
'message' => '(Optional) `gifsicle` was not found or executable; you may not use `convert+gifsicle` for better animated GIF thumbnailing.',
'effect' => function (&$config) { if ($config['thumb_method'] == 'gm') $config['thumb_method'] = 'gm+gifsicle';
if ($config['thumb_method'] == 'convert') $config['thumb_method'] = 'convert+gifsicle'; },
),
array(
'category' => 'Image processing',
'name' => '`md5sum` (quick file hashing)',
'name' => '`md5sum` (quick file hashing on GNU/Linux)',
'prereq' => '',
'result' => $can_exec && shell_exec('echo "vichan" | md5sum') == "141225c362da02b5c359c45b665168de -\n",
'required' => false,
'message' => '(Optional) `md5sum` was not found or executable; file hashing for multiple images will be slower.',
'message' => '(Optional) `md5sum` was not found or executable; file hashing for multiple images will be slower. Ignore if not using Linux.',
'effect' => function (&$config) { $config['gnu_md5'] = true; },
),
array(
'category' => 'Image processing',
'name' => '`/sbin/md5` (quick file hashing on BSDs)',
'result' => $can_exec && shell_exec('echo "vichan" | /sbin/md5 -r') == "141225c362da02b5c359c45b665168de\n",
'required' => false,
'message' => '(Optional) `/sbin/md5` was not found or executable; file hashing for multiple images will be slower. Ignore if not using BSD.',
'effect' => function (&$config) { $config['bsd_md5'] = true; },
),
array(
'category' => 'File permissions',
@ -735,6 +807,13 @@ if ($step == 0) {
'required' => true,
'message' => 'You must give vichan permission to create (and write to) the <code>templates/cache</code> directory or performance will be drastically reduced.'
),
array(
'category' => 'File permissions',
'name' => getcwd() . '/tmp/cache',
'result' => is_dir('tmp/cache') && is_writable('tmp/cache'),
'required' => true,
'message' => 'You must give vichan permission to write to the <code>tmp/cache</code> directory.'
),
array(
'category' => 'File permissions',
'name' => getcwd() . '/inc/instance-config.php',
@ -758,17 +837,27 @@ if ($step == 0) {
'message' => 'vichan is still beta software and it\'s not going to come out of beta any time soon. As there are often many months between releases yet changes and bug fixes are very frequent, it\'s recommended to use the git repository to maintain your vichan installation. Using git makes upgrading much easier.'
)
);
$config['font_awesome'] = true;
$additional_config = array();
foreach ($tests as $test) {
if ($test['result'] && isset($test['effect'])) {
$test['effect']($additional_config);
}
}
$more = '';
create_config_from_array($more, $additional_config);
$_SESSION['more'] = $more;
echo Element('page.html', array(
'body' => Element('installer/check-requirements.html', array(
'extensions' => $extensions,
'tests' => $tests,
'config' => $config
'config' => $config,
)),
'title' => 'Checking environment',
'config' => $config
'config' => $config,
));
} elseif ($step == 2) {
// Basic config
@ -779,14 +868,18 @@ if ($step == 0) {
echo Element('page.html', array(
'body' => Element('installer/config.html', array(
'config' => $config
'config' => $config,
'more' => $_SESSION['more'],
)),
'title' => 'Configuration',
'config' => $config
));
} elseif ($step == 3) {
$more = $_POST['more'];
unset($_POST['more']);
$instance_config =
'<?php
'<'.'?php
/*
* Instance Configuration
@ -798,27 +891,10 @@ if ($step == 0) {
';
function create_config_from_array(&$instance_config, &$array, $prefix = '') {
foreach ($array as $name => $value) {
if (is_array($value)) {
$instance_config .= "\n";
create_config_from_array($instance_config, $value, $prefix . '[\'' . addslashes($name) . '\']');
$instance_config .= "\n";
} else {
$instance_config .= ' $config' . $prefix . '[\'' . addslashes($name) . '\'] = ';
if (is_numeric($value))
$instance_config .= $value;
else
$instance_config .= "'" . addslashes($value) . "'";
$instance_config .= ";\n";
}
}
}
create_config_from_array($instance_config, $_POST);
$instance_config .= "\n";
$instance_config .= $more;
$instance_config .= "\n";
if (@file_put_contents('inc/instance-config.php', $instance_config)) {

43
install.sql

@ -65,6 +65,7 @@ CREATE TABLE IF NOT EXISTS `boards` (
`uri` varchar(58) CHARACTER SET utf8 NOT NULL,
`title` tinytext NOT NULL,
`subtitle` tinytext,
-- `indexed` boolean default true,
PRIMARY KEY (`uri`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
@ -131,8 +132,8 @@ CREATE TABLE IF NOT EXISTS `modlogs` (
CREATE TABLE IF NOT EXISTS `mods` (
`id` smallint(6) unsigned NOT NULL AUTO_INCREMENT,
`username` varchar(30) NOT NULL,
`password` char(64) CHARACTER SET ascii NOT NULL COMMENT 'SHA256',
`salt` char(32) CHARACTER SET ascii NOT NULL,
`password` varchar(256) CHARACTER SET ascii NOT NULL COMMENT 'SHA256',
`version` varchar(64) CHARACTER SET ascii NOT NULL,
`type` smallint(2) NOT NULL,
`boards` text CHARACTER SET utf8 NOT NULL,
PRIMARY KEY (`id`),
@ -244,7 +245,7 @@ CREATE TABLE IF NOT EXISTS `search_queries` (
`ip` varchar(39) NOT NULL,
`time` int(11) NOT NULL,
`query` text NOT NULL
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
-- --------------------------------------------------------
@ -296,6 +297,42 @@ CREATE TABLE IF NOT EXISTS `ban_appeals` (
KEY `ban_id` (`ban_id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4 AUTO_INCREMENT=1 ;
-- --------------------------------------------------------
--
-- Table structure for table `pages`
--
CREATE TABLE `pages` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`board` varchar(255) DEFAULT NULL,
`name` varchar(255) NOT NULL,
`title` varchar(255) DEFAULT NULL,
`type` varchar(255) DEFAULT NULL,
`content` text,
PRIMARY KEY (`id`),
UNIQUE KEY `u_pages` (`name`,`board`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
-- --------------------------------------------------------
--
-- Table structure for table `nntp_references`
--
CREATE TABLE `nntp_references` (
`board` varchar(60) NOT NULL,
`id` int(11) unsigned NOT NULL,
`message_id` varchar(255) CHARACTER SET ascii NOT NULL,
`message_id_digest` varchar(40) CHARACTER SET ascii NOT NULL,
`own` tinyint(1) NOT NULL,
`headers` text,
PRIMARY KEY (`message_id_digest`),
UNIQUE KEY `message_id` (`message_id`),
UNIQUE KEY `u_board_id` (`board`, `id`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;

27
js/auto-scroll.js

@ -0,0 +1,27 @@
$('document').ready(function () {
var autoScroll = localStorage['autoScroll'] ? true : false;
if (window.Options && Options.get_tab('general')){
Options.extend_tab('general','<label id=\'autoScroll\'><input type=\'checkbox\' />' + ' Scroll to new posts' + '</label>');
$('#autoScroll').find('input').prop('checked', autoScroll);
}
$('#autoScroll').on('change', function() {
if(autoScroll) {
delete localStorage.autoScroll;
} else {
localStorage.autoScroll = true;
}
autoScroll =! autoScroll
if(active_page == 'thread')
$('input.auto-scroll').prop('checked', autoScroll);
});
if (active_page == 'thread') {
$('span[id="updater"]').children('a').after(' (<input class="auto-scroll" type="checkbox"></input> Scroll to New posts)');
$('input.auto-scroll').prop('checked', autoScroll);
$(document).on('new_post', function (e, post) {
if ($('input.auto-scroll').prop('checked'))
{
scrollTo(0, $(post).offset().top - window.innerHeight + $(post).outerHeight(true));
}
});
}
});

15
js/catalog-search.js

@ -17,6 +17,21 @@ var catalogSearch = function() {
$threads = $('.threads .thread'),
$searchLabel = $('<label for="catalog_search">Search: </label>'),
$searchBox = $('<input id="catalog_search" type="text" placeholder="Search" />');
function searchToggle() {
var button = $('#catalog_search_button');
if (!button.data('expanded')) {
button.data('expanded', '1');
button.text('Close');
$('.catalog_search').append(' <input id="search_field" style="border: inset 1px;">');
$('#search_field').focus();
} else {
button.removeData('expanded');
button.text('Search');
$('.catalog_search').children().last().remove();
$('div[id="Grid"]>.mix').each(function () { $(this).css('display', 'inline-block'); });
}
}
$controls.append($searchLabel)
.append($searchBox);

35
js/catalog.js

@ -1,8 +1,16 @@
if (active_page == 'catalog') $(function(){
if (localStorage.catalog !== undefined) {
var catalog = JSON.parse(localStorage.catalog);
} else {
var catalog = {};
localStorage.catalog = JSON.stringify(catalog);
}
$("#sort_by").change(function(){
var value = this.value;
$("#sort-"+value).trigger("click");
$('#Grid').mixItUp('sort', (value == "random" ? value : "sticky:desc " + value));
catalog.sort_by = value;
localStorage.catalog = JSON.stringify(catalog);
});
$("#image_size").change(function(){
@ -11,7 +19,30 @@ if (active_page == 'catalog') $(function(){
$(".grid-li").removeClass("grid-size-small");
$(".grid-li").removeClass("grid-size-large");
$(".grid-li").addClass("grid-size-"+value);
catalog.image_size = value;
localStorage.catalog = JSON.stringify(catalog);
});
$('#Grid').mixitup({});
$('#Grid').mixItUp({
animation: {
enable: false
}
});
if (catalog.sort_by !== undefined) {
$('#sort_by').val(catalog.sort_by).trigger('change');
}
if (catalog.image_size !== undefined) {
$('#image_size').val(catalog.image_size).trigger('change');
}
$('div.thread').on('click', function(e) {
if ($(this).css('overflow-y') === 'hidden') {
$(this).css('overflow-y', 'auto');
$(this).css('width', '100%');
} else {
$(this).css('overflow-y', 'hidden');
$(this).css('width', 'auto');
}
});
});

383
js/comment-toolbar.js

@ -0,0 +1,383 @@
/*
* comment-toolbar.js
* - Adds a toolbar above the commenting area containing most of 8Chan's formatting options
* - Press Esc to close quick-reply window when it's in focus
*
* Usage:
* $config['additional_javascript'][] = 'js/jquery.min.js';
* $config['additional_javascript'][] = 'js/comment-toolbar.js';
*/
if (active_page == 'thread' || active_page == 'index') {
var formatText = (function($){
"use strict";
var self = {};
self.rules = {
spoiler: {
text: _('Spoiler'),
key: 's',
multiline: false,
exclusiveline: false,
prefix:'**',
suffix:'**'
},
italics: {
text: _('Italics'),
key: 'i',
multiline: false,
exclusiveline: false,
prefix: "''",
suffix: "''"
},
bold: {
text: _('Bold'),
key: 'b',
multiline: false,
exclusiveline: false,
prefix: "'''",
suffix: "'''"
},
underline: {
text: _('Underline'),
key: 'u',
multiline: false,
exclusiveline: false,
prefix:'__',
suffix:'__'
},
code: {
text: _('Code'),
key: 'f',
multiline: true,
exclusiveline: false,
prefix: '[code]',
suffix: '[/code]'
},
strike: {
text: _('Strike'),
key: 'd',
multiline:false,
exclusiveline:false,
prefix:'~~',
suffix:'~~'
},
heading: {
text: _('Heading'),
key: 'r',
multiline:false,
exclusiveline:true,
prefix:'==',
suffix:'=='
}
};
self.toolbar_wrap = function(node) {
var parent = $(node).parents('form[name="post"]');
self.wrap(parent.find('#body')[0],'textarea[name="body"]', parent.find('.format-text > select')[0].value, false);
};
self.wrap = function(ref, target, option, expandedwrap) {
// clean and validate arguments
if (ref == null) return;
var settings = {multiline: false, exclusiveline: false, prefix:'', suffix: null};
$.extend(settings,JSON.parse(localStorage.formatText_rules)[option]);
// resolve targets into array of proper node elements
// yea, this is overly verbose, oh well.
var res = [];
if (target instanceof Array) {
for (var indexa in target) {
if (target.hasOwnProperty(indexa)) {
if (typeof target[indexa] == 'string') {
var nodes = $(target[indexa]);
for (var indexb in nodes) {
if (indexa.hasOwnProperty(indexb)) res.push(nodes[indexb]);
}
} else {
res.push(target[indexa]);
}
}
}
} else {
if (typeof target == 'string') {
var nodes = $(target);
for (var index in nodes) {
if (nodes.hasOwnProperty(index)) res.push(nodes[index]);
}
} else {
res.push(target);
}
}
target = res;
//record scroll top to restore it later.
var scrollTop = ref.scrollTop;
//We will restore the selection later, so record the current selection
var selectionStart = ref.selectionStart;
var selectionEnd = ref.selectionEnd;
var text = ref.value;
var before = text.substring(0, selectionStart);
var selected = text.substring(selectionStart, selectionEnd);
var after = text.substring(selectionEnd);
var whiteSpace = [" ","\t"];
var breakSpace = ["\r","\n"];
var cursor;
// handles multiline selections on formatting that doesn't support spanning over multiple lines
if (!settings.multiline) selected = selected.replace(/(\r|\n|\r\n)/g,settings.suffix +"$1"+ settings.prefix);
// handles formatting that requires it to be on it's own line OR if the user wishes to expand the wrap to the nearest linebreak
if (settings.exclusiveline || expandedwrap) {
// buffer the begining of the selection until a linebreak
cursor = before.length -1;
while (cursor >= 0 && breakSpace.indexOf(before.charAt(cursor)) == -1) {
cursor--;
}
selected = before.substring(cursor +1) + selected;
before = before.substring(0, cursor +1);
// buffer the end of the selection until a linebreak
cursor = 0;
while (cursor < after.length && breakSpace.indexOf(after.charAt(cursor)) == -1) {
cursor++;
}
selected += after.substring(0, cursor);
after = after.substring(cursor);
}
// set values
var res = before + settings.prefix + selected + settings.suffix + after;
$(target).val(res);
// restore the selection area and scroll of the reference
ref.selectionEnd = before.length + settings.prefix.length + selected.length;
if (selectionStart === selectionEnd) {
ref.selectionStart = ref.selectionEnd;
} else {
ref.selectionStart = before.length + settings.prefix.length;
}
ref.scrollTop = scrollTop;
};
self.build_toolbars = function(){
if (localStorage.formatText_toolbar == 'true'){
// remove existing toolbars
if ($('.format-text').length > 0) $('.format-text').remove();
// Place toolbar above each textarea input
var name, options = '', rules = JSON.parse(localStorage.formatText_rules);
for (var index in rules) {
if (!rules.hasOwnProperty(index)) continue;
name = rules[index].text;
//add hint if key exists
if (rules[index].key) {
name += ' (CTRL + '+ rules[index].key.toUpperCase() +')';
}
options += '<option value="'+ index +'">'+ name +'</option>';
}
$('[name="body"]').before('<div class="format-text"><a href="javascript:;" onclick="formatText.toolbar_wrap(this);">Wrap</a><select>'+ options +'</select></div>');
$('body').append('<style>#quick-reply .format-text>a{width:15%;display:inline-block;text-align:center;}#quick-reply .format-text>select{width:85%;};</style>');
}
};
self.add_rule = function(rule, index){
if (rule === undefined) rule = {
text: 'New Rule',
key: '',
multiline:false,
exclusiveline:false,
prefix:'',
suffix:''
}
// generate an id for the rule
if (index === undefined) {
var rules = JSON.parse(localStorage.formatText_rules);
while (rules[index] || index === undefined) {
index = ''
index +='abcdefghijklmnopqrstuvwxyz'.substr(Math.floor(Math.random()*26),1);
index +='abcdefghijklmnopqrstuvwxyz'.substr(Math.floor(Math.random()*26),1);
index +='abcdefghijklmnopqrstuvwxyz'.substr(Math.floor(Math.random()*26),1);
}
}
if (window.Options && Options.get_tab('formatting')){
var html = $('<div class="format_rule" name="'+ index +'"></div>').html('\
<input type="text" name="text" class="format_option" size="10" value=\"'+ rule.text.replace(/"/g, '&quot;') +'\">\
<input type="checkbox" name="multiline" class="format_option" '+ (rule.multiline ? 'checked' : '') +'>\
<input type="checkbox" name="exclusiveline" class="format_option" '+ (rule.exclusiveline ? 'checked' : '') +'>\
<input type="text" name="prefix" class="format_option" size="8" value=\"'+ (rule.prefix ? rule.prefix.replace(/"/g, '&quot;') : '') +'\">\
<input type="text" name="suffix" class="format_option" size="8" value=\"'+ (rule.suffix ? rule.suffix.replace(/"/g, '&quot;') : '') +'\">\
<input type="text" name="key" class="format_option" size="2" maxlength="1" value=\"'+ rule.key +'\">\
<input type="button" value="X" onclick="if(confirm(\'Do you wish to remove the '+ rule.text +' formatting rule?\'))$(this).parent().remove();">\
');
if ($('.format_rule').length > 0) {
$('.format_rule').last().after(html);
} else {
Options.extend_tab('formatting', html);
}
}
};
self.save_rules = function(){
var rule, newrules = {}, rules = $('.format_rule');
for (var index=0;rules[index];index++) {
rule = $(rules[index]);
newrules[rule.attr('name')] = {
text: rule.find('[name="text"]').val(),
key: rule.find('[name="key"]').val(),
prefix: rule.find('[name="prefix"]').val(),
suffix: rule.find('[name="suffix"]').val(),
multiline: rule.find('[name="multiline"]').is(':checked'),
exclusiveline: rule.find('[name="exclusiveline"]').is(':checked')
};
}
localStorage.formatText_rules = JSON.stringify(newrules);
self.build_toolbars();
};
self.reset_rules = function(to_default) {
$('.format_rule').remove();
var rules;
if (to_default) rules = self.rules;
else rules = JSON.parse(localStorage.formatText_rules);
for (var index in rules){
if (!rules.hasOwnProperty(index)) continue;
self.add_rule(rules[index], index);
}
};
// setup default rules for customizing
if (!localStorage.formatText_rules) localStorage.formatText_rules = JSON.stringify(self.rules);
// setup code to be ran when page is ready (work around for main.js compilation).
$(document).ready(function(){
// Add settings to Options panel general tab
if (window.Options && Options.get_tab('general')) {
var s1 = '#formatText_keybinds>input', s2 = '#formatText_toolbar>input', e = 'change';
Options.extend_tab('general', '\
<fieldset>\
<legend>Formatting Options</legend>\
<label id="formatText_keybinds"><input type="checkbox">' + _('Enable formatting keybinds') + '</label>\
<label id="formatText_toolbar"><input type="checkbox">' + _('Show formatting toolbar') + '</label>\
</fieldset>\
');
} else {
var s1 = '#formatText_keybinds', s2 = '#formatText_toolbar', e = 'click';
$('hr:first').before('<div id="formatText_keybinds" style="text-align:right"><a class="unimportant" href="javascript:void(0)">'+ _('Enable formatting keybinds') +'</a></div>');
$('hr:first').before('<div id="formatText_toolbar" style="text-align:right"><a class="unimportant" href="javascript:void(0)">'+ _('Show formatting toolbar') +'</a></div>');
}
// add the tab for customizing the format settings
if (window.Options && !Options.get_tab('formatting')) {
Options.add_tab('formatting', 'angle-right', _('Customize Formatting'));
Options.extend_tab('formatting', '\
<style>\
.format_option{\
margin-right:5px;\
overflow:initial;\
font-size:15px;\
}\
.format_option[type="text"]{\
text-align:center;\
padding-bottom: 2px;\
padding-top: 2px;\
}\
.format_option:last-child{\
margin-right:0;\
}\
fieldset{\
margin-top:5px;\
}\
</style>\
');
// Data control row
Options.extend_tab('formatting', '\
<button onclick="formatText.add_rule();">'+_('Add Rule')+'</button>\
<button onclick="formatText.save_rules();">'+_('Save Rules')+'</button>\
<button onclick="formatText.reset_rules(false);">'+_('Revert')+'</button>\
<button onclick="formatText.reset_rules(true);">'+_('Reset to Default')+'</button>\
');
// Descriptor row
Options.extend_tab('formatting', '\
<span class="format_option" style="margin-left:25px;">Name</span>\
<span class="format_option" style="margin-left:45px;" title="Multi-line: Allow formatted area to contain linebreaks.">ML</span>\
<span class="format_option" style="margin-left:0px;" title="Exclusive-line: Require formatted area to start after and end before a linebreak.">EL</span>\
<span class="format_option" style="margin-left:25px;" title="Text injected at the start of a format area.">Prefix</span>\
<span class="format_option" style="margin-left:60px;" title="Text injected at the end of a format area.">Suffix</span>\
<span class="format_option" style="margin-left:40px;" title="Optional keybind value to allow keyboard shortcut access.">Key</span>\
');
// Rule rows
var rules = JSON.parse(localStorage.formatText_rules);
for (var index in rules){
if (!rules.hasOwnProperty(index)) continue;
self.add_rule(rules[index], index);
}
}
// setting for enabling formatting keybinds
$(s1).on(e, function(e) {
console.log('Keybind');
if (!localStorage.formatText_keybinds || localStorage.formatText_keybinds == 'false') {
localStorage.formatText_keybinds = 'true';
if (window.Options && Options.get_tab('general')) e.target.checked = true;
} else {
localStorage.formatText_keybinds = 'false';
if (window.Options && Options.get_tab('general')) e.target.checked = false;
}
});
// setting for toolbar injection
$(s2).on(e, function(e) {
console.log('Toolbar');
if (!localStorage.formatText_toolbar || localStorage.formatText_toolbar == 'false') {
localStorage.formatText_toolbar = 'true';
if (window.Options && Options.get_tab('general')) e.target.checked = true;
formatText.build_toolbars();
} else {
localStorage.formatText_toolbar = 'false';
if (window.Options && Options.get_tab('general')) e.target.checked = false;
$('.format-text').remove();
}
});
// make sure the tab settings are switch properly at loadup
if (window.Options && Options.get_tab('general')) {
if (localStorage.formatText_keybinds == 'true') $(s1)[0].checked = true;
else $(s1)[0].checked = false;
if (localStorage.formatText_toolbar == 'true') $(s2)[0].checked = true;
else $(s2)[0].checked = false;
}
// Initial toolbar injection
formatText.build_toolbars();
//attach listener to <body> so it also works on quick-reply box
$('body').on('keydown', '[name="body"]', function(e) {
if (!localStorage.formatText_keybinds || localStorage.formatText_keybinds == 'false') return;
var key = String.fromCharCode(e.which).toLowerCase();
var rules = JSON.parse(localStorage.formatText_rules);
for (var index in rules) {
if (!rules.hasOwnProperty(index)) continue;
if (key === rules[index].key && e.ctrlKey) {
e.preventDefault();
if (e.shiftKey) {
formatText.wrap(e.target, 'textarea[name="body"]', index, true);
} else {
formatText.wrap(e.target, 'textarea[name="body"]', index, false);
}
}
}
});
// Signal that comment-toolbar loading has completed.
$(document).trigger('formatText');
});
return self;
})(jQuery);
}

14
js/expand-all-images.js

@ -23,7 +23,15 @@ onready(function(){
.text(_('Expand all images'))
.click(function() {
$('a img.post-image').each(function() {
if (!$(this).parent()[0].dataset.expanded)
// Don't expand YouTube embeds
if ($(this).parent().parent().hasClass('video-container'))
return;
// or WEBM
if (/^\/player\.php\?/.test($(this).parent().attr('href')))
return;
if (!$(this).parent().data('expanded'))
$(this).parent().click();
});
@ -34,8 +42,8 @@ onready(function(){
$('div#shrink-all-images a')
.text(_('Shrink all images'))
.click(function(){
$('a img.post-image').each(function() {
if ($(this).parent()[0].dataset.expanded)
$('a img.full-image').each(function() {
if ($(this).parent().data('expanded'))
$(this).parent().click();
});
$(this).parent().remove();

2
js/expand-too-long.js

@ -17,7 +17,7 @@ $(function() {
e.preventDefault();
var url = $(this).attr('href');
var body = $(this).parent().parent();
var body = $(this).parents('.body');
$.ajax({
url: url,

2
js/favorites.js

@ -50,7 +50,7 @@ function add_favorites() {
$('.boardlist').append(boards);
};
if (active_page == 'thread' || active_page == 'index') {
if (active_page == 'thread' || active_page == 'index' || active_page == 'catalog' || active_page == 'ukko') {
$(document).ready(function(){
var favorites = JSON.parse(localStorage.favorites);
var is_board_favorite = ~$.inArray(board_name, favorites);

3
js/file-selector.js

@ -1,8 +1,9 @@
/*
* file-selector.js - Add support for drag and drop file selection, and paste from clipbboard on supported browsers.
* file-selector.js - Add support for drag and drop file selection, and paste from clipboard on supported browsers.
*
* Usage:
* $config['additional_javascript'][] = 'js/jquery.min.js';
* $config['additional_javascript'][] = 'js/ajax.js';
* $config['additional_javascript'][] = 'js/file-selector.js';
*/
function init_file_selector(max_images) {

67
js/fix-report-delete-submit.js

@ -1,26 +1,69 @@
/*
* fix-report-delete-submit.js
* https://github.com/savetheinternet/Tinyboard/blob/master/js/fix-report-delete-submit.js
*
* Fixes a known bug regarding the delete/report submit buttons.
*
* Released under the MIT license
* Copyright (c) 2012 Michael Save <savetheinternet@tinyboard.org>
*
* Usage:
* $config['additional_javascript'][] = 'js/jquery.min.js';
* $config['additional_javascript'][] = 'js/post-menu.js';
* $config['additional_javascript'][] = 'js/fix-report-delete-submit.js';
*
*/
$(document).ready(function(){
$('form[name="postcontrols"] div.delete input:not([type="checkbox"]):not([type="submit"]):not([type="hidden"])').keypress(function(e) {
if(e.which == 13) {
if (active_page == 'thread' || active_page == 'index' || active_page == 'ukko') {
$(document).on('menu_ready', function(){
var Menu = window.Menu;
if ($('#delete-fields #password').length) {
Menu.add_item("delete_post_menu", _("Delete post"));
Menu.add_item("delete_file_menu", _("Delete file"));
Menu.onclick(function(e, $buf) {
var ele = e.target.parentElement.parentElement;
var $ele = $(ele);
var threadId = $ele.parent().attr('id').replace('thread_', '');
var postId = $ele.find('.post_no').not('[id]').text();
var board_name = $ele.parent().data('board');
$buf.find('#delete_post_menu,#delete_file_menu').click(function(e) {
e.preventDefault();
$(this).next().click();
return false;
$('#delete_'+postId).prop('checked', 'checked');
if ($(this).attr('id') === 'delete_file_menu') {
$('#delete_file').prop('checked', 'checked');
} else {
$('#delete_file').prop('checked', '');
}
$('input[type="hidden"][name="board"]').val(board_name);
$('input[name=delete][type=submit]').click();
});
});
}
Menu.add_item("report_menu", _("Report"));
//Menu.add_item("global_report_menu", _("Global report"));
Menu.onclick(function(e, $buf) {
var ele = e.target.parentElement.parentElement;
var $ele = $(ele);
var threadId = $ele.parent().attr('id').replace('thread_', '');
var postId = $ele.find('.post_no').not('[id]').text();
var board_name = $ele.parent().data('board');
$buf.find('#report_menu,#global_report_menu').click(function(e) {
if ($(this).attr('id') === "global_report_menu") {
var global = '&global';
} else {
var global = '';
}
return true;
window.open(configRoot+'report.php?board='+board_name+'&post=delete_'+postId+global, "", (global?"width=600, height=575":"width=500, height=275"));
});
});
$(document).on('new_post', function(){
$('input.delete').hide();
});
$('input.delete').hide();
$('#post-moderation-fields').hide();
});
if (typeof window.Menu !== "undefined") {
$(document).trigger('menu_ready');
}
}

72
js/forced-anon.js

@ -18,10 +18,10 @@
if (active_page == 'ukko' || active_page == 'thread' || active_page == 'index' || (window.Options && Options.get_tab('general')))
$(document).ready(function() {
var force_anon = function() {
if($(this).children('a.capcode').length == 0) {
if ($(this).children('a.capcode').length == 0) {
var id = $(this).parent().children('a.post_no:eq(1)').text();
if($(this).children('a.email').length != 0)
if ($(this).children('a.email').length != 0)
var p = $(this).children('a.email');
else
var p = $(this);
@ -29,7 +29,7 @@ $(document).ready(function() {
old_info[id] = {'name': p.children('span.name').text(), 'trip': p.children('span.trip').text()};
p.children('span.name').text('Anonymous');
if(p.children('span.trip').length != 0)
if (p.children('span.trip').length != 0)
p.children('span.trip').text('');
}
};
@ -40,44 +40,60 @@ $(document).ready(function() {
var disable_fa = function() {
$('p.intro label').each(function() {
if($(this).children('a.capcode').length == 0) {
if ($(this).children('a.capcode').length == 0) {
var id = $(this).parent().children('a.post_no:eq(1)').text();
if(old_info[id]) {
if($(this).children('a.email').length != 0)
if ($(this).children('a.email').length != 0)
var p = $(this).children('a.email');
else
var p = $(this);
p.children('span.name').text(old_info[id]['name']);
if(p.children('span.trip').length != 0)
if (p.children('span.trip').length != 0)
p.children('span.trip').text(old_info[id]['trip']);
}
}
});
};
var toggle_id = function() {
if (localStorage.hideids == 'true'){
$(this).addClass('hidden');
} else {
$(this).removeClass('hidden');
}
};
old_info = {};
forced_anon = localStorage['forcedanon'] ? true : false;
var selector, event;
if (window.Options && Options.get_tab('general')) {
selector = '#forced-anon';
event = 'change';
Options.extend_tab("general", "<label id='forced-anon'><input type='checkbox' />"+_('Forced anonymity')+"</label>");
}
else {
selector = '#forced-anon';
event = 'click';
if (window.Options && Options.get_tab('general')) {
var s1 = '#hide-ids', s2 = '#forced-anon', e = 'change';
Options.extend_tab("general", "<label id='hide-ids'><input type='checkbox' /> "+_('Hide IDs')+"</label>");
Options.extend_tab("general", "<label id='forced-anon'><input type='checkbox' /> "+_('Forced anonymity')+"</label>");
}
else {
var s1 = '#hide-ids', s2 = '#forced-anon', e = 'click';
$('hr:first').before('<div id="hide-ids" style="text-align:right"><a class="unimportant" href="javascript:void(0)">Hide IDs</a></div>');
$('hr:first').before('<div id="forced-anon" style="text-align:right"><a class="unimportant" href="javascript:void(0)">-</a></div>');
$('div#forced-anon a').text(_('Forced anonymity')+' (' + (forced_anon ? _('enabled') : _('disabled')) + ')');
}
}
$(s1).on(e, function(e) {
if (!localStorage.hideids || localStorage.hideids == 'false') {
localStorage.hideids = 'true';
if (window.Options && Options.get_tab('general')) e.target.checked = true;
} else {
localStorage.hideids = 'false';
if (window.Options && Options.get_tab('general')) e.target.checked = false;
}
$('.poster_id').each(toggle_id);
});
$(selector).on(event, function() {
$(s2).on(e, function() {
forced_anon = !forced_anon;
if(forced_anon) {
if (forced_anon) {
$('div#forced-anon a').text(_('Forced anonymity')+' ('+_('enabled')+')');
localStorage.forcedanon = true;
enable_fa();
@ -86,21 +102,27 @@ $(document).ready(function() {
delete localStorage.forcedanon;
disable_fa();
}
return false;
});
// initial option setup on script load
if (localStorage.hideids == 'true'){
if (window.Options && Options.get_tab('general')) $('#hide-ids>input').prop('checked',true);
$('.poster_id').each(toggle_id);
}
if(forced_anon) {
enable_fa();
if (window.Options && Options.get_tab('general')) {
$('#toggle-locked-threads>input').prop('checked', true);
}
if (window.Options && Options.get_tab('general')) {
$('#toggle-locked-threads>input').prop('checked', true);
}
}
$(document).on('new_post', function(e, post) {
if(forced_anon)
if (forced_anon)
$(post).find('p.intro label').each(force_anon);
if (localStorage.hideids == 'true')
$(post).find('.poster_id').each(toggle_id);
});
});

2
js/hide-images.js

@ -77,7 +77,7 @@ $(document).ready(function(){
$(this).hide().after(show_link);
if ($(img).parent()[0].dataset.expanded == 'true') {
if ($(img).parent().data('expanded') == 'true') {
$(img).parent().click();
}

2
js/hide-threads.js

@ -36,7 +36,7 @@ $(document).ready(function(){
}
}
var fields_to_hide = 'div.post,div.video-container,video,iframe,img:not(.unanimated),canvas,p.fileinfo,a.hide-thread-link,div.new-posts,br';
var fields_to_hide = 'div.file,div.post,div.video-container,video,iframe,img:not(.unanimated),canvas,p.fileinfo,a.hide-thread-link,div.new-posts,br';
var do_hide_threads = function() {
var id = $(this).children('p.intro').children('a.post_no:eq(1)').text();

16
js/id_colors.js

@ -1,18 +1,18 @@
if (active_page == 'thread' || active_page == 'index') {
$(document).ready(function(){
if (window.Options && Options.get_tab('general')) {
selector = '#color-ids>input';
event = 'change';
var selector = '#color-ids>input';
var e = 'change';
Options.extend_tab("general", "<label id='color-ids'><input type='checkbox' /> "+_('Color IDs')+"</label>");
}
else {
selector = '#color-ids';
event = 'click';
var selector = '#color-ids';
var e = 'click';
$('hr:first').before('<div id="color-ids" style="text-align:right"><a class="unimportant" href="javascript:void(0)">'+_('Color IDs')+'</a></div>')
}
$(selector).on(event, function() {
$(selector).on(e, function() {
if (localStorage.color_ids === 'true') {
localStorage.color_ids = 'false';
} else {
@ -50,12 +50,6 @@ if (active_page == 'thread' || active_page == 'index') {
"border-radius": "8px",
"color": ft
});
$(el).mouseover(function() {
$(this).css('color', '#800000'); // how about a CSS :hover rule instead?
}).mouseout(function() {
$(this).css('color', ft);
});
}
$(".poster_id").each(function(k, v){

1
js/image-hover.js

@ -180,3 +180,4 @@ function imageHoverEnd() { //Pashe, WTFPL
initImageHover();
});
}

89
js/infinite-scroll.js

@ -21,7 +21,7 @@ var activate = function() {
if (document.location.hash != '#all') return false;
$(window).on("scroll", function() {
scrolltest();
scrolltest();
});
scrolltest();
@ -30,53 +30,54 @@ var activate = function() {
var scrolltest = function() {
if ($(window).scrollTop() + $(window).height() + 1000 > $(document).height() && !loading) {
load_next_page();
load_next_page();
}
};
var load_next_page = function() {
if (loading) return;
loading = true;
var this_page = $(".pages a.selected:last");
var next_page = this_page.next();
var href = next_page.prop("href");
if (!href) return;
var boardheader = $('<h2>'+_('Page')+' '+next_page.html()+'</h2>');
var loading_ind = $('<h2>'+_('Loading...')+'</h2>').insertBefore('form[name="postcontrols"]>.delete:first');
$.get(href, function(data) {
var doc = $(data);
loading_ind.remove();
boardheader.insertBefore('form[name="postcontrols"]>.delete:first');
var i = 0;
doc.find('div[id*="thread_"]').each(function() {
var checkout = $(this).attr('id').replace('thread_', '');
var $this = this;
if ($('div#thread_' + checkout).length == 0) {
// Delay DOM insertion to lessen the lag.
setTimeout(function() {
$($this).insertBefore('form[name="postcontrols"]>.delete:first');
$(document).trigger('new_post', $this);
$($this).hide().slideDown();
}, 500*i);
i++;
}
});
setTimeout(function() {
loading = false;
scrolltest();
}, 500*(i+1));
next_page.addClass('selected');
});
if (loading) return;
loading = true;
var this_page = $(".pages a.selected:last");
var next_page = this_page.next();
var href = next_page.prop("href");
if (!href) return;
var boardheader = $('<h2>'+_('Page')+' '+next_page.html()+'</h2>');
var loading_ind = $('<h2>'+_('Loading...')+'</h2>').insertBefore('#post-moderation-fields');
$.get(href, function(data) {
var doc = $(data);
loading_ind.remove();
boardheader.insertBefore('#post-moderation-fields');
var i = 0;
doc.find('div[id*="thread_"]').each(function() {
var checkout = $(this).attr('id').replace('thread_', '');
var $this = this;
if ($('div#thread_' + checkout).length == 0) {
// Delay DOM insertion to lessen the lag.
setTimeout(function() {
$($this).insertBefore('#post-moderation-fields');
$(document).trigger('new_post', $this);
$($this).hide().slideDown();
}, 500*i);
i++;
}
});
setTimeout(function() {
loading = false;
scrolltest();
}, 500*(i+1));
next_page.addClass('selected');
});
};
var button = $("<a href='#all'>"+_("All")+" </a>").prependTo(".pages");

206
js/inline-expanding.js

@ -7,65 +7,201 @@
* Copyright (c) 2013-2014 Marcin Łabanowski <marcin@6irc.net>
*
* Usage:
* // $config['additional_javascript'][] = 'js/jquery.min.js';
* $config['additional_javascript'][] = 'js/jquery.min.js';
* $config['additional_javascript'][] = 'js/inline-expanding.js';
*
*/
onready(function(){
$(document).ready(function(){
'use strict';
var DEFAULT_MAX = 5; // default maximum image loads
var inline_expand_post = function() {
var link = this.getElementsByTagName('a');
var loadingQueue = (function () {
var MAX_IMAGES = localStorage.inline_expand_max || DEFAULT_MAX; // maximum number of images to load concurrently, 0 to disable
var loading = 0; // number of images that is currently loading
var waiting = []; // waiting queue
var enqueue = function (ele) {
waiting.push(ele);
};
var dequeue = function () {
return waiting.shift();
};
var update = function() {
var ele;
while (loading < MAX_IMAGES || MAX_IMAGES === 0) {
ele = dequeue();
if (ele) {
++loading;
ele.deferred.resolve();
} else {
return;
}
}
};
return {
remove: function (ele) {
var i = waiting.indexOf(ele);
if (i > -1) {
waiting.splice(i, 1);
}
if ($(ele).data('imageLoading') === 'true') {
$(ele).data('imageLoading', 'false');
clearTimeout(ele.timeout);
--loading;
}
},
add: function (ele) {
ele.deferred = $.Deferred();
ele.deferred.done(function () {
var $loadstart = $.Deferred();
var thumb = ele.childNodes[0];
var img = ele.childNodes[1];
var onLoadStart = function (img) {
if (img.naturalWidth) {
$loadstart.resolve(img, thumb);
} else {
return (ele.timeout = setTimeout(onLoadStart, 30, img));
}
};
$(img).one('load', function () {
$.when($loadstart).done(function () {
// once fully loaded, update the waiting queue
--loading;
$(ele).data('imageLoading', 'false');
update();
});
});
$loadstart.done(function (img, thumb) {
thumb.style.display = 'none';
img.style.display = '';
});
img.setAttribute('src', ele.href);
$(ele).data('imageLoading', 'true');
ele.timeout = onLoadStart(img);
});
if (loading < MAX_IMAGES || MAX_IMAGES === 0) {
++loading;
ele.deferred.resolve();
} else {
enqueue(ele);
}
}
};
})();
for (var i = 0; i < link.length; i++) {
if (typeof link[i] == "object" && link[i].childNodes && typeof link[i].childNodes[0] !== 'undefined' && link[i].childNodes[0].src && link[i].childNodes[0].className.match(/post-image/) && !link[i].className.match(/file/)) {
link[i].childNodes[0].style.maxWidth = '98%';
if (typeof link[i] == "object" && link[i].childNodes && typeof link[i].childNodes[0] !== 'undefined' &&
link[i].childNodes[0].src && link[i].childNodes[0].className.match(/post-image/) && !link[i].className.match(/file/)) {
link[i].onclick = function(e) {
if (this.childNodes[0].className == 'hidden')
var img, post_body, still_open, canvas, scroll;
var thumb = this.childNodes[0];
var padding = 5;
var boardlist = $('.boardlist')[0];
if (thumb.className == 'hidden')
return false;
if (e.which == 2 || e.metaKey)
if (e.which == 2 || e.ctrlKey) // open in new tab
return true;
if (!this.dataset.src) {
if (!$(this).data('expanded')) {
if (~this.parentNode.className.indexOf('multifile'))
$(this).data('width', this.parentNode.style.width);
this.parentNode.removeAttribute('style');
this.dataset.expanded = 'true';
$(this).data('expanded', 'true');
if (this.childNodes[0].tagName === 'CANVAS') {
this.removeChild(this.childNodes[0]);
this.childNodes[0].style.display = 'block';
if (thumb.tagName === 'CANVAS') {
canvas = thumb;
thumb = thumb.nextSibling;
this.removeChild(canvas);
canvas.style.display = 'block';
}
this.dataset.src= this.childNodes[0].src;
this.dataset.width = this.childNodes[0].style.width;
this.dataset.height = this.childNodes[0].style.height;
this.childNodes[0].src = this.href;
this.childNodes[0].style.width = 'auto';
this.childNodes[0].style.height = 'auto';
this.childNodes[0].style.opacity = '0.4';
this.childNodes[0].style.filter = 'alpha(opacity=40)';
this.childNodes[0].onload = function() {
this.style.opacity = '';
delete this.style.filter;
}
thumb.style.opacity = '0.4';
thumb.style.filter = 'alpha(opacity=40)';
img = document.createElement('img');
img.className = 'full-image';
img.style.display = 'none';
img.setAttribute('alt', 'Fullsized image');
this.appendChild(img);
loadingQueue.add(this);
} else {
loadingQueue.remove(this);
scroll = false;
// scroll to thumb if not triggered by 'shrink all image'
if (e.target.className == 'full-image') {
scroll = true;
}
if (~this.parentNode.className.indexOf('multifile'))
this.parentNode.style.width = (parseInt(this.dataset.width)+40)+'px';
this.childNodes[0].src = this.dataset.src;
this.childNodes[0].style.width = this.dataset.width;
this.childNodes[0].style.height = this.dataset.height;
delete this.dataset.expanded;
delete this.dataset.src;
delete this.childNodes[0].style.opacity;
delete this.childNodes[0].style.filter;
this.parentNode.style.width = $(this).data('width');
thumb.style.opacity = '';
thumb.style.display = '';
if (thumb.nextSibling) this.removeChild(thumb.nextSibling); //full image loaded or loading
$(this).removeData('expanded');
delete thumb.style.filter;
// do the scrolling after page reflow
if (scroll) {
post_body = $(thumb).parentsUntil('form > div').last();
// on multifile posts, determin how many other images are still expanded
still_open = post_body.find('.post-image').filter(function(){
return $(this).parent().data('expanded') == 'true';
}).length;
// deal with differnt boards' menu styles
if ($(boardlist).css('position') == 'fixed')
padding += boardlist.getBoundingClientRect().height;
if (still_open > 0) {
if (thumb.getBoundingClientRect().top - padding < 0)
$(document).scrollTop($(thumb).parent().parent().offset().top - padding);
} else {
if (post_body[0].getBoundingClientRect().top - padding < 0)
$(document).scrollTop(post_body.offset().top - padding);
}
}
if (localStorage.no_animated_gif === 'true' && typeof unanimate_gif === 'function') {
unanimate_gif(this.childNodes[0]);
unanimate_gif(thumb);
}
}
return false;
}
};
}
}
};
// setting up user option
if (window.Options && Options.get_tab('general')) {
Options.extend_tab('general', '<span id="inline-expand-max">'+ _('Number of simultaneous image downloads (0 to disable): ') +
'<input type="number" step="1" min="0" size="4"></span>');
$('#inline-expand-max input')
.css('width', '50px')
.val(localStorage.inline_expand_max || DEFAULT_MAX)
.on('change', function (e) {
// validation in case some fucktard tries to enter a negative floating point number
var n = parseInt(e.target.value);
var val = (n<0) ? 0 : n;
localStorage.inline_expand_max = val;
});
}
if (window.jQuery) {

8
js/inline.js

@ -123,8 +123,8 @@ $(document).ready(function() {
$clone.insertAfter(link.node)
}
App.options.add('useInlining', 'Enable inlining')
App.options.add('hidePost', 'Hide inlined backlinked posts')
App.options.add('useInlining', _('Enable inlining'))
App.options.add('hidePost', _('Hide inlined backlinked posts'))
$('head').append(
'<style>' +
@ -138,8 +138,8 @@ $(document).ready(function() {
// don't attach to outbound links
if (App.options.get('useInlining')) {
var assign_inline = function() {
$('.body a:not([rel]), .mentioned a')
var assign_inline = function() {
$('.body a[href*="'+location.pathname+'"]').not('[rel]').not('.toolong > a').add('.mentioned a')
.attr('onclick', null)// XXX disable highlightReply
.off('click')
.click(inline)

106
js/jquery.mixitup.min.js

@ -1,43 +1,67 @@
/*
* MIXITUP - A CSS3 and JQuery Filter & Sort Plugin
* Version: 1.5.5
* License: Creative Commons Attribution-NoDerivs 3.0 Unported - CC BY-ND 3.0
* http://creativecommons.org/licenses/by-nd/3.0/
* This software may be used freely on commercial and non-commercial projects with attribution to the author/copyright holder.
* Author: Patrick Kunka
* Copyright 2012-2013 Patrick Kunka, Barrel LLC, All Rights Reserved
*
* http://mixitup.io
*/
Copyright 2014 KunkaLabs Limited.
@author KunkaLabs Limited.
@link https://mixitup.kunkalabs.com
@license Commercial use requires a commercial license.
https://mixitup.kunkalabs.com/licenses/
(function(d){function r(e,c,l,b,a){function f(){n.unbind("webkitTransitionEnd transitionend otransitionend oTransitionEnd");c&&x(c,l,b,a);a.startOrder=[];a.newOrder=[];a.origSort=[];a.checkSort=[];v.removeStyle(a.prefix+"filter, filter, "+a.prefix+"transform, transform, opacity, display").css(a.clean).removeAttr("data-checksum");window.atob||v.css({display:"none",opacity:"0"});n.removeStyle(a.prefix+"transition, transition, "+a.prefix+"perspective, perspective, "+a.prefix+"perspective-origin, perspective-origin, "+
(a.resizeContainer?"height":""));"list"==a.layoutMode?(p.css({display:a.targetDisplayList,opacity:"1"}),a.origDisplay=a.targetDisplayList):(p.css({display:a.targetDisplayGrid,opacity:"1"}),a.origDisplay=a.targetDisplayGrid);a.origLayout=a.layoutMode;setTimeout(function(){v.removeStyle(a.prefix+"transition, transition");a.mixing=!1;if("function"==typeof a.onMixEnd){var b=a.onMixEnd.call(this,a);a=b?b:a}})}clearInterval(a.failsafe);a.mixing=!0;a.filter=e;if("function"==typeof a.onMixStart){var g=a.onMixStart.call(this,
a);a=g?g:a}for(var k=a.transitionSpeed,g=0;2>g;g++){var h=0==g?h=a.prefix:"";a.transition[h+"transition"]="all "+k+"ms linear";a.transition[h+"transform"]=h+"translate3d(0,0,0)";a.perspective[h+"perspective"]=a.perspectiveDistance+"px";a.perspective[h+"perspective-origin"]=a.perspectiveOrigin}var w=a.targetSelector,v=b.find(w);v.each(function(){this.data={}});var n=v.parent();n.css(a.perspective);a.easingFallback="ease-in-out";"smooth"==a.easing&&(a.easing="cubic-bezier(0.25, 0.46, 0.45, 0.94)");
"snap"==a.easing&&(a.easing="cubic-bezier(0.77, 0, 0.175, 1)");"windback"==a.easing&&(a.easing="cubic-bezier(0.175, 0.885, 0.320, 1.275)",a.easingFallback="cubic-bezier(0.175, 0.885, 0.320, 1)");"windup"==a.easing&&(a.easing="cubic-bezier(0.6, -0.28, 0.735, 0.045)",a.easingFallback="cubic-bezier(0.6, 0.28, 0.735, 0.045)");g="list"==a.layoutMode&&null!=a.listEffects?a.listEffects:a.effects;Array.prototype.indexOf&&(a.fade=-1<g.indexOf("fade")?"0":"",a.scale=-1<g.indexOf("scale")?"scale(.01)":"",a.rotateZ=
-1<g.indexOf("rotateZ")?"rotate(180deg)":"",a.rotateY=-1<g.indexOf("rotateY")?"rotateY(90deg)":"",a.rotateX=-1<g.indexOf("rotateX")?"rotateX(90deg)":"",a.blur=-1<g.indexOf("blur")?"blur(8px)":"",a.grayscale=-1<g.indexOf("grayscale")?"grayscale(100%)":"");var p=d(),s=d(),t=[],r=!1;"string"===typeof e?t=z(e):(r=!0,d.each(e,function(a){t[a]=z(this)}));"or"==a.filterLogic?(""==t[0]&&t.shift(),1>t.length?s=s.add(b.find(w+":visible")):v.each(function(){var a=d(this);if(r){var b=0;d.each(t,function(d){this.length?
a.is("."+this.join(", ."))&&b++:0<b&&b++});b==t.length?p=p.add(a):s=s.add(a)}else a.is("."+t.join(", ."))?p=p.add(a):s=s.add(a)})):(p=p.add(n.find(w+"."+t.join("."))),s=s.add(n.find(w+":not(."+t.join(".")+"):visible")));e=p.length;var u=d(),q=d(),m=d();s.each(function(){var a=d(this);"none"!=a.css("display")&&(u=u.add(a),m=m.add(a))});if(p.filter(":visible").length==e&&!u.length&&!c){if(a.origLayout==a.layoutMode)return f(),!1;if(1==p.length)return"list"==a.layoutMode?(b.addClass(a.listClass),b.removeClass(a.gridClass),
m.css("display",a.targetDisplayList)):(b.addClass(a.gridClass),b.removeClass(a.listClass),m.css("display",a.targetDisplayGrid)),f(),!1}a.origHeight=n.height();if(p.length){b.removeClass(a.failClass);p.each(function(){var a=d(this);"none"==a.css("display")?q=q.add(a):m=m.add(a)});if(a.origLayout!=a.layoutMode&&!1==a.animateGridList)return"list"==a.layoutMode?(b.addClass(a.listClass),b.removeClass(a.gridClass),m.css("display",a.targetDisplayList)):(b.addClass(a.gridClass),b.removeClass(a.listClass),
m.css("display",a.targetDisplayGrid)),f(),!1;if(!window.atob)return f(),!1;v.css(a.clean);m.each(function(){this.data.origPos=d(this).offset()});"list"==a.layoutMode?(b.addClass(a.listClass),b.removeClass(a.gridClass),q.css("display",a.targetDisplayList)):(b.addClass(a.gridClass),b.removeClass(a.listClass),q.css("display",a.targetDisplayGrid));q.each(function(){this.data.showInterPos=d(this).offset()});u.each(function(){this.data.hideInterPos=d(this).offset()});m.each(function(){this.data.preInterPos=
d(this).offset()});"list"==a.layoutMode?m.css("display",a.targetDisplayList):m.css("display",a.targetDisplayGrid);c&&x(c,l,b,a);if(c&&A(a.origSort,a.checkSort))return f(),!1;u.hide();q.each(function(a){this.data.finalPos=d(this).offset()});m.each(function(){this.data.finalPrePos=d(this).offset()});a.newHeight=n.height();c&&x("reset",null,b,a);q.hide();m.css("display",a.origDisplay);"block"==a.origDisplay?(b.addClass(a.listClass),q.css("display",a.targetDisplayList)):(b.removeClass(a.listClass),q.css("display",
a.targetDisplayGrid));a.resizeContainer&&n.css("height",a.origHeight+"px");e={};for(g=0;2>g;g++)h=0==g?h=a.prefix:"",e[h+"transform"]=a.scale+" "+a.rotateX+" "+a.rotateY+" "+a.rotateZ,e[h+"filter"]=a.blur+" "+a.grayscale;q.css(e);m.each(function(){var b=this.data,c=d(this);c.hasClass("mix_tohide")?(b.preTX=b.origPos.left-b.hideInterPos.left,b.preTY=b.origPos.top-b.hideInterPos.top):(b.preTX=b.origPos.left-b.preInterPos.left,b.preTY=b.origPos.top-b.preInterPos.top);for(var e={},k=0;2>k;k++){var h=
0==k?h=a.prefix:"";e[h+"transform"]="translate("+b.preTX+"px,"+b.preTY+"px)"}c.css(e)});"list"==a.layoutMode?(b.addClass(a.listClass),b.removeClass(a.gridClass)):(b.addClass(a.gridClass),b.removeClass(a.listClass));setTimeout(function(){if(a.resizeContainer){for(var b={},c=0;2>c;c++){var e=0==c?e=a.prefix:"";b[e+"transition"]="all "+k+"ms ease-in-out";b.height=a.newHeight+"px"}n.css(b)}u.css("opacity",a.fade);q.css("opacity",1);q.each(function(){var b=this.data;b.tX=b.finalPos.left-b.showInterPos.left;
b.tY=b.finalPos.top-b.showInterPos.top;for(var c={},e=0;2>e;e++){var h=0==e?h=a.prefix:"";c[h+"transition-property"]=h+"transform, "+h+"filter, opacity";c[h+"transition-timing-function"]=a.easing+", linear, linear";c[h+"transition-duration"]=k+"ms";c[h+"transition-delay"]="0";c[h+"transform"]="translate("+b.tX+"px,"+b.tY+"px)";c[h+"filter"]="none"}d(this).css("-webkit-transition","all "+k+"ms "+a.easingFallback).css(c)});m.each(function(){var b=this.data;b.tX=0!=b.finalPrePos.left?b.finalPrePos.left-
b.preInterPos.left:0;b.tY=0!=b.finalPrePos.left?b.finalPrePos.top-b.preInterPos.top:0;for(var c={},e=0;2>e;e++){var h=0==e?h=a.prefix:"";c[h+"transition"]="all "+k+"ms "+a.easing;c[h+"transform"]="translate("+b.tX+"px,"+b.tY+"px)"}d(this).css("-webkit-transition","all "+k+"ms "+a.easingFallback).css(c)});b={};for(c=0;2>c;c++)e=0==c?e=a.prefix:"",b[e+"transition"]="all "+k+"ms "+a.easing+", "+e+"filter "+k+"ms linear, opacity "+k+"ms linear",b[e+"transform"]=a.scale+" "+a.rotateX+" "+a.rotateY+" "+
a.rotateZ,b[e+"filter"]=a.blur+" "+a.grayscale,b.opacity=a.fade;u.css(b);n.bind("webkitTransitionEnd transitionend otransitionend oTransitionEnd",function(a){if(-1<a.originalEvent.propertyName.indexOf("transform")||-1<a.originalEvent.propertyName.indexOf("opacity"))-1<w.indexOf(".")?d(a.target).hasClass(w.replace(".",""))&&f():d(a.target).is(w)&&f()})},10);a.failsafe=setTimeout(function(){a.mixing&&f()},k+400)}else{a.resizeContainer&&n.css("height",a.origHeight+"px");if(!window.atob)return f(),!1;
u=s;setTimeout(function(){n.css(a.perspective);if(a.resizeContainer){for(var c={},e=0;2>e;e++){var d=0==e?d=a.prefix:"";c[d+"transition"]="height "+k+"ms ease-in-out";c.height=a.minHeight+"px"}n.css(c)}v.css(a.transition);if(s.length){c={};for(e=0;2>e;e++)d=0==e?d=a.prefix:"",c[d+"transform"]=a.scale+" "+a.rotateX+" "+a.rotateY+" "+a.rotateZ,c[d+"filter"]=a.blur+" "+a.grayscale,c.opacity=a.fade;u.css(c);n.bind("webkitTransitionEnd transitionend otransitionend oTransitionEnd",function(c){if(-1<c.originalEvent.propertyName.indexOf("transform")||
-1<c.originalEvent.propertyName.indexOf("opacity"))b.addClass(a.failClass),f()})}else a.mixing=!1},10)}}function x(e,c,l,b){function a(a,b){var c=isNaN(1*a.attr(e))?a.attr(e).toLowerCase():1*a.attr(e),d=isNaN(1*b.attr(e))?b.attr(e).toLowerCase():1*b.attr(e);return c<d?-1:c>d?1:0}function f(a){"asc"==c?k.prepend(a).prepend(" "):k.append(a).append(" ")}function g(a){a=a.slice();for(var b=a.length,c=b;c--;){var e=parseInt(Math.random()*b),d=a[c];a[c]=a[e];a[e]=d}return a}l.find(b.targetSelector).wrapAll('<div class="mix_sorter"/>');
var k=l.find(".mix_sorter");b.origSort.length||k.find(b.targetSelector+":visible").each(function(){d(this).wrap("<s/>");b.origSort.push(d(this).parent().html().replace(/\s+/g,""));d(this).unwrap()});k.empty();if("reset"==e)d.each(b.startOrder,function(){k.append(this).append(" ")});else if("default"==e)d.each(b.origOrder,function(){f(this)});else if("random"==e)b.newOrder.length||(b.newOrder=g(b.startOrder)),d.each(b.newOrder,function(){k.append(this).append(" ")});else if("custom"==e)d.each(c,function(){f(this)});
else{if("undefined"===typeof b.origOrder[0].attr(e))return console.log("No such attribute found. Terminating"),!1;b.newOrder.length||(d.each(b.origOrder,function(){b.newOrder.push(d(this))}),b.newOrder.sort(a));d.each(b.newOrder,function(){f(this)})}b.checkSort=[];k.find(b.targetSelector+":visible").each(function(a){var c=d(this);0==a&&c.attr("data-checksum","1");c.wrap("<s/>");b.checkSort.push(c.parent().html().replace(/\s+/g,""));c.unwrap()});l.find(b.targetSelector).unwrap()}function B(e){for(var c=
["Webkit","Moz","O","ms"],d=0;d<c.length;d++)if(c[d]+"Transition"in e.style)return c[d];return"transition"in e.style?"":!1}function A(e,c){if(e.length!=c.length)return!1;for(var d=0;d<c.length;d++)if(e[d].compare&&!e[d].compare(c[d])||e[d]!==c[d])return!1;return!0}function z(e){e=e.replace(/\s{2,}/g," ");var c=e.split(" ");d.each(c,function(d){"all"==this&&(c[d]="mix_all")});""==c[0]&&c.shift();return c}var y={init:function(e){return this.each(function(){var c=window.navigator.appVersion.match(/Chrome\/(\d+)\./),
c=c?parseInt(c[1],10):!1,l=function(a){a=document.getElementById(a);var b=a.parentElement,c=document.createElement("div"),d=document.createDocumentFragment();b.insertBefore(c,a);d.appendChild(a);b.replaceChild(a,c)};(c&&31==c||32==c)&&l(this.id);var b={targetSelector:".mix",filterSelector:".filter",sortSelector:".sort",buttonEvent:"click",effects:["fade","scale"],listEffects:null,easing:"smooth",layoutMode:"grid",targetDisplayGrid:"inline-block",targetDisplayList:"block",listClass:"",gridClass:"",
transitionSpeed:600,showOnLoad:"all",sortOnLoad:!1,multiFilter:!1,filterLogic:"or",resizeContainer:!0,minHeight:0,failClass:"fail",perspectiveDistance:"3000",perspectiveOrigin:"50% 50%",animateGridList:!0,onMixLoad:null,onMixStart:null,onMixEnd:null,container:null,origOrder:[],startOrder:[],newOrder:[],origSort:[],checkSort:[],filter:"",mixing:!1,origDisplay:"",origLayout:"",origHeight:0,newHeight:0,isTouch:!1,resetDelay:0,failsafe:null,prefix:"",easingFallback:"ease-in-out",transition:{},perspective:{},
clean:{},fade:"1",scale:"",rotateX:"",rotateY:"",rotateZ:"",blur:"",grayscale:""};e&&d.extend(b,e);this.config=b;d.support.touch="ontouchend"in document;d.support.touch&&(b.isTouch=!0,b.resetDelay=350);b.container=d(this);var a=b.container;b.prefix=B(a[0]);b.prefix=b.prefix?"-"+b.prefix.toLowerCase()+"-":"";a.find(b.targetSelector).each(function(){b.origOrder.push(d(this))});if(b.sortOnLoad){var f;d.isArray(b.sortOnLoad)?(c=b.sortOnLoad[0],f=b.sortOnLoad[1],d(b.sortSelector+"[data-sort="+b.sortOnLoad[0]+
"][data-order="+b.sortOnLoad[1]+"]").addClass("active")):(d(b.sortSelector+"[data-sort="+b.sortOnLoad+"]").addClass("active"),c=b.sortOnLoad,b.sortOnLoad="desc");x(c,f,a,b)}for(f=0;2>f;f++)c=0==f?c=b.prefix:"",b.transition[c+"transition"]="all "+b.transitionSpeed+"ms ease-in-out",b.perspective[c+"perspective"]=b.perspectiveDistance+"px",b.perspective[c+"perspective-origin"]=b.perspectiveOrigin;for(f=0;2>f;f++)c=0==f?c=b.prefix:"",b.clean[c+"transition"]="none";"list"==b.layoutMode?(a.addClass(b.listClass),
b.origDisplay=b.targetDisplayList):(a.addClass(b.gridClass),b.origDisplay=b.targetDisplayGrid);b.origLayout=b.layoutMode;f=b.showOnLoad.split(" ");d.each(f,function(){d(b.filterSelector+'[data-filter="'+this+'"]').addClass("active")});a.find(b.targetSelector).addClass("mix_all");"all"==f[0]&&(f[0]="mix_all",b.showOnLoad="mix_all");var g=d();d.each(f,function(){g=g.add(d("."+this))});g.each(function(){var a=d(this);"list"==b.layoutMode?a.css("display",b.targetDisplayList):a.css("display",b.targetDisplayGrid);
a.css(b.transition)});setTimeout(function(){b.mixing=!0;g.css("opacity","1");setTimeout(function(){"list"==b.layoutMode?g.removeStyle(b.prefix+"transition, transition").css({display:b.targetDisplayList,opacity:1}):g.removeStyle(b.prefix+"transition, transition").css({display:b.targetDisplayGrid,opacity:1});b.mixing=!1;if("function"==typeof b.onMixLoad){var a=b.onMixLoad.call(this,b);b=a?a:b}},b.transitionSpeed)},10);b.filter=b.showOnLoad;d(b.sortSelector).bind(b.buttonEvent,function(){if(!b.mixing){var c=
d(this),e=c.attr("data-sort"),f=c.attr("data-order");if(!c.hasClass("active"))d(b.sortSelector).removeClass("active"),c.addClass("active");else if("random"!=e)return!1;a.find(b.targetSelector).each(function(){b.startOrder.push(d(this))});r(b.filter,e,f,a,b)}});d(b.filterSelector).bind(b.buttonEvent,function(){if(!b.mixing){var c=d(this);if(!1==b.multiFilter)d(b.filterSelector).removeClass("active"),c.addClass("active"),b.filter=c.attr("data-filter"),d(b.filterSelector+'[data-filter="'+b.filter+'"]').addClass("active");
else{var e=c.attr("data-filter");c.hasClass("active")?(c.removeClass("active"),b.filter=b.filter.replace(RegExp("(\\s|^)"+e),"")):(c.addClass("active"),b.filter=b.filter+" "+e)}r(b.filter,null,null,a,b)}})})},toGrid:function(){return this.each(function(){var e=this.config;"grid"!=e.layoutMode&&(e.layoutMode="grid",r(e.filter,null,null,d(this),e))})},toList:function(){return this.each(function(){var e=this.config;"list"!=e.layoutMode&&(e.layoutMode="list",r(e.filter,null,null,d(this),e))})},filter:function(e){return this.each(function(){var c=
this.config;c.mixing||(d(c.filterSelector).removeClass("active"),d(c.filterSelector+'[data-filter="'+e+'"]').addClass("active"),r(e,null,null,d(this),c))})},sort:function(e){return this.each(function(){var c=this.config,l=d(this);if(!c.mixing){d(c.sortSelector).removeClass("active");if(d.isArray(e)){var b=e[0],a=e[1];d(c.sortSelector+'[data-sort="'+e[0]+'"][data-order="'+e[1]+'"]').addClass("active")}else d(c.sortSelector+'[data-sort="'+e+'"]').addClass("active"),b=e,a="desc";l.find(c.targetSelector).each(function(){c.startOrder.push(d(this))});
r(c.filter,b,a,l,c)}})},multimix:function(e){return this.each(function(){var c=this.config,l=d(this);multiOut={filter:c.filter,sort:null,order:"desc",layoutMode:c.layoutMode};d.extend(multiOut,e);c.mixing||(d(c.filterSelector).add(c.sortSelector).removeClass("active"),d(c.filterSelector+'[data-filter="'+multiOut.filter+'"]').addClass("active"),"undefined"!==typeof multiOut.sort&&(d(c.sortSelector+'[data-sort="'+multiOut.sort+'"][data-order="'+multiOut.order+'"]').addClass("active"),l.find(c.targetSelector).each(function(){c.startOrder.push(d(this))})),
c.layoutMode=multiOut.layoutMode,r(multiOut.filter,multiOut.sort,multiOut.order,l,c))})},remix:function(e){return this.each(function(){var c=this.config,l=d(this);c.origOrder=[];l.find(c.targetSelector).each(function(){var b=d(this);b.addClass("mix_all");c.origOrder.push(b)});c.mixing||"undefined"===typeof e||(d(c.filterSelector).removeClass("active"),d(c.filterSelector+'[data-filter="'+e+'"]').addClass("active"),r(e,null,null,l,c))})}};d.fn.mixitup=function(d,c){if(y[d])return y[d].apply(this,Array.prototype.slice.call(arguments,
1));if("object"===typeof d||!d)return y.init.apply(this,arguments)};d.fn.removeStyle=function(e){return this.each(function(){var c=d(this);e=e.replace(/\s+/g,"");var l=e.split(",");d.each(l,function(){var b=RegExp(this.toString()+"[^;]+;?","g");c.attr("style",function(a,c){if(c)return c.replace(b,"")})})})}})(jQuery);
Non-commercial use permitted under terms of CC-BY-NC license.
http://creativecommons.org/licenses/by-nc/3.0/
*/
(function(f,l){f.MixItUp=function(){this._execAction("_constructor",0);f.extend(this,{selectors:{target:".mix",filter:".filter",sort:".sort"},animation:{enable:!0,effects:"fade scale",duration:600,easing:"ease",perspectiveDistance:"3000",perspectiveOrigin:"50% 50%",queue:!0,queueLimit:1,animateChangeLayout:!1,animateResizeContainer:!0,animateResizeTargets:!1,staggerSequence:!1,reverseOut:!1},callbacks:{onMixLoad:!1,onMixStart:!1,onMixBusy:!1,onMixEnd:!1,onMixFail:!1,_user:!1},controls:{enable:!0,
live:!1,toggleFilterButtons:!1,toggleLogic:"or",activeClass:"active"},layout:{display:"inline-block",containerClass:"",containerClassFail:"fail"},load:{filter:"all",sort:!1},_$body:null,_$container:null,_$targets:null,_$parent:null,_$sortButtons:null,_$filterButtons:null,_suckMode:!1,_mixing:!1,_sorting:!1,_clicking:!1,_loading:!0,_changingLayout:!1,_changingClass:!1,_changingDisplay:!1,_origOrder:[],_startOrder:[],_newOrder:[],_activeFilter:null,_toggleArray:[],_toggleString:"",_activeSort:"default:asc",
_newSort:null,_startHeight:null,_newHeight:null,_incPadding:!0,_newDisplay:null,_newClass:null,_targetsBound:0,_targetsDone:0,_queue:[],_$show:f(),_$hide:f()});this._execAction("_constructor",1)};f.MixItUp.prototype={constructor:f.MixItUp,_instances:{},_handled:{_filter:{},_sort:{}},_bound:{_filter:{},_sort:{}},_actions:{},_filters:{},extend:function(a){for(var c in a)f.MixItUp.prototype[c]=a[c]},addAction:function(a,c,b,d){f.MixItUp.prototype._addHook("_actions",a,c,b,d)},addFilter:function(a,c,
b,d){f.MixItUp.prototype._addHook("_filters",a,c,b,d)},_addHook:function(a,c,b,d,e){a=f.MixItUp.prototype[a];var g={};e=1===e||"post"===e?"post":"pre";g[c]={};g[c][e]={};g[c][e][b]=d;f.extend(!0,a,g)},_init:function(a,c){this._execAction("_init",0,arguments);c&&f.extend(!0,this,c);this._$body=f("body");this._domNode=a;this._$container=f(a);this._$container.addClass(this.layout.containerClass);this._id=a.id;this._platformDetect();this._brake=this._getPrefixedCSS("transition","none");this._refresh(!0);
this._$parent=this._$targets.parent().length?this._$targets.parent():this._$container;this.load.sort&&(this._newSort=this._parseSort(this.load.sort),this._activeSort=this._newSortString=this.load.sort,this._sort(),this._printSort());this._activeFilter="all"===this.load.filter?this.selectors.target:"none"===this.load.filter?"":this.load.filter;this.controls.enable&&this._bindHandlers();if(this.controls.toggleFilterButtons){this._buildToggleArray();for(var b=0;b<this._toggleArray.length;b++)this._updateControls({filter:this._toggleArray[b],
sort:this._activeSort},!0)}else this.controls.enable&&this._updateControls({filter:this._activeFilter,sort:this._activeSort});this._filter();this._init=!0;this._$container.data("mixItUp",this);this._execAction("_init",1,arguments);this._buildState();this._$targets.css(this._brake);this._goMix(this.animation.enable)},_platformDetect:function(){var a=["Webkit","Moz","O","ms"],c=["webkit","moz"],b=window.navigator.appVersion.match(/Chrome\/(\d+)\./)||!1,d="undefined"!==typeof InstallTrigger,e=function(b){for(var c=
0;c<a.length;c++)if(a[c]+"Transition"in b.style)return{prefix:"-"+a[c].toLowerCase()+"-",vendor:a[c]};return"transition"in b.style?"":!1}(this._domNode);this._execAction("_platformDetect",0);this._chrome=b?parseInt(b[1],10):!1;this._ff=d?parseInt(window.navigator.userAgent.match(/rv:([^)]+)\)/)[1]):!1;this._prefix=e.prefix;this._vendor=e.vendor;(this._suckMode=window.atob&&this._prefix?!1:!0)&&(this.animation.enable=!1);this._ff&&4>=this._ff&&(this.animation.enable=!1);for(b=0;b<c.length&&!window.requestAnimationFrame;b++)window.requestAnimationFrame=
window[c[b]+"RequestAnimationFrame"];"function"!==typeof Object.getPrototypeOf&&(Object.getPrototypeOf="object"===typeof"test".__proto__?function(a){return a.__proto__}:function(a){return a.constructor.prototype});this._domNode.nextElementSibling===l&&Object.defineProperty(Element.prototype,"nextElementSibling",{get:function(){for(var a=this.nextSibling;a;){if(1===a.nodeType)return a;a=a.nextSibling}return null}});this._execAction("_platformDetect",1)},_refresh:function(a,c){this._execAction("_refresh",
0,arguments);this._$targets=this._$container.find(this.selectors.target);for(var b=0;b<this._$targets.length;b++){var d=this._$targets[b];if(d.dataset===l||c){d.dataset={};for(var e=0;e<d.attributes.length;e++){var g=d.attributes[e],k=g.name,g=g.value;-1<k.indexOf("data-")&&(k=this._helpers._camelCase(k.substring(5,k.length)),d.dataset[k]=g)}}d.mixParent===l&&(d.mixParent=this._id)}if(this._$targets.length&&a||!this._origOrder.length&&this._$targets.length)for(this._origOrder=[],b=0;b<this._$targets.length;b++)d=
this._$targets[b],this._origOrder.push(d);this._execAction("_refresh",1,arguments)},_bindHandlers:function(){var a=this,c=f.MixItUp.prototype._bound._filter,b=f.MixItUp.prototype._bound._sort;a._execAction("_bindHandlers",0);if(a.controls.live)a._$body.on("click.mixItUp."+a._id,a.selectors.sort,function(){a._processClick(f(this),"sort")}).on("click.mixItUp."+a._id,a.selectors.filter,function(){a._processClick(f(this),"filter")});else a._$sortButtons=f(a.selectors.sort),a._$filterButtons=f(a.selectors.filter),
a._$sortButtons.on("click.mixItUp."+a._id,function(){a._processClick(f(this),"sort")}),a._$filterButtons.on("click.mixItUp."+a._id,function(){a._processClick(f(this),"filter")});c[a.selectors.filter]=c[a.selectors.filter]===l?1:c[a.selectors.filter]+1;b[a.selectors.sort]=b[a.selectors.sort]===l?1:b[a.selectors.sort]+1;a._execAction("_bindHandlers",1)},_processClick:function(a,c){var b=this,d=function(a,c,d){var e=f.MixItUp.prototype;e._handled["_"+c][b.selectors[c]]=e._handled["_"+c][b.selectors[c]]===
l?1:e._handled["_"+c][b.selectors[c]]+1;e._handled["_"+c][b.selectors[c]]===e._bound["_"+c][b.selectors[c]]&&(a[(d?"remove":"add")+"Class"](b.controls.activeClass),delete e._handled["_"+c][b.selectors[c]])};b._execAction("_processClick",0,arguments);if(!b._mixing||b.animation.queue&&b._queue.length<b.animation.queueLimit){b._clicking=!0;if("sort"===c){var e=a.attr("data-sort");if(!a.hasClass(b.controls.activeClass)||-1<e.indexOf("random"))f(b.selectors.sort).removeClass(b.controls.activeClass),d(a,
c),b.sort(e)}if("filter"===c){var g=a.attr("data-filter"),e="or"===b.controls.toggleLogic?",":"";b.controls.toggleFilterButtons?(b._buildToggleArray(),a.hasClass(b.controls.activeClass)?(d(a,c,!0),d=b._toggleArray.indexOf(g),b._toggleArray.splice(d,1)):(d(a,c),b._toggleArray.push(g)),b._toggleArray=f.grep(b._toggleArray,function(a){return a}),b._toggleString=b._toggleArray.join(e),b.filter(b._toggleString)):a.hasClass(b.controls.activeClass)||(f(b.selectors.filter).removeClass(b.controls.activeClass),
d(a,c),b.filter(g))}b._execAction("_processClick",1,arguments)}else"function"===typeof b.callbacks.onMixBusy&&b.callbacks.onMixBusy.call(b._domNode,b._state,b),b._execAction("_processClickBusy",1,arguments)},_buildToggleArray:function(){var a=this._activeFilter.replace(/\s/g,"");this._execAction("_buildToggleArray",0,arguments);if("or"===this.controls.toggleLogic)this._toggleArray=a.split(",");else{this._toggleArray=a.split(".");!this._toggleArray[0]&&this._toggleArray.shift();for(var a=0,c;c=this._toggleArray[a];a++)this._toggleArray[a]=
"."+c}this._execAction("_buildToggleArray",1,arguments)},_updateControls:function(a,c){var b={filter:a.filter,sort:a.sort},d="filter",e=null;this._execAction("_updateControls",0,arguments);a.filter===l&&(b.filter=this._activeFilter);a.sort===l&&(b.sort=this._activeSort);b.filter===this.selectors.target&&(b.filter="all");for(var g=0;2>g;g++){if(e=this.controls.live?f(this.selectors[d]):this["_$"+d+"Buttons"]){var k="[data-"+d+'="'+b[d]+'"]';c&&"filter"===d&&"none"!==b.filter&&""!==b.filter?e.filter(k).addClass(this.controls.activeClass):
e.removeClass(this.controls.activeClass).filter(k).addClass(this.controls.activeClass)}d="sort"}this._execAction("_updateControls",1,arguments)},_filter:function(){this._execAction("_filter",0);for(var a=0;a<this._$targets.length;a++){var c=f(this._$targets[a]);c.is(this._activeFilter)?this._$show=this._$show.add(c):this._$hide=this._$hide.add(c)}this._execAction("_filter",1)},_sort:function(){var a=this,c=function(a){a=a.slice();for(var b=a.length,c=b;c--;){var f=parseInt(Math.random()*b),h=a[c];
a[c]=a[f];a[f]=h}return a};a._execAction("_sort",0);a._startOrder=[];for(var b=0;b<a._$targets.length;b++)a._startOrder.push(a._$targets[b]);switch(a._newSort[0].sortBy){case "default":a._newOrder=a._origOrder;break;case "random":a._newOrder=c(a._startOrder);break;case "custom":a._newOrder=a._newSort[0].order;break;default:a._newOrder=a._startOrder.concat().sort(function(b,c){return a._compare(b,c)})}a._execAction("_sort",1)},_compare:function(a,c,b){b=b?b:0;var d=this,e=d._newSort[b].order,g=function(a){return a.dataset[d._newSort[b].sortBy]||
0},f=isNaN(1*g(a))?g(a).toLowerCase():1*g(a),g=isNaN(1*g(c))?g(c).toLowerCase():1*g(c);return f<g?"asc"===e?-1:1:f>g?"asc"===e?1:-1:f===g&&d._newSort.length>b+1?d._compare(a,c,b+1):0},_printSort:function(a){var c=a?this._startOrder:this._newOrder,b=this._$parent[0].querySelectorAll(this.selectors.target),d=b.length?b[b.length-1].nextElementSibling:null,e=document.createDocumentFragment();this._execAction("_printSort",0,arguments);for(var g=0;g<b.length;g++){var f=b[g],h=f.nextSibling;"absolute"!==
f.style.position&&(h&&"#text"===h.nodeName&&this._$parent[0].removeChild(h),this._$parent[0].removeChild(f))}for(g=0;g<c.length;g++)b=c[g],"default"!==this._newSort[0].sortBy||"desc"!==this._newSort[0].order||a?(e.appendChild(b),e.appendChild(document.createTextNode(" "))):(e.insertBefore(b,e.firstChild),e.insertBefore(document.createTextNode(" "),b));d?this._$parent[0].insertBefore(e,d):this._$parent[0].appendChild(e);this._execAction("_printSort",1,arguments)},_parseSort:function(a){for(var c="string"===
typeof a?a.split(" "):[a],b=[],d=0;d<c.length;d++){var e="string"===typeof a?c[d].split(":"):["custom",c[d]],e={sortBy:this._helpers._camelCase(e[0]),order:e[1]||"asc"};b.push(e);if("default"===e.sortBy||"random"===e.sortBy)break}return this._execFilter("_parseSort",b,arguments)},_parseEffects:function(){var a=this,c={opacity:"",transformIn:"",transformOut:"",filter:""},b=function(b,c,d){return-1<a.animation.effects.indexOf(b)?c&&(b=a.animation.effects.indexOf(b+"("),-1<b)?(b=a.animation.effects.substring(b),
{val:/\(([^)]+)\)/.exec(b)[1]}):!0:!1},d=function(a,d){for(var f=[["scale",".01"],["translateX","20px"],["translateY","20px"],["translateZ","20px"],["rotateX","90deg"],["rotateY","90deg"],["rotateZ","180deg"]],h=0;h<f.length;h++){var m=f[h][0],l=f[h][1],p=d&&"scale"!==m,q=c[a],n;b(m)?(n=m+"(",m=b(m,!0).val||l,p=p?"-"===m.charAt(0)?m.substr(1,m.length):"-"+m:m,n=n+p+") "):n="";c[a]=q+n}};c.opacity=b("fade")?b("fade",!0).val||"0":"1";d("transformIn");a.animation.reverseOut?d("transformOut",!0):c.transformOut=
c.transformIn;c.transition={};c.transition=a._getPrefixedCSS("transition","all "+a.animation.duration+"ms "+a.animation.easing+", opacity "+a.animation.duration+"ms linear");a.animation.stagger=b("stagger")?!0:!1;a.animation.staggerDuration=parseInt(b("stagger")?b("stagger",!0).val?b("stagger",!0).val:100:100);return a._execFilter("_parseEffects",c)},_buildState:function(a){var c={};this._execAction("_buildState",0);c={activeFilter:""===this._activeFilter?"none":this._activeFilter,activeSort:a&&this._newSortString?
this._newSortString:this._activeSort,fail:!this._$show.length&&""!==this._activeFilter,$targets:this._$targets,$show:this._$show,$hide:this._$hide,totalTargets:this._$targets.length,totalShow:this._$show.length,totalHide:this._$hide.length,display:a&&this._newDisplay?this._newDisplay:this.layout.display};if(a)return this._execFilter("_buildState",c);this._state=c;this._execAction("_buildState",1)},_goMix:function(a){var c=this,b=function(){if(c._chrome&&31===c._chrome){var a=c._$parent[0],b=a.parentElement,
e=document.createElement("div"),f=document.createDocumentFragment();b.insertBefore(e,a);f.appendChild(a);b.replaceChild(a,e)}c._setInter();d()},d=function(){var a=window.pageYOffset,b=window.pageXOffset;c._getInterMixData();c._setFinal();c._getFinalMixData();window.pageYOffset!==a&&window.scrollTo(b,a);c._prepTargets();window.requestAnimationFrame?requestAnimationFrame(e):setTimeout(function(){e()},20)},e=function(){c._animateTargets();0===c._targetsBound&&c._cleanUp()},f=c._buildState(!0);c._execAction("_goMix",
0,arguments);!c.animation.duration&&(a=!1);c._mixing=!0;c._$container.removeClass(c.layout.containerClassFail);"function"===typeof c.callbacks.onMixStart&&c.callbacks.onMixStart.call(c._domNode,c._state,f,c);c._$container.trigger("mixStart",[c._state,f,c]);c._getOrigMixData();a&&!c._suckMode?window.requestAnimationFrame?requestAnimationFrame(b):b():c._cleanUp();c._execAction("_goMix",1,arguments)},_getTargetData:function(a,c){var b;a.dataset[c+"PosX"]=a.offsetLeft;a.dataset[c+"PosY"]=a.offsetTop;
this.animation.animateResizeTargets&&(b=window.getComputedStyle(a),a.dataset[c+"MarginBottom"]=parseInt(b.marginBottom),a.dataset[c+"MarginRight"]=parseInt(b.marginRight),a.dataset[c+"Width"]=a.offsetWidth,a.dataset[c+"Height"]=a.offsetHeight)},_getOrigMixData:function(){var a=this._suckMode?{boxSizing:""}:window.getComputedStyle(this._$parent[0]);this._incPadding="border-box"===(a.boxSizing||a[this._vendor+"BoxSizing"]);this._execAction("_getOrigMixData",0);!this._suckMode&&(this.effects=this._parseEffects());
this._$toHide=this._$hide.filter(":visible");this._$toShow=this._$show.filter(":hidden");this._$pre=this._$targets.filter(":visible");this._startHeight=this._incPadding?this._$parent.outerHeight():this._$parent.height();for(a=0;a<this._$pre.length;a++)this._getTargetData(this._$pre[a],"orig");this._execAction("_getOrigMixData",1)},_setInter:function(){this._execAction("_setInter",0);this._changingLayout&&this.animation.animateChangeLayout?(this._$toShow.css("display",this._newDisplay),this._changingClass&&
this._$container.removeClass(this.layout.containerClass).addClass(this._newClass)):this._$toShow.css("display",this.layout.display);this._execAction("_setInter",1)},_getInterMixData:function(){this._execAction("_getInterMixData",0);for(var a=0;a<this._$toShow.length;a++){var c=this._$toShow[a];this._getTargetData(c,"inter")}for(a=0;a<this._$pre.length;a++)c=this._$pre[a],this._getTargetData(c,"inter");this._execAction("_getInterMixData",1)},_setFinal:function(){this._execAction("_setFinal",0);this._sorting&&
this._printSort();this._$toHide.removeStyle("display");this._changingLayout&&this.animation.animateChangeLayout&&this._$pre.css("display",this._newDisplay);this._execAction("_setFinal",1)},_getFinalMixData:function(){this._execAction("_getFinalMixData",0);for(var a=0;a<this._$toShow.length;a++){var c=this._$toShow[a];this._getTargetData(c,"final")}for(a=0;a<this._$pre.length;a++)c=this._$pre[a],this._getTargetData(c,"final");this._newHeight=this._incPadding?this._$parent.outerHeight():this._$parent.height();
this._sorting&&this._printSort(!0);this._$toShow.removeStyle("display");this._$pre.css("display",this.layout.display);this._changingClass&&this.animation.animateChangeLayout&&this._$container.removeClass(this._newClass).addClass(this.layout.containerClass);this._execAction("_getFinalMixData",1)},_prepTargets:function(){var a={_in:this._getPrefixedCSS("transform",this.effects.transformIn),_out:this._getPrefixedCSS("transform",this.effects.transformOut)};this._execAction("_prepTargets",0);this.animation.animateResizeContainer&&
this._$parent.css("height",this._startHeight+"px");for(var c=0;c<this._$toShow.length;c++){var b=this._$toShow[c],d=f(b);b.style.opacity=this.effects.opacity;b.style.display=this._changingLayout&&this.animation.animateChangeLayout?this._newDisplay:this.layout.display;d.css(a._in);this.animation.animateResizeTargets&&(b.style.width=b.dataset.finalWidth+"px",b.style.height=b.dataset.finalHeight+"px",b.style.marginRight=-(b.dataset.finalWidth-b.dataset.interWidth)+1*b.dataset.finalMarginRight+"px",b.style.marginBottom=
-(b.dataset.finalHeight-b.dataset.interHeight)+1*b.dataset.finalMarginBottom+"px")}for(c=0;c<this._$pre.length;c++)b=this._$pre[c],d=f(b),a=this._getPrefixedCSS("transform","translate("+(b.dataset.origPosX-b.dataset.interPosX)+"px,"+(b.dataset.origPosY-b.dataset.interPosY)+"px)"),d.css(a),this.animation.animateResizeTargets&&(b.style.width=b.dataset.origWidth+"px",b.style.height=b.dataset.origHeight+"px",b.dataset.origWidth-b.dataset.finalWidth&&(b.style.marginRight=-(b.dataset.origWidth-b.dataset.interWidth)+
1*b.dataset.origMarginRight+"px"),b.dataset.origHeight-b.dataset.finalHeight&&(b.style.marginBottom=-(b.dataset.origHeight-b.dataset.interHeight)+1*b.dataset.origMarginBottom+"px"));this._execAction("_prepTargets",1)},_animateTargets:function(){var a,c;this._execAction("_animateTargets",0);this._targetsBound=this._targetsDone=0;this._$parent.css(this._getPrefixedCSS("perspective",this.animation.perspectiveDistance+"px")).css(this._getPrefixedCSS("perspective-origin",this.animation.perspectiveOrigin));
this.animation.animateResizeContainer&&this._$parent.css(this._getPrefixedCSS("transition","height "+this.animation.duration+"ms ease")).css("height",this._newHeight+"px");for(var b=0;b<this._$toShow.length;b++){var d=this._$toShow[b],e=f(d);a=d.dataset.finalPosX-d.dataset.interPosX;c=d.dataset.finalPosY-d.dataset.interPosY;var g=this._getDelay(b),k={};d.style.opacity="";for(d=0;2>d;d++){var h=0===d?h=this._prefix:"";this._ff&&20>=this._ff&&(k[h+"transition-property"]="all",k[h+"transition-timing-function"]=
this.animation.easing+"ms",k[h+"transition-duration"]=this.animation.duration+"ms");k[h+"transition-delay"]=g+"ms";k[h+"transform"]="translate("+a+"px,"+c+"px)"}(this.effects.transform||this.effects.opacity)&&this._bindTargetDone(e);this._ff&&20>=this._ff?e.css(k):e.css(this.effects.transition).css(k)}for(b=0;b<this._$pre.length;b++)d=this._$pre[b],e=f(d),a=d.dataset.finalPosX-d.dataset.interPosX,c=d.dataset.finalPosY-d.dataset.interPosY,g=this._getDelay(b),d.dataset.finalPosX===d.dataset.origPosX&&
d.dataset.finalPosY===d.dataset.origPosY||this._bindTargetDone(e),e.css(this._getPrefixedCSS("transition","all "+this.animation.duration+"ms "+this.animation.easing+" "+g+"ms")),e.css(this._getPrefixedCSS("transform","translate("+a+"px,"+c+"px)")),this.animation.animateResizeTargets&&(d.dataset.origWidth-d.dataset.finalWidth&&1*d.dataset.finalWidth&&(d.style.width=d.dataset.finalWidth+"px",d.style.marginRight=-(d.dataset.finalWidth-d.dataset.interWidth)+1*d.dataset.finalMarginRight+"px"),d.dataset.origHeight-
d.dataset.finalHeight&&1*d.dataset.finalHeight&&(d.style.height=d.dataset.finalHeight+"px",d.style.marginBottom=-(d.dataset.finalHeight-d.dataset.interHeight)+1*d.dataset.finalMarginBottom+"px"));this._changingClass&&this._$container.removeClass(this.layout.containerClass).addClass(this._newClass);for(b=0;b<this._$toHide.length;b++){d=this._$toHide[b];e=f(d);g=this._getDelay(b);a={};for(d=0;2>d;d++)h=0===d?h=this._prefix:"",a[h+"transition-delay"]=g+"ms",a[h+"transform"]=this.effects.transformOut,
a.opacity=this.effects.opacity;e.css(this.effects.transition).css(a);(this.effects.transform||this.effects.opacity)&&this._bindTargetDone(e)}this._execAction("_animateTargets",1)},_bindTargetDone:function(a){var c=this,b=a[0];c._execAction("_bindTargetDone",0,arguments);b.dataset.bound||(b.dataset.bound=!0,c._targetsBound++,a.on("webkitTransitionEnd.mixItUp transitionend.mixItUp",function(d){(-1<d.originalEvent.propertyName.indexOf("transform")||-1<d.originalEvent.propertyName.indexOf("opacity"))&&
f(d.originalEvent.target).is(c.selectors.target)&&(a.off(".mixItUp"),delete b.dataset.bound,c._targetDone())}));c._execAction("_bindTargetDone",1,arguments)},_targetDone:function(){this._execAction("_targetDone",0);this._targetsDone++;this._targetsDone===this._targetsBound&&this._cleanUp();this._execAction("_targetDone",1)},_cleanUp:function(){var a=this,c=a.animation.animateResizeTargets?"transform opacity width height margin-bottom margin-right":"transform opacity";unBrake=function(){a._$targets.removeStyle("transition",
a._prefix)};a._execAction("_cleanUp",0);a._changingLayout?a._$show.css("display",a._newDisplay):a._$show.css("display",a.layout.display);a._$targets.css(a._brake);a._$targets.removeStyle(c,a._prefix).removeAttr("data-inter-pos-x data-inter-pos-y data-final-pos-x data-final-pos-y data-orig-pos-x data-orig-pos-y data-orig-height data-orig-width data-final-height data-final-width data-inter-width data-inter-height data-orig-margin-right data-orig-margin-bottom data-inter-margin-right data-inter-margin-bottom data-final-margin-right data-final-margin-bottom");
a._$hide.removeStyle("display");a._$parent.removeStyle("height transition perspective-distance perspective perspective-origin-x perspective-origin-y perspective-origin perspectiveOrigin",a._prefix);a._sorting&&(a._printSort(),a._activeSort=a._newSortString,a._sorting=!1);a._changingLayout&&(a._changingDisplay&&(a.layout.display=a._newDisplay,a._changingDisplay=!1),a._changingClass&&(a._$parent.removeClass(a.layout.containerClass).addClass(a._newClass),a.layout.containerClass=a._newClass,a._changingClass=
!1),a._changingLayout=!1);a._refresh();a._buildState();a._state.fail&&a._$container.addClass(a.layout.containerClassFail);a._$show=f();a._$hide=f();window.requestAnimationFrame&&requestAnimationFrame(unBrake);a._mixing=!1;"function"===typeof a.callbacks._user&&a.callbacks._user.call(a._domNode,a._state,a);"function"===typeof a.callbacks.onMixEnd&&a.callbacks.onMixEnd.call(a._domNode,a._state,a);a._$container.trigger("mixEnd",[a._state,a]);a._state.fail&&("function"===typeof a.callbacks.onMixFail&&
a.callbacks.onMixFail.call(a._domNode,a._state,a),a._$container.trigger("mixFail",[a._state,a]));a._loading&&("function"===typeof a.callbacks.onMixLoad&&a.callbacks.onMixLoad.call(a._domNode,a._state,a),a._$container.trigger("mixLoad",[a._state,a]));a._queue.length&&(a._execAction("_queue",0),a.multiMix(a._queue[0][0],a._queue[0][1],a._queue[0][2]),a._queue.splice(0,1));a._execAction("_cleanUp",1);a._loading=!1},_getPrefixedCSS:function(a,c,b){var d={};for(i=0;2>i;i++){var e=0===i?this._prefix:"";
b?d[e+a]=e+c:d[e+a]=c}return this._execFilter("_getPrefixedCSS",d,arguments)},_getDelay:function(a){var c="function"===typeof this.animation.staggerSequence?this.animation.staggerSequence.call(this._domNode,a,this._state):a;return this._execFilter("_getDelay",this.animation.stagger?c*this.animation.staggerDuration:0,arguments)},_parseMultiMixArgs:function(a){for(var c={command:null,animate:this.animation.enable,callback:null},b=0;b<a.length;b++){var d=a[b];null!==d&&("object"===typeof d||"string"===
typeof d?c.command=d:"boolean"===typeof d?c.animate=d:"function"===typeof d&&(c.callback=d))}return this._execFilter("_parseMultiMixArgs",c,arguments)},_parseInsertArgs:function(a){for(var c={index:0,$object:f(),multiMix:{filter:this._state.activeFilter},callback:null},b=0;b<a.length;b++){var d=a[b];"number"===typeof d?c.index=d:"object"===typeof d&&d instanceof f?c.$object=d:"object"===typeof d&&this._helpers._isElement(d)?c.$object=f(d):"object"===typeof d&&null!==d?c.multiMix=d:"boolean"!==typeof d||
d?"function"===typeof d&&(c.callback=d):c.multiMix=!1}return this._execFilter("_parseInsertArgs",c,arguments)},_execAction:function(a,c,b){c=c?"post":"pre";if(!this._actions.isEmptyObject&&this._actions.hasOwnProperty(a))for(var d in this._actions[a][c])this._actions[a][c][d].call(this,b)},_execFilter:function(a,c,b){if(!this._filters.isEmptyObject&&this._filters.hasOwnProperty(a))for(var d in this._filters[a])return this._filters[a][d].call(this,b);else return c},_helpers:{_camelCase:function(a){return a.replace(/-([a-z])/g,
function(a){return a[1].toUpperCase()})},_isElement:function(a){return window.HTMLElement?a instanceof HTMLElement:null!==a&&1===a.nodeType&&"string"===a.nodeName}},isMixing:function(){return this._execFilter("isMixing",this._mixing)},filter:function(){var a=this._parseMultiMixArgs(arguments);this._clicking&&(this._toggleString="");this.multiMix({filter:a.command},a.animate,a.callback)},sort:function(){var a=this._parseMultiMixArgs(arguments);this.multiMix({sort:a.command},a.animate,a.callback)},
changeLayout:function(){var a=this._parseMultiMixArgs(arguments);this.multiMix({changeLayout:a.command},a.animate,a.callback)},multiMix:function(){var a=this._parseMultiMixArgs(arguments);this._execAction("multiMix",0,arguments);if(this._mixing)this.animation.queue&&this._queue.length<this.animation.queueLimit?(this._queue.push(arguments),this.controls.enable&&!this._clicking&&this._updateControls(a.command),this._execAction("multiMixQueue",1,arguments)):("function"===typeof this.callbacks.onMixBusy&&
this.callbacks.onMixBusy.call(this._domNode,this._state,this),this._$container.trigger("mixBusy",[this._state,this]),this._execAction("multiMixBusy",1,arguments));else{this.controls.enable&&!this._clicking&&(this.controls.toggleFilterButtons&&this._buildToggleArray(),this._updateControls(a.command,this.controls.toggleFilterButtons));2>this._queue.length&&(this._clicking=!1);delete this.callbacks._user;a.callback&&(this.callbacks._user=a.callback);var c=a.command.sort,b=a.command.filter,d=a.command.changeLayout;
this._refresh();c&&(this._newSort=this._parseSort(c),this._newSortString=c,this._sorting=!0,this._sort());b!==l&&(this._activeFilter=b="all"===b?this.selectors.target:b);this._filter();d&&(this._newDisplay="string"===typeof d?d:d.display||this.layout.display,this._newClass=d.containerClass||"",this._newDisplay!==this.layout.display||this._newClass!==this.layout.containerClass)&&(this._changingLayout=!0,this._changingClass=this._newClass!==this.layout.containerClass,this._changingDisplay=this._newDisplay!==
this.layout.display);this._$targets.css(this._brake);this._goMix(a.animate^this.animation.enable?a.animate:this.animation.enable);this._execAction("multiMix",1,arguments)}},insert:function(){var a=this._parseInsertArgs(arguments),c="function"===typeof a.callback?a.callback:null,b=document.createDocumentFragment(),d;this._refresh();d=this._$targets.length?a.index<this._$targets.length||!this._$targets.length?this._$targets[a.index]:this._$targets[this._$targets.length-1].nextElementSibling:this._$parent[0].children[0];
this._execAction("insert",0,arguments);if(a.$object){for(var e=0;e<a.$object.length;e++)b.appendChild(a.$object[e]),b.appendChild(document.createTextNode(" "));this._$parent[0].insertBefore(b,d)}this._execAction("insert",1,arguments);"object"===typeof a.multiMix&&this.multiMix(a.multiMix,c)},prepend:function(){var a=this._parseInsertArgs(arguments);this.insert(0,a.$object,a.multiMix,a.callback)},append:function(){var a=this._parseInsertArgs(arguments);this.insert(this._state.totalTargets,a.$object,
a.multiMix,a.callback)},getOption:function(a){var c=function(a,c){for(var e=c.split("."),f=e.pop(),k=e.length,h=1,m=e[0]||c;(a=a[m])&&h<k;)m=e[h],h++;if(a!==l)return a[f]!==l?a[f]:a};return a?this._execFilter("getOption",c(this,a),arguments):this},setOptions:function(a){this._execAction("setOptions",0,arguments);"object"===typeof a&&f.extend(!0,this,a);this._execAction("setOptions",1,arguments)},getState:function(){return this._execFilter("getState",this._state,this)},forceRefresh:function(){this._refresh(!1,
!0)},destroy:function(a){this._execAction("destroy",0,arguments);this._$body.add(f(this.selectors.sort)).add(f(this.selectors.filter)).off(".mixItUp");for(var c=0;c<this._$targets.length;c++){var b=this._$targets[c];a&&(b.style.display="");delete b.mixParent}this._execAction("destroy",1,arguments);delete f.MixItUp.prototype._instances[this._id]}};f.fn.mixItUp=function(){var a=arguments,c=[],b,d=function(a,b){var c=new f.MixItUp;c._execAction("_instantiate",0,arguments);a.id=a.id?a.id:"MixItUp"+("00000"+
(16777216*Math.random()<<0).toString(16)).substr(-6).toUpperCase();c._instances[a.id]||(c._instances[a.id]=c,c._init(a,b));c._execAction("_instantiate",1,arguments)};b=this.each(function(){if(a&&"string"===typeof a[0]){var b=f.MixItUp.prototype._instances[this.id];"isLoaded"===a[0]?c.push(b?!0:!1):(b=b[a[0]](a[1],a[2],a[3]),b!==l&&c.push(b))}else d(this,a[0])});return c.length?1<c.length?c:c[0]:b};f.fn.removeStyle=function(a,c){c=c?c:"";return this.each(function(){for(var b=a.split(" "),d=0;d<b.length;d++)for(var e=
0;4>e;e++){switch(e){case 0:var g=b[d];break;case 1:g=f.MixItUp.prototype._helpers._camelCase(g);break;case 2:g=c+b[d];break;case 3:g=f.MixItUp.prototype._helpers._camelCase(c+b[d])}this.style[g]!==l&&"unknown"!==typeof this.style[g]&&0<this.style[g].length&&(this.style[g]="");if(!c&&1===e)break}this.attributes&&this.attributes.style&&this.attributes.style!==l&&""===this.attributes.style.value&&this.attributes.removeNamedItem("style")})}})(jQuery);

80
js/local-time.js

@ -13,7 +13,9 @@
*
*/
onready(function(){
$(document).ready(function(){
'use strict';
var iso8601 = function(s) {
s = s.replace(/\.\d\d\d+/,""); // remove milliseconds
s = s.replace(/-/,"/").replace(/-/,"/");
@ -36,28 +38,78 @@ onready(function(){
return strftime(window.post_date, t, datelocale);
};
function timeDifference(current, previous) {
var msPerMinute = 60 * 1000;
var msPerHour = msPerMinute * 60;
var msPerDay = msPerHour * 24;
var msPerMonth = msPerDay * 30;
var msPerYear = msPerDay * 365;
var elapsed = current - previous;
if (elapsed < msPerMinute) {
return 'Just now';
} else if (elapsed < msPerHour) {
return Math.round(elapsed/msPerMinute) + (Math.round(elapsed/msPerMinute)<=1 ? ' minute ago':' minutes ago');
} else if (elapsed < msPerDay ) {
return Math.round(elapsed/msPerHour ) + (Math.round(elapsed/msPerHour)<=1 ? ' hour ago':' hours ago');
} else if (elapsed < msPerMonth) {
return Math.round(elapsed/msPerDay) + (Math.round(elapsed/msPerDay)<=1 ? ' day ago':' days ago');
} else if (elapsed < msPerYear) {
return Math.round(elapsed/msPerMonth) + (Math.round(elapsed/msPerMonth)<=1 ? ' month ago':' months ago');
} else {
return Math.round(elapsed/msPerYear ) + (Math.round(elapsed/msPerYear)<=1 ? ' year ago':' years ago');
}
}
var do_localtime = function(elem) {
var times = elem.getElementsByTagName('time');
var currentTime = Date.now();
for(var i = 0; i < times.length; i++) {
if(typeof times[i].getAttribute('data-local') == 'undefined')
continue;
var t = iso8601(times[i].getAttribute('datetime'));
var t = times[i].getAttribute('datetime');
var postTime = new Date(t);
times[i].setAttribute('data-local', 'true');
times[i].innerHTML = dateformat(t);
};
if (!localStorage.show_relative_time || localStorage.show_relative_time === 'false') {
times[i].innerHTML = dateformat(iso8601(t));
times[i].setAttribute('title', timeDifference(currentTime, postTime.getTime()));
} else {
times[i].innerHTML = timeDifference(currentTime, postTime.getTime());
times[i].setAttribute('title', dateformat(iso8601(t)));
}
}
};
do_localtime(document);
if (window.jQuery) {
if (window.Options && Options.get_tab('general') && window.jQuery) {
var interval_id;
Options.extend_tab('general', '<label id="show-relative-time"><input type="checkbox">' + _('Show relative time') + '</label>');
$('#show-relative-time>input').on('change', function() {
if (localStorage.show_relative_time === 'true') {
localStorage.show_relative_time = 'false';
clearInterval(interval_id);
} else {
localStorage.show_relative_time = 'true';
interval_id = setInterval(do_localtime, 30000, document);
}
// no need to refresh page
do_localtime(document);
});
if (localStorage.show_relative_time === 'true') {
$('#show-relative-time>input').attr('checked','checked');
interval_id = setInterval(do_localtime, 30000, document);
}
// allow to work with auto-reload.js, etc.
$(document).on('new_post', function(e, post) {
do_localtime(post);
});
}
});
do_localtime(document);
});

23
js/no-animated-gif.js

@ -11,8 +11,9 @@
* //$config['additional_javascript'][] = 'js/options/general.js';
* $config['additional_javascript'][] = 'js/no-animated-gif.js';
*/
function unanimate_gif(e) {
if ($(e).closest('.thread').children('.thread-hidden').length > 0) return;
if (active_page === "catalog")
var c = $('<canvas class="thread-image"></canvas>');
else
@ -34,13 +35,19 @@ function unanimate_gif(e) {
$(e).addClass("unanimated").hide();
}
$(function(){
var gif_finder = 'img.post-image[src$=".gif"], img.thread-image[src$=".gif"]';
function no_animated_gif() {
var anim_gifs = $('img.post-image[src$=".gif"], img.thread-image[src$=".gif"]');
var anim_gifs = $(gif_finder);
localStorage.no_animated_gif = true;
$('#no-animated-gif>a').text(_('Animate GIFs'));
$('#no-animated-gif>input').prop('checked', true);
$.each(anim_gifs, function(i, e) {unanimate_gif(e)} );
$(document).on('new_post', new_post_handler);
}
function animated_gif() {
@ -49,10 +56,17 @@ function animated_gif() {
localStorage.no_animated_gif = false;
$('#no-animated-gif>a').text(_('Unanimate GIFs'));
$('#no-animated-gif>input').prop('checked', false);
$(document).off('new_post', new_post_handler);
}
function new_post_handler(e, post) {
$(post).find(gif_finder).each(function(k, v) {
unanimate_gif(v);
});
}
if (active_page == 'thread' || active_page == 'index' || active_page == 'ukko' || active_page == 'catalog') {
$(function(){
var selector, event;
if (window.Options && Options.get_tab('general')) {
selector = '#no-animated-gif>input';
@ -75,5 +89,6 @@ if (active_page == 'thread' || active_page == 'index' || active_page == 'ukko' |
if (localStorage.no_animated_gif === 'true')
no_animated_gif();
});
}
});

72
js/options/fav.js

@ -0,0 +1,72 @@
$(document).ready(function(){
//Creating functions
var generateList = function(){
var favStor = [];
for(var i=1; i<favorites.length+1; i++){
favStor.push($("#sortable > div:nth-child("+i+")").html());
}
return favStor;
} //This will generate a list of boards based off of the list on the screen
function removeBoard(boardNumber){
favorites.splice(boardNumber, 1);
localStorage.favorites = JSON.stringify(favorites);
$("#sortable > div:nth-child("+(boardNumber+1)+")").remove();
$("#minusList > div:nth-child("+(favorites.length+1)+")").remove();
add_favorites();
} //This removes a board from favorites, localStorage.favorites and the page
function addBoard(){
$("#sortable").append("<div>"+($("#plusBox").val())+"</div>");
$("#minusList").append( $('<div data-board="'+favorites.length+'" style="cursor: pointer; margin-right: 5px">-</div>').on('click', function(e){removeBoard($(this).data('board'));}) );
favorites.push($("#plusBox").val());
localStorage.favorites = JSON.stringify(favorites);
$("#plusBox").val(""); //Removing text from textbox
add_favorites();
} //This adds the text inside the textbox to favorites, localStorage.favorites and the page
var favorites = JSON.parse(localStorage.favorites);
Options.add_tab('fav-tab','star',_("Favorites"));
//Pregenerating list of boards
var favList = $('<div id="sortable" style="cursor: pointer; display: inline-block">');
for(var i=0; i<favorites.length; i++){
favList.append( $('<div>'+favorites[i]+'</div>') );
}
//Creating list of minus symbols to remove unwanted boards
var minusList = $('<div id="minusList" style="color: #0000FF; display: inline-block">');
for(var i=0; i<favorites.length; i++){
minusList.append( $('<div data-board="'+i+'" style="cursor: pointer; margin-right: 5px">-</div>').on('click', function(e){removeBoard($(this).data('board'));}) );
}
//Help message so people understand how sorting boards works
$("<span>"+_("Drag the boards to sort them.")+"</span><br><br>").appendTo(Options.get_tab('fav-tab').content);
//Adding list of boards and minus symbols to remove boards with
$(minusList).appendTo(Options.get_tab('fav-tab').content); //Adding the list of minus symbols to the tab
$(favList).appendTo(Options.get_tab('fav-tab').content); //Adding the list of favorite boards to the tab
//Adding spacing and text box to right boards into
var addDiv = $("<div id='favs-add-board'>");
var plusBox = $("<input id=\"plusBox\" type=\"text\">").appendTo(addDiv);
plusBox.keydown(function( event ) {
if(event.keyCode == 13){
$("#plus").click();
}
});
//Adding plus symbol to use to add board
$("<div id=\"plus\">+</div>").css({
cursor: "pointer",
color: "#0000FF"
}).on('click', function(e){addBoard()}).appendTo(addDiv);
addDiv.appendTo(Options.get_tab('fav-tab').content); //Adding the plus button
favList.sortable(); //Making boards with sortable id use the sortable jquery function
favList.on('sortstop', function() {
favorites = generateList();
localStorage.favorites = JSON.stringify(favorites);
add_favorites();
});
});

2
js/options/user-css.js

@ -17,7 +17,7 @@ var textarea = $("<textarea></textarea>").css({
"font-size": 12,
position: "absolute",
top: 35, bottom: 35,
width: "calc(100% - 12px)", margin: 0, padding: 0, border: "1px solid black",
width: "calc(100% - 20px)", margin: 0, padding: "4px", border: "1px solid black",
left: 5, right: 5
}).appendTo(tab.content);
var submit = $("<input type='button' value='"+_("Update custom CSS")+"'>").css({

2
js/options/user-js.js

@ -17,7 +17,7 @@ var textarea = $("<textarea></textarea>").css({
"font-size": 12,
position: "absolute",
top: 35, bottom: 35,
width: "calc(100% - 12px)", margin: 0, padding: 0, border: "1px solid black",
width: "calc(100% - 20px)", margin: 0, padding: "4px", border: "1px solid black",
left: 5, right: 5
}).appendTo(tab.content);
var submit = $("<input type='button' value='"+_("Update custom Javascript")+"'>").css({

852
js/post-filter.js

@ -0,0 +1,852 @@
if (active_page === 'thread' || active_page === 'index' || active_page === 'catalog' || active_page === 'ukko') {
$(document).on('menu_ready', function () {
'use strict';
// returns blacklist object from storage
function getList() {
return JSON.parse(localStorage.postFilter);
}
// stores blacklist into storage and reruns the filter
function setList(blacklist) {
localStorage.postFilter = JSON.stringify(blacklist);
$(document).trigger('filter_page');
}
// unit: seconds
function timestamp() {
return Math.floor((new Date()).getTime() / 1000);
}
function initList(list, boardId, threadId) {
if (typeof list.postFilter[boardId] == 'undefined') {
list.postFilter[boardId] = {};
list.nextPurge[boardId] = {};
}
if (typeof list.postFilter[boardId][threadId] == 'undefined') {
list.postFilter[boardId][threadId] = [];
}
list.nextPurge[boardId][threadId] = {timestamp: timestamp(), interval: 86400}; // 86400 seconds == 1 day
}
function addFilter(type, value, useRegex) {
var list = getList();
var filter = list.generalFilter;
var obj = {
type: type,
value: value,
regex: useRegex
};
for (var i=0; i<filter.length; i++) {
if (filter[i].type == type && filter[i].value == value && filter[i].regex == useRegex)
return;
}
filter.push(obj);
setList(list);
drawFilterList();
}
function removeFilter(type, value, useRegex) {
var list = getList();
var filter = list.generalFilter;
for (var i=0; i<filter.length; i++) {
if (filter[i].type == type && filter[i].value == value && filter[i].regex == useRegex) {
filter.splice(i, 1);
break;
}
}
setList(list);
drawFilterList();
}
function nameSpanToString(el) {
var s = '';
$.each($(el).contents(), function(k,v) {
if (v.nodeName === 'IMG')
s=s+$(v).attr('alt')
if (v.nodeName === '#text')
s=s+v.nodeValue
});
return s.trim();
}
var blacklist = {
add: {
post: function (boardId, threadId, postId, hideReplies) {
var list = getList();
var filter = list.postFilter;
initList(list, boardId, threadId);
for (var i in filter[boardId][threadId]) {
if (filter[boardId][threadId][i].post == postId) return;
}
filter[boardId][threadId].push({
post: postId,
hideReplies: hideReplies
});
setList(list);
},
uid: function (boardId, threadId, uniqueId, hideReplies) {
var list = getList();
var filter = list.postFilter;
initList(list, boardId, threadId);
for (var i in filter[boardId][threadId]) {
if (filter[boardId][threadId][i].uid == uniqueId) return;
}
filter[boardId][threadId].push({
uid: uniqueId,
hideReplies: hideReplies
});
setList(list);
}
},
remove: {
post: function (boardId, threadId, postId) {
var list = getList();
var filter = list.postFilter;
// thread already pruned
if (typeof filter[boardId] == 'undefined' || typeof filter[boardId][threadId] == 'undefined')
return;
for (var i=0; i<filter[boardId][threadId].length; i++) {
if (filter[boardId][threadId][i].post == postId) {
filter[boardId][threadId].splice(i, 1);
break;
}
}
if ($.isEmptyObject(filter[boardId][threadId])) {
delete filter[boardId][threadId];
delete list.nextPurge[boardId][threadId];
if ($.isEmptyObject(filter[boardId])) {
delete filter[boardId];
delete list.nextPurge[boardId];
}
}
setList(list);
},
uid: function (boardId, threadId, uniqueId) {
var list = getList();
var filter = list.postFilter;
// thread already pruned
if (typeof filter[boardId] == 'undefined' || typeof filter[boardId][threadId] == 'undefined')
return;
for (var i=0; i<filter[boardId][threadId].length; i++) {
if (filter[boardId][threadId][i].uid == uniqueId) {
filter[boardId][threadId].splice(i, 1);
break;
}
}
if ($.isEmptyObject(filter[boardId][threadId])) {
delete filter[boardId][threadId];
delete list.nextPurge[boardId][threadId];
if ($.isEmptyObject(filter[boardId])) {
delete filter[boardId];
delete list.nextPurge[boardId];
}
}
setList(list);
}
}
};
/*
* hide/show the specified thread/post
*/
function hide(ele) {
var $ele = $(ele);
if ($(ele).data('hidden'))
return;
$(ele).data('hidden', true);
if ($ele.hasClass('op')) {
$ele.parent().find('.body, .files, .video-container').not($ele.children('.reply').children()).hide();
// hide thread replies on index view
if (active_page == 'index' || active_page == 'ukko') $ele.parent().find('.omitted, .reply:not(.hidden), post_no, .mentioned, br').hide();
} else {
// normal posts
$ele.children('.body, .files, .video-container').hide();
}
}
function show(ele) {
var $ele = $(ele);
$(ele).data('hidden', false);
if ($ele.hasClass('op')) {
$ele.parent().find('.body, .files, .video-container').show();
if (active_page == 'index') $ele.parent().find('.omitted, .reply:not(.hidden), post_no, .mentioned, br').show();
} else {
// normal posts
$ele.children('.body, .files, .video-container').show();
}
}
/*
* create filter menu when the button is clicked
*/
function initPostMenu(pageData) {
var Menu = window.Menu;
var submenu;
Menu.add_item('filter-menu-hide', _('Hide post'));
Menu.add_item('filter-menu-unhide', _('Unhide post'));
submenu = Menu.add_submenu('filter-menu-add', _('Add filter'));
submenu.add_item('filter-add-post-plus', _('Post +'), _('Hide post and all replies'));
submenu.add_item('filter-add-id', _('ID'));
submenu.add_item('filter-add-id-plus', _('ID +'), _('Hide ID and all replies'));
submenu.add_item('filter-add-name', _('Name'));
submenu.add_item('filter-add-trip', _('Tripcode'));
submenu = Menu.add_submenu('filter-menu-remove', _('Remove filter'));
submenu.add_item('filter-remove-id', _('ID'));
submenu.add_item('filter-remove-name', _('Name'));
submenu.add_item('filter-remove-trip', _('Tripcode'));
Menu.onclick(function (e, $buffer) {
var ele = e.target.parentElement.parentElement;
var $ele = $(ele);
var threadId = $ele.parent().attr('id').replace('thread_', '');
var boardId = $ele.parent().data('board');
var postId = $ele.find('.post_no').not('[id]').text();
if (pageData.hasUID) {
var postUid = $ele.find('.poster_id').text();
}
var postName;
var postTrip = '';
if (!pageData.forcedAnon) {
postName = (typeof $ele.find('.name').contents()[0] == 'undefined') ? '' : nameSpanToString($ele.find('.name')[0]);
postTrip = $ele.find('.trip').text();
}
/* display logic and bind click handlers
*/
// unhide button
if ($ele.data('hidden')) {
$buffer.find('#filter-menu-unhide').click(function () {
// if hidden due to post id, remove it from blacklist
// otherwise just show this post
blacklist.remove.post(boardId, threadId, postId);
show(ele);
});
$buffer.find('#filter-menu-hide').addClass('hidden');
} else {
$buffer.find('#filter-menu-unhide').addClass('hidden');
$buffer.find('#filter-menu-hide').click(function () {
blacklist.add.post(boardId, threadId, postId, false);
});
}
// post id
if (!$ele.data('hiddenByPost')) {
$buffer.find('#filter-add-post-plus').click(function () {
blacklist.add.post(boardId, threadId, postId, true);
});
} else {
$buffer.find('#filter-add-post-plus').addClass('hidden');
}
// UID
if (pageData.hasUID && !$ele.data('hiddenByUid')) {
$buffer.find('#filter-add-id').click(function () {
blacklist.add.uid(boardId, threadId, postUid, false);
});
$buffer.find('#filter-add-id-plus').click(function () {
blacklist.add.uid(boardId, threadId, postUid, true);
});
$buffer.find('#filter-remove-id').addClass('hidden');
} else if (pageData.hasUID) {
$buffer.find('#filter-remove-id').click(function () {
blacklist.remove.uid(boardId, threadId, postUid);
});
$buffer.find('#filter-add-id').addClass('hidden');
$buffer.find('#filter-add-id-plus').addClass('hidden');
} else {
// board doesn't use UID
$buffer.find('#filter-add-id').addClass('hidden');
$buffer.find('#filter-add-id-plus').addClass('hidden');
$buffer.find('#filter-remove-id').addClass('hidden');
}
// name
if (!pageData.forcedAnon && !$ele.data('hiddenByName')) {
$buffer.find('#filter-add-name').click(function () {
addFilter('name', postName, false);
});
$buffer.find('#filter-remove-name').addClass('hidden');
} else if (!pageData.forcedAnon) {
$buffer.find('#filter-remove-name').click(function () {
removeFilter('name', postName, false);
});
$buffer.find('#filter-add-name').addClass('hidden');
} else {
// board has forced anon
$buffer.find('#filter-remove-name').addClass('hidden');
$buffer.find('#filter-add-name').addClass('hidden');
}
// tripcode
if (!pageData.forcedAnon && !$ele.data('hiddenByTrip') && postTrip !== '') {
$buffer.find('#filter-add-trip').click(function () {
addFilter('trip', postTrip, false);
});
$buffer.find('#filter-remove-trip').addClass('hidden');
} else if (!pageData.forcedAnon && postTrip !== '') {
$buffer.find('#filter-remove-trip').click(function () {
removeFilter('trip', postTrip, false);
});
$buffer.find('#filter-add-trip').addClass('hidden');
} else {
// board has forced anon
$buffer.find('#filter-remove-trip').addClass('hidden');
$buffer.find('#filter-add-trip').addClass('hidden');
}
/* hide sub menus if all items are hidden
*/
if (!$buffer.find('#filter-menu-remove > ul').children().not('.hidden').length) {
$buffer.find('#filter-menu-remove').addClass('hidden');
}
if (!$buffer.find('#filter-menu-add > ul').children().not('.hidden').length) {
$buffer.find('#filter-menu-add').addClass('hidden');
}
});
}
/*
* hide/unhide thread on index view
*/
function quickToggle(ele, threadId, pageData) {
/*if ($(ele).find('.hide-thread-link').length)
$('.hide-thread-link').remove();*/
if ($(ele).hasClass('op') && !$(ele).find('.hide-thread-link').length) {
$('<a class="hide-thread-link" style="float:left;margin-right:5px" href="javascript:void(0)">[' + ($(ele).data('hidden') ? '+' : '&ndash;') + ']</a>')
.insertBefore($(ele).find(':not(h2,h2 *):first'))
.click(function() {
var postId = $(ele).find('.post_no').not('[id]').text();
var hidden = $(ele).data('hidden');
var boardId = $(ele).parents('.thread').data('board');
if (hidden) {
blacklist.remove.post(boardId, threadId, postId, false);
$(this).html('[&ndash;]');
} else {
blacklist.add.post(boardId, threadId, postId, false);
$(this).text('[+]');
}
});
}
}
/*
* determine whether the reply post should be hidden
* - applies to all posts on page load or filtering rule change
* - apply to new posts on thread updates
* - must explicitly set the state of each attributes because filter will reapply to all posts after filtering rule change
*/
function filter(post, threadId, pageData) {
var $post = $(post);
var list = getList();
var postId = $post.find('.post_no').not('[id]').text();
var name, trip, uid, subject, comment;
var i, length, array, rule, pattern; // temp variables
var boardId = $post.data('board');
if (!boardId) boardId = $post.parents('.thread').data('board');
var localList = pageData.localList;
var noReplyList = pageData.noReplyList;
var hasUID = pageData.hasUID;
var forcedAnon = pageData.forcedAnon;
var hasTrip = ($post.find('.trip').length > 0);
var hasSub = ($post.find('.subject').length > 0);
$post.data('hidden', false);
$post.data('hiddenByUid', false);
$post.data('hiddenByPost', false);
$post.data('hiddenByName', false);
$post.data('hiddenByTrip', false);
$post.data('hiddenBySubject', false);
$post.data('hiddenByComment', false);
// add post with matched UID to localList
if (hasUID &&
typeof list.postFilter[boardId] != 'undefined' &&
typeof list.postFilter[boardId][threadId] != 'undefined') {
uid = $post.find('.poster_id').text();
array = list.postFilter[boardId][threadId];
for (i=0; i<array.length; i++) {
if (array[i].uid == uid) {
$post.data('hiddenByUid', true);
localList.push(postId);
if (array[i].hideReplies) noReplyList.push(postId);
break;
}
}
}
// match localList
if (localList.length) {
if ($.inArray(postId, localList) != -1) {
if ($post.data('hiddenByUid') !== true) $post.data('hiddenByPost', true);
hide(post);
}
}
// matches generalFilter
if (!forcedAnon)
name = (typeof $post.find('.name').contents()[0] == 'undefined') ? '' : nameSpanToString($post.find('.name')[0]);
if (!forcedAnon && hasTrip)
trip = $post.find('.trip').text();
if (hasSub)
subject = $post.find('.subject').text();
array = $post.find('.body').contents().filter(function () {if ($(this).text() !== '') return true;}).toArray();
array = $.map(array, function (ele) {
return $(ele).text().trim();
});
comment = array.join(' ');
for (i = 0, length = list.generalFilter.length; i < length; i++) {
rule = list.generalFilter[i];
if (rule.regex) {
pattern = new RegExp(rule.value);
switch (rule.type) {
case 'name':
if (!forcedAnon && pattern.test(name)) {
$post.data('hiddenByName', true);
hide(post);
}
break;
case 'trip':
if (!forcedAnon && hasTrip && pattern.test(trip)) {
$post.data('hiddenByTrip', true);
hide(post);
}
break;
case 'sub':
if (hasSub && pattern.test(subject)) {
$post.data('hiddenBySubject', true);
hide(post);
}
break;
case 'com':
if (pattern.test(comment)) {
$post.data('hiddenByComment', true);
hide(post);
}
break;
}
} else {
switch (rule.type) {
case 'name':
if (!forcedAnon && rule.value == name) {
$post.data('hiddenByName', true);
hide(post);
}
break;
case 'trip':
if (!forcedAnon && hasTrip && rule.value == trip) {
$post.data('hiddenByTrip', true);
hide(post);
}
break;
case 'sub':
pattern = new RegExp('\\b'+ rule.value+ '\\b');
if (hasSub && pattern.test(subject)) {
$post.data('hiddenBySubject', true);
hide(post);
}
break;
case 'com':
pattern = new RegExp('\\b'+ rule.value+ '\\b');
if (pattern.test(comment)) {
$post.data('hiddenByComment', true);
hide(post);
}
break;
}
}
}
// check for link to filtered posts
$post.find('.body a').not('[rel="nofollow"]').each(function () {
var replyId = $(this).text().match(/^>>(\d+)$/);
if (!replyId)
return;
replyId = replyId[1];
if ($.inArray(replyId, noReplyList) != -1) {
hide(post);
}
});
// post didn't match any filters
if (!$post.data('hidden')) {
show(post);
}
}
/* (re)runs the filter on the entire page
*/
function filterPage(pageData) {
var list = getList();
if (active_page != 'catalog') {
// empty the local and no-reply list
pageData.localList = [];
pageData.noReplyList = [];
$('.thread').each(function () {
var $thread = $(this);
// disregard the hidden threads constructed by post-hover.js
if ($thread.css('display') == 'none')
return;
var threadId = $thread.attr('id').replace('thread_', '');
var boardId = $thread.data('board');
var op = $thread.children('.op')[0];
var i, array; // temp variables
// add posts to localList and noReplyList
if (typeof list.postFilter[boardId] != 'undefined' && typeof list.postFilter[boardId][threadId] != 'undefined') {
array = list.postFilter[boardId][threadId];
for (i=0; i<array.length; i++) {
if ( typeof array[i].post == 'undefined')
continue;
pageData.localList.push(array[i].post);
if (array[i].hideReplies) pageData.noReplyList.push(array[i].post);
}
}
// run filter on OP
filter(op, threadId, pageData);
quickToggle(op, threadId, pageData);
// iterate filter over each post
if (!$(op).data('hidden') || active_page == 'thread') {
$thread.find('.reply').not('.hidden').each(function () {
filter(this, threadId, pageData);
});
}
});
} else {
var postFilter = list.postFilter[pageData.boardId];
var $collection = $('.mix');
if ($.isEmptyObject(postFilter))
return;
// for each thread that has filtering rules
// check if filter contains thread OP and remove the thread from catalog
$.each(postFilter, function (key, thread) {
var threadId = key;
$.each(thread, function () {
if (this.post == threadId) {
$collection.filter('[data-id='+ threadId +']').remove();
}
});
});
}
}
function initStyle() {
var $ele, cssStyle, cssString;
$ele = $('<div>').addClass('post reply').hide().appendTo('body');
cssStyle = $ele.css(['background-color', 'border-color']);
cssStyle.hoverBg = $('body').css('background-color');
$ele.remove();
cssString = '\n/*** Generated by post-filter ***/\n' +
'#filter-control input[type=text] {width: 130px;}' +
'#filter-control input[type=checkbox] {vertical-align: middle;}' +
'#filter-control #clear {float: right;}\n' +
'#filter-container {margin-top: 20px; border: 1px solid; height: 270px; overflow: auto;}\n' +
'#filter-list {width: 100%; border-collapse: collapse;}\n' +
'#filter-list th {text-align: center; height: 20px; font-size: 14px; border-bottom: 1px solid;}\n' +
'#filter-list th:nth-child(1) {text-align: center; width: 70px;}\n' +
'#filter-list th:nth-child(2) {text-align: left;}\n' +
'#filter-list th:nth-child(3) {text-align: center; width: 58px;}\n' +
'#filter-list tr:not(#header) {height: 22px;}\n' +
'#filter-list tr:nth-child(even) {background-color:rgba(255, 255, 255, 0.5);}\n' +
'#filter-list td:nth-child(1) {text-align: center; width: 70px;}\n' +
'#filter-list td:nth-child(3) {text-align: center; width: 58px;}\n' +
'#confirm {text-align: right; margin-bottom: -18px; padding-top: 2px; font-size: 14px; color: #FF0000;}';
if (!$('style.generated-css').length) $('<style class="generated-css">').appendTo('head');
$('style.generated-css').html($('style.generated-css').html() + cssString);
}
function drawFilterList() {
var list = getList().generalFilter;
var $ele = $('#filter-list');
var $row, i, length, obj, val;
var typeName = {
name: 'name',
trip: 'tripcode',
sub: 'subject',
com: 'comment'
};
$ele.empty();
$ele.append('<tr id="header"><th>Type</th><th>Content</th><th>Remove</th></tr>');
for (i = 0, length = list.length; i < length; i++) {
obj = list[i];
// display formatting
val = (obj.regex) ? '/'+ obj.value +'/' : obj.value;
$row = $('<tr>');
$row.append(
'<td>'+ typeName[obj.type] +'</td>',
'<td>'+ val +'</td>',
$('<td>').append(
$('<a>').html('X')
.addClass('del-btn')
.attr('href', '#')
.data('type', obj.type)
.data('val', obj.value)
.data('useRegex', obj.regex)
)
);
$ele.append($row);
}
}
function initOptionsPanel() {
if (window.Options && !Options.get_tab('filter')) {
Options.add_tab('filter', 'list', _('Filters'));
Options.extend_tab('filter',
'<div id="filter-control">' +
'<select>' +
'<option value="name">'+_('Name')+'</option>' +
'<option value="trip">'+_('Tripcode')+'</option>' +
'<option value="sub">'+_('Subject')+'</option>' +
'<option value="com">'+_('Comment')+'</option>' +
'</select>' +
'<input type="text">' +
'<input type="checkbox">' +
'regex ' +
'<button id="set-filter">'+_('Add')+'</button>' +
'<button id="clear">'+_('Clear all filters')+'</button>' +
'<div id="confirm" class="hidden">' +
_('This will clear all filtering rules including hidden posts.')+' <a id="confirm-y" href="#">'+_('yes')+'</a> | <a id="confirm-n" href="#">'+_('no')+'</a>' +
'</div>' +
'</div>' +
'<div id="filter-container"><table id="filter-list"></table></div>'
);
drawFilterList();
// control buttons
$('#filter-control').on('click', '#set-filter', function () {
var type = $('#filter-control select option:selected').val();
var value = $('#filter-control input[type=text]').val();
var useRegex = $('#filter-control input[type=checkbox]').prop('checked');
//clear the input form
$('#filter-control input[type=text]').val('');
addFilter(type, value, useRegex);
drawFilterList();
});
$('#filter-control').on('click', '#clear', function () {
$('#filter-control #clear').addClass('hidden');
$('#filter-control #confirm').removeClass('hidden');
});
$('#filter-control').on('click', '#confirm-y', function (e) {
e.preventDefault();
$('#filter-control #clear').removeClass('hidden');
$('#filter-control #confirm').addClass('hidden');
setList({
generalFilter: [],
postFilter: {},
nextPurge: {},
lastPurge: timestamp()
});
drawFilterList();
});
$('#filter-control').on('click', '#confirm-n', function (e) {
e.preventDefault();
$('#filter-control #clear').removeClass('hidden');
$('#filter-control #confirm').addClass('hidden');
});
// remove button
$('#filter-list').on('click', '.del-btn', function (e) {
e.preventDefault();
var $ele = $(e.target);
var type = $ele.data('type');
var val = $ele.data('val');
var useRegex = $ele.data('useRegex');
removeFilter(type, val, useRegex);
});
}
}
/*
* clear out pruned threads
*/
function purge() {
var list = getList();
var board, thread, boardId, threadId;
var deferred;
var requestArray = [];
var successHandler = function (boardId, threadId) {
return function () {
// thread still alive, keep it in the list and increase the time between checks.
var list = getList();
var thread = list.nextPurge[boardId][threadId];
thread.timestamp = timestamp();
thread.interval = Math.floor(thread.interval * 1.5);
setList(list);
};
};
var errorHandler = function (boardId, threadId) {
return function (xhr) {
if (xhr.status == 404) {
var list = getList();
delete list.nextPurge[boardId][threadId];
delete list.postFilter[boardId][threadId];
if ($.isEmptyObject(list.nextPurge[boardId])) delete list.nextPurge[boardId];
if ($.isEmptyObject(list.postFilter[boardId])) delete list.postFilter[boardId];
setList(list);
}
};
};
if ((timestamp() - list.lastPurge) < 86400) // less than 1 day
return;
for (boardId in list.nextPurge) {
board = list.nextPurge[boardId];
for (threadId in board) {
thread = board[threadId];
if (timestamp() > (thread.timestamp + thread.interval)) {
// check if thread is pruned
deferred = $.ajax({
cache: false,
url: '/'+ boardId +'/res/'+ threadId +'.json',
success: successHandler(boardId, threadId),
error: errorHandler(boardId, threadId)
});
requestArray.push(deferred);
}
}
}
// when all requests complete
$.when.apply($, requestArray).always(function () {
var list = getList();
list.lastPurge = timestamp();
setList(list);
});
}
function init() {
if (typeof localStorage.postFilter === 'undefined') {
localStorage.postFilter = JSON.stringify({
generalFilter: [],
postFilter: {},
nextPurge: {},
lastPurge: timestamp()
});
}
var pageData = {
boardId: board_name, // get the id from the global variable
localList: [], // all the blacklisted post IDs or UIDs that apply to the current page
noReplyList: [], // any posts that replies to the contents of this list shall be hidden
hasUID: (document.getElementsByClassName('poster_id').length > 0),
forcedAnon: ($('th:contains(Name)').length === 0) // tests by looking for the Name label on the reply form
};
initStyle();
initOptionsPanel();
initPostMenu(pageData);
filterPage(pageData);
// on new posts
$(document).on('new_post', function (e, post) {
var threadId;
if ($(post).hasClass('reply')) {
threadId = $(post).parents('.thread').attr('id').replace('thread_', '');
} else {
threadId = $(post).attr('id').replace('thread_', '');
post = $(post).children('.op')[0];
}
filter(post, threadId, pageData);
quickToggle(post, threadId, pageData);
});
$(document).on('filter_page', function () {
filterPage(pageData);
});
// shift+click on catalog to hide thread
if (active_page == 'catalog') {
$(document).on('click', '.mix', function(e) {
if (e.shiftKey) {
var threadId = $(this).data('id').toString();
var postId = threadId;
blacklist.add.post(pageData.boardId, threadId, postId, false);
}
});
}
// clear out the old threads
purge();
}
init();
});
if (typeof window.Menu !== "undefined") {
$(document).trigger('menu_ready');
}
}

5
js/post-hover.js

@ -54,10 +54,7 @@ onready(function(){
hovered_at = {'x': e.pageX, 'y': e.pageY};
var start_hover = function($link) {
if($.contains($post[0], $link[0])) {
// link links to itself or to op; ignore
}
else if($post.is(':visible') &&
if ($post.is(':visible') &&
$post.offset().top >= $(window).scrollTop() &&
$post.offset().top + $post.height() <= $(window).scrollTop() + $(window).height()) {
// post is in view

211
js/post-menu.js

@ -0,0 +1,211 @@
/*
* post-menu.js - adds dropdown menu to posts
*
* Creates a global Menu object with four public methods:
*
* Menu.onclick(fnc)
* registers a function to be executed after button click, before the menu is displayed
* Menu.add_item(id, text[, title])
* adds an item to the top level of menu
* Menu.add_submenu(id, text)
* creates and returns a List object through which to manipulate the content of the submenu
* Menu.get_submenu(id)
* returns the submenu with the specified id from the top level menu
*
* The List object contains all the methods from Menu except onclick()
*
* Example usage:
* Menu.add_item('filter-menu-hide', 'Hide post');
* Menu.add_item('filter-menu-unhide', 'Unhide post');
*
* submenu = Menu.add_submenu('filter-menu-add', 'Add filter');
* submenu.add_item('filter-add-post-plus', 'Post +', 'Hide post and all replies');
* submenu.add_item('filter-add-id', 'ID');
*
* Usage:
* $config['additional_javascript'][] = 'js/jquery.min.js';
* $config['additional_javascript'][] = 'js/post-menu.js';
*/
$(document).ready(function () {
var List = function (menuId, text) {
this.id = menuId;
this.text = text;
this.items = [];
this.add_item = function (itemId, text, title) {
this.items.push(new Item(itemId, text, title));
};
this.list_items = function () {
var array = [];
var i, length, obj, $ele;
if ($.isEmptyObject(this.items))
return;
length = this.items.length;
for (i = 0; i < length; i++) {
obj = this.items[i];
$ele = $('<li>', {id: obj.id}).text(obj.text);
if ('title' in obj) $ele.attr('title', obj.title);
if (obj instanceof Item) {
$ele.addClass('post-item');
} else {
$ele.addClass('post-submenu');
$ele.prepend(obj.list_items());
$ele.append($('<span>', {class: 'post-menu-arrow'}).text('»'));
}
array.push($ele);
}
return $('<ul>').append(array);
};
this.add_submenu = function (menuId, text) {
var ele = new List(menuId, text);
this.items.push(ele);
return ele;
};
this.get_submenu = function (menuId) {
for (var i = 0; i < this.items.length; i++) {
if ((this.items[i] instanceof Item) || this.items[i].id != menuId) continue;
return this.items[i];
}
};
};
var Item = function (itemId, text, title) {
this.id = itemId;
this.text = text;
// optional
if (typeof title != 'undefined') this.title = title;
};
function buildMenu(e) {
var pos = $(e.target).offset();
var i, length;
var $menu = $('<div class="post-menu"></div>').append(mainMenu.list_items());
// execute registered click handlers
length = onclick_callbacks.length;
for (i = 0; i < length; i++) {
onclick_callbacks[i](e, $menu);
}
// set menu position and append to page
$menu.css({top: pos.top, left: pos.left + 20});
$('body').append($menu);
}
function addButton(post) {
var $ele = $(post);
$ele.find('input.delete').after(
$('<a>', {href: '#', class: 'post-btn', title: 'Post menu'}).text('▶')
);
}
/* * * * * * * * * *
Public methods
* * * * * * * * * */
var Menu = {};
var mainMenu = new List();
var onclick_callbacks = [];
Menu.onclick = function (fnc) {
onclick_callbacks.push(fnc);
};
Menu.add_item = function (itemId, text, title) {
mainMenu.add_item(itemId, text, title);
};
Menu.add_submenu = function (menuId, text) {
return mainMenu.add_submenu(menuId, text);
};
Menu.get_submenu = function (id) {
return mainMenu.get_submenu(id);
};
window.Menu = Menu;
/* * * * * * * *
Initialize
* * * * * * * */
/* Styling
*/
var $ele, cssStyle, cssString;
$ele = $('<div>').addClass('post reply').hide().appendTo('body');
cssStyle = $ele.css(['border-top-color']);
cssStyle.hoverBg = $('body').css('background-color');
$ele.remove();
cssString =
'\n/*** Generated by post-menu ***/\n' +
'.post-menu {position: absolute; font-size: 12px; line-height: 1.3em;}\n' +
'.post-menu ul {\n' +
' background-color: '+ cssStyle['border-top-color'] +'; border: 1px solid #666;\n' +
' list-style: none; padding: 0; margin: 0; white-space: nowrap;\n}\n' +
'.post-menu .post-submenu{white-space: normal; width: 90px;}' +
'.post-menu .post-submenu>ul{white-space: nowrap; width: auto;}' +
'.post-menu li {cursor: pointer; position: relative; padding: 4px 4px; vertical-align: middle;}\n' +
'.post-menu li:hover {background-color: '+ cssStyle.hoverBg +';}\n' +
'.post-menu ul ul {display: none; position: absolute;}\n' +
'.post-menu li:hover>ul {display: block; left: 100%; margin-top: -3px;}\n' +
'.post-menu-arrow {float: right; margin-left: 10px;}\n' +
'.post-menu.hidden, .post-menu .hidden {display: none;}\n' +
'.post-btn {transition: transform 0.1s; width: 15px; text-align: center; font-size: 10pt; opacity: 0.8; text-decoration: none; margin: -6px 0px 0px -5px !important; display: inline-block;}\n' +
'.post-btn:hover {opacity: 1;}\n' +
'.post-btn-open {transform: rotate(90deg);}\n';
if (!$('style.generated-css').length) $('<style class="generated-css">').appendTo('head');
$('style.generated-css').html($('style.generated-css').html() + cssString);
/* Add buttons
*/
$('.reply:not(.hidden), .thread>.op').each(function () {
addButton(this);
});
/* event handlers
*/
$('form[name=postcontrols]').on('click', '.post-btn', function (e) {
e.preventDefault();
var post = e.target.parentElement.parentElement;
$('.post-menu').remove();
if ($(e.target).hasClass('post-btn-open')) {
$('.post-btn-open').removeClass('post-btn-open');
} else {
// close previous button
$('.post-btn-open').removeClass('post-btn-open');
$(post).find('.post-btn').addClass('post-btn-open');
buildMenu(e);
}
});
$(document).on('click', function (e){
if ($(e.target).hasClass('post-btn') || $(e.target).hasClass('post-submenu'))
return;
$('.post-menu').remove();
$('.post-btn-open').removeClass('post-btn-open');
});
// on new posts
$(document).on('new_post', function (e, post) {
addButton(post);
});
$(document).trigger('menu_ready');
});

2
js/quick-reply.js

@ -283,7 +283,7 @@
$postForm.find('br').remove();
$postForm.find('table').prepend('<tr><th colspan="2">\
<span class="handle">\
<a class="close-btn" href="javascript:void(0)">X</a>\
<a class="close-btn" href="javascript:void(0)">×</a>\
' + _('Quick Reply') + '\
</span>\
</th></tr>');

2
js/show-own-posts.js

@ -33,7 +33,7 @@ var update_own = function() {
if (posts[board] && posts[board].indexOf(id) !== -1) { // Own post!
$(this).addClass('you');
$(this).find('span.name').first().append(' '+_('(You)'));
$(this).find('span.name').first().append(' <span class="own_post">'+_('(You)')+'</span>');
}
// Update references

42
js/thread-watcher.js

@ -28,10 +28,10 @@ watchlist.render = function(reset) {
JSON.parse(localStorage.watchlist).forEach(function(e, i) {
//look at line 69, that's what (e) is here.
threads.push('<div class="watchlist-inner" id="watchlist-'+i+'">' +
'<span>Board: '+e[0]+'</span>&nbsp' +
'<span>Thread: '+'<a href="'+e[3]+'">'+e[1]+'</a></span>&nbsp' +
'<span>Replies: '+e[2]+'</span>&nbsp' +
'<a class="watchlist-remove">[Unwatch]</a>'+
'<span>/'+e[0]+'/ - ' +
'<a href="'+e[3]+'">'+e[1].replace("thread_", _("Thread #"))+'</a>' +
' ('+e[2]+') </span>' +
'<a class="watchlist-remove">X</a>'+
'</div>');
});
if ($('#watchlist').length) {
@ -40,17 +40,15 @@ watchlist.render = function(reset) {
$('#watchlist').append(threads.join(''));
} else {
//If the watchlist has not yet been rendered, create it.
$('form[name="post"]').before(
var menuStyle = getComputedStyle($('.boardlist')[0]);
$((active_page == 'ukko') ? 'hr:first' : (active_page == 'catalog') ? 'body>span:first' : 'form[name="post"]').before(
$('<div id="watchlist">'+
'<div class="watchlist-controls">'+
'<span><a id="clearList">[Clear List]</a></span>&nbsp'+
'<span><a id="clearGhosts">[Clear Ghosts]</a></span>'+
'<span><a id="clearList">['+_('Clear List')+']</a></span>&nbsp'+
'<span><a id="clearGhosts">['+_('Clear Ghosts')+']</a></span>'+
'</div>'+
threads.join('')+
'</div>').css({
background: $('.reply').css('background'),
borderColor : $('.reply').css('border-color')
}));
'</div>').css("background-color", menuStyle.backgroundColor).css("border", menuStyle.borderBottomWidth+" "+menuStyle.borderBottomStyle+" "+menuStyle.borderBottomColor));
}
return this;
};
@ -62,6 +60,8 @@ watchlist.render = function(reset) {
watchlist.add = function(sel) {
var threadName, threadInfo;
var board_name = $(sel).parents('.thread').data('board');
if (active_page === 'thread') {
if ($('.subject').length){
//If a subject is given, use the first 20 characters as the thread name.
@ -72,7 +72,7 @@ watchlist.add = function(sel) {
//board name, thread name as defined above, current amount of posts, thread url
threadInfo = [board_name, threadName, $('.post').length, location.href];
} else if (active_page === 'index') {
} else if (active_page === 'index' || active_page === 'ukko') {
var postCount;
//Figure out the post count.
@ -81,8 +81,8 @@ watchlist.add = function(sel) {
} else {
postCount = $(sel).parents('.op').siblings('.post').length+1;
}
//Grab the reply link.
var threadLink = $(sel).siblings('a:contains("[Reply]")').attr('href');
//Grab the reply link.;
var threadLink = $(sel).siblings('a:not(.watchThread)').last().attr('href');
//Figure out the thread name. If anon, use the thread id.
if ($(sel).parent().find('.subject').length) {
threadName = $(sel).parent().find('.subject').text().substring(0,20);
@ -144,16 +144,21 @@ watchlist.exists = function(sel) {
};
$(document).ready(function(){
if (!(active_page == 'thread' || active_page == 'index' || active_page == 'catalog' || active_page == 'ukko')) {
return;
}
//Append the watchlist toggle button.
$('.boardlist').append('<span>[ <a id="watchlist-toggle">watchlist</a> ]</span>');
$('.boardlist').append('<span>[ <a class="watchlist-toggle" href="#">'+_('watchlist')+'</a> ]</span>');
//Append a watch thread button after every OP.
$('.op>.intro').append('<a class="watchThread">[Watch Thread]</a>');
$('.op>.intro').append('<a class="watchThread" href="#">['+_('Watch Thread')+']</a>');
//Draw the watchlist, hidden.
watchlist.render();
//Show or hide the watchlist.
$('#watchlist-toggle').on('click', function(e) {
$('.watchlist-toggle').on('click', function(e) {
e.preventDefault();
//if ctrl+click, reset the watchlist.
if (e.ctrlKey) {
watchlist.render(true);
@ -167,7 +172,8 @@ $(document).ready(function(){
//Trigger the watchlist add function.
//The selector is passed as an argument in case the page is not a thread.
$('.watchThread').on('click', function() {
$('.watchThread').on('click', function(e) {
e.preventDefault();
watchlist.add(this).render();
});

2
js/toggle-images.js

@ -20,7 +20,7 @@ $(document).ready(function(){
$('<style type="text/css"> img.hidden{ opacity: 0.1; background: grey; border: 1px solid #000; } </style>').appendTo($('head'));
var hideImage = function() {
if ($(this).parent()[0].dataset.expanded == 'true') {
if ($(this).parent().data('expanded') == 'true') {
$(this).parent().click();
}
$(this)

24
log.php

@ -0,0 +1,24 @@
<?php
include 'inc/functions.php';
include 'inc/mod/pages.php';
if (!isset($_GET['board']) || !preg_match("/{$config['board_regex']}/u", $_GET['board'])) {
http_response_code(400);
error('Bad board.');
}
if (!openBoard($_GET['board'])) {
http_response_code(404);
error('No board.');
}
if ($config['public_logs'] == 0) error('This board has public logs disabled. Ask the board owner to enable it.');
if ($config['public_logs'] == 1) $hide_names = false;
if ($config['public_logs'] == 2) $hide_names = true;
if (!isset($_GET['page'])) {
$page = 1;
} else {
$page = (int)$_GET['page'];
};
mod_board_log($board['uri'], $page, $hide_names, true);

35
mod.php

@ -5,22 +5,14 @@
*/
require_once 'inc/functions.php';
require_once 'inc/bans.php';
require_once 'inc/mod/pages.php';
require_once 'inc/mod/auth.php';
if ($config['debug'])
$parse_start_time = microtime(true);
// Fix for magic quotes
if (get_magic_quotes_gpc()) {
function strip_array($var) {
return is_array($var) ? array_map('strip_array', $var) : stripslashes($var);
}
$_GET = strip_array($_GET);
$_POST = strip_array($_POST);
}
require_once 'inc/bans.php';
require_once 'inc/mod/pages.php';
check_login(true);
$query = isset($_SERVER['QUERY_STRING']) ? rawurldecode($_SERVER['QUERY_STRING']) : '';
@ -47,11 +39,19 @@ $pages = array(
'/log' => 'log', // modlog
'/log/(\d+)' => 'log', // modlog
'/log:([^/]+)' => 'user_log', // modlog
'/log:([^/]+)/(\d+)' => 'user_log', // modlog
'/news' => 'secure_POST news', // view news
'/news/(\d+)' => 'secure_POST news', // view news
'/news/delete/(\d+)' => 'secure news_delete', // delete from news
'/log:([^/:]+)' => 'user_log', // modlog
'/log:([^/:]+)/(\d+)' => 'user_log', // modlog
'/log:b:([^/]+)' => 'board_log', // modlog
'/log:b:([^/]+)/(\d+)' => 'board_log', // modlog
'/edit_news' => 'secure_POST news', // view news
'/edit_news/(\d+)' => 'secure_POST news', // view news
'/edit_news/delete/(\d+)' => 'secure news_delete', // delete from news
'/edit_pages(?:/?(\%b)?)' => 'secure_POST pages',
'/edit_page/(\d+)' => 'secure_POST edit_page',
'/edit_pages/delete/([a-z0-9]+)' => 'secure delete_page',
'/edit_pages/delete/([a-z0-9]+)/(\%b)' => 'secure delete_page_board',
'/noticeboard' => 'secure_POST noticeboard', // view noticeboard
'/noticeboard/(\d+)' => 'secure_POST noticeboard', // view noticeboard
@ -88,6 +88,7 @@ $pages = array(
'/(\%b)/deletebyip/(\d+)(/global)?' => 'secure deletebyip', // delete all posts by IP address
'/(\%b)/(un)?lock/(\d+)' => 'secure lock', // lock thread
'/(\%b)/(un)?sticky/(\d+)' => 'secure sticky', // sticky thread
'/(\%b)/(un)?cycle/(\d+)' => 'secure cycle', // cycle thread
'/(\%b)/bump(un)?lock/(\d+)' => 'secure bumplock', // "bumplock" thread
'/themes' => 'themes_list', // manage themes

534
post.php

@ -23,6 +23,170 @@ if ((!isset($_POST['mod']) || !$_POST['mod'])
error("Board is locked");
}
$dropped_post = false;
// Is it a post coming from NNTP? Let's extract it and pretend it's a normal post.
if (isset($_GET['Newsgroups']) && $config['nntpchan']['enabled']) {
if ($_SERVER['REMOTE_ADDR'] != $config['nntpchan']['trusted_peer']) {
error("NNTPChan: Forbidden. $_SERVER[REMOTE_ADDR] is not a trusted peer");
}
$_POST = array();
$_POST['json_response'] = true;
$headers = json_encode($_GET);
if (!isset ($_GET['Message-Id'])) {
if (!isset ($_GET['Message-ID'])) {
error("NNTPChan: No message ID");
}
else $msgid = $_GET['Message-ID'];
}
else $msgid = $_GET['Message-Id'];
$groups = preg_split("/,\s*/", $_GET['Newsgroups']);
if (count($groups) != 1) {
error("NNTPChan: Messages can go to only one newsgroup");
}
$group = $groups[0];
if (!isset($config['nntpchan']['dispatch'][$group])) {
error("NNTPChan: We don't synchronize $group");
}
$xboard = $config['nntpchan']['dispatch'][$group];
$ref = null;
if (isset ($_GET['References'])) {
$refs = preg_split("/,\s*/", $_GET['References']);
if (count($refs) > 1) {
error("NNTPChan: We don't support multiple references");
}
$ref = $refs[0];
$query = prepare("SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id` = :ref");
$query->bindValue(':ref', $ref);
$query->execute() or error(db_error($query));
$ary = $query->fetchAll(PDO::FETCH_ASSOC);
if (count($ary) == 0) {
error("NNTPChan: We don't have $ref that $msgid references");
}
$p_id = $ary[0]['id'];
$p_board = $ary[0]['board'];
if ($p_board != $xboard) {
error("NNTPChan: Cross board references not allowed. Tried to reference $p_board on $xboard");
}
$_POST['thread'] = $p_id;
}
$date = isset($_GET['Date']) ? strtotime($_GET['Date']) : time();
list($ct) = explode('; ', $_GET['Content-Type']);
$query = prepare("SELECT COUNT(*) AS `c` FROM ``nntp_references`` WHERE `message_id` = :msgid");
$query->bindValue(":msgid", $msgid);
$query->execute() or error(db_error($query));
$a = $query->fetch(PDO::FETCH_ASSOC);
if ($a['c'] > 0) {
error("NNTPChan: We already have this post. Post discarded.");
}
if ($ct == 'text/plain') {
$content = file_get_contents("php://input");
}
elseif ($ct == 'multipart/mixed' || $ct == 'multipart/form-data') {
_syslog(LOG_INFO, "MM: Files: ".print_r($GLOBALS, true)); // Debug
$content = '';
$newfiles = array();
foreach ($_FILES['attachment']['error'] as $id => $error) {
if ($_FILES['attachment']['type'][$id] == 'text/plain') {
$content .= file_get_contents($_FILES['attachment']['tmp_name'][$id]);
}
elseif ($_FILES['attachment']['type'][$id] == 'message/rfc822') { // Signed message, ignore for now
}
else { // A real attachment :^)
$file = array();
$file['name'] = $_FILES['attachment']['name'][$id];
$file['type'] = $_FILES['attachment']['type'][$id];
$file['size'] = $_FILES['attachment']['size'][$id];
$file['tmp_name'] = $_FILES['attachment']['tmp_name'][$id];
$file['error'] = $_FILES['attachment']['error'][$id];
$newfiles["file$id"] = $file;
}
}
$_FILES = $newfiles;
}
else {
error("NNTPChan: Wrong mime type: $ct");
}
$_POST['subject'] = isset($_GET['Subject']) ? ($_GET['Subject'] == 'None' ? '' : $_GET['Subject']) : '';
$_POST['board'] = $xboard;
if (isset ($_GET['From'])) {
list($name, $mail) = explode(" <", $_GET['From'], 2);
$mail = preg_replace('/>\s+$/', '', $mail);
$_POST['name'] = $name;
//$_POST['email'] = $mail;
$_POST['email'] = '';
}
if (isset ($_GET['X_Sage'])) {
$_POST['email'] = 'sage';
}
$content = preg_replace_callback('/>>([0-9a-fA-F]{6,})/', function($id) use ($xboard) {
$id = $id[1];
$query = prepare("SELECT `board`,`id` FROM ``nntp_references`` WHERE `message_id_digest` LIKE :rule");
$idx = $id . "%";
$query->bindValue(':rule', $idx);
$query->execute() or error(db_error($query));
$ary = $query->fetchAll(PDO::FETCH_ASSOC);
if (count($ary) == 0) {
return ">>>>$id";
}
else {
$ret = array();
foreach ($ary as $v) {
if ($v['board'] != $xboard) {
$ret[] = ">>>/".$v['board']."/".$v['id'];
}
else {
$ret[] = ">>".$v['id'];
}
}
return implode($ret, ", ");
}
}, $content);
$_POST['body'] = $content;
$dropped_post = array(
'date' => $date,
'board' => $xboard,
'msgid' => $msgid,
'headers' => $headers,
'from_nntp' => true,
);
}
elseif (isset($_GET['Newsgroups'])) {
error("NNTPChan: NNTPChan support is disabled");
}
if (isset($_POST['delete'])) {
// Delete
@ -82,9 +246,11 @@ if (isset($_POST['delete'])) {
if (isset($_POST['file'])) {
// Delete just the file
deleteFile($id);
modLog("User deleted file from his own post #$id");
} else {
// Delete entire post
deletePost($id);
modLog("User deleted his own post #$id");
}
_syslog(LOG_INFO, 'Deleted post: ' .
@ -136,12 +302,29 @@ if (isset($_POST['delete'])) {
if (count($report) > $config['report_limit'])
error($config['error']['toomanyreports']);
if ($config['report_captcha'] && !isset($_POST['captcha_text'], $_POST['captcha_cookie'])) {
error($config['error']['bot']);
}
if ($config['report_captcha']) {
$resp = file_get_contents($config['captcha']['provider_check'] . "?" . http_build_query([
'mode' => 'check',
'text' => $_POST['captcha_text'],
'extra' => $config['captcha']['extra'],
'cookie' => $_POST['captcha_cookie']
]));
if ($resp !== '1') {
error($config['error']['captcha']);
}
}
$reason = escape_markup_modifiers($_POST['reason']);
markup($reason);
foreach ($report as &$id) {
$query = prepare(sprintf("SELECT `thread` , `body_nomarkup` FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
$query = prepare(sprintf("SELECT `id`,`thread` , `body_nomarkup` FROM ``posts_%s`` WHERE `id` = :id", $board['uri']));
$query->bindValue(':id', $id, PDO::PARAM_INT);
$query->execute() or error(db_error($query));
@ -197,13 +380,14 @@ if (isset($_POST['delete'])) {
$root = $is_mod ? $config['root'] . $config['file_mod'] . '?/' : $config['root'];
if (!isset($_POST['json_response'])) {
header('Location: ' . $root . $board['dir'] . $config['file_index'], true, $config['redirect_http']);
$index = $root . $board['dir'] . $config['file_index'];
echo Element('page.html', array('config' => $config, 'body' => '<div style="text-align:center"><a href="javascript:window.close()">[ ' . _('Close window') ." ]</a> <a href='$index'>[ " . _('Return') . ' ]</a></div>', 'title' => _('Report submitted!')));
} else {
header('Content-Type: text/json');
echo json_encode(array('success' => true));
}
} elseif (isset($_POST['post'])) {
if (!isset($_POST['body'], $_POST['board']))
} elseif (isset($_POST['post']) || $dropped_post) {
if (!isset($_POST['body'], $_POST['board']) && !$dropped_post)
error($config['error']['bot']);
$post = array('board' => $_POST['board'], 'files' => array());
@ -230,66 +414,72 @@ if (isset($_POST['delete'])) {
} else
$post['op'] = true;
// Check for CAPTCHA right after opening the board so the "return" link is in there
if ($config['recaptcha']) {
if (!isset($_POST['recaptcha_challenge_field']) || !isset($_POST['recaptcha_response_field']))
error($config['error']['bot']);
// Check what reCAPTCHA has to say...
$resp = recaptcha_check_answer($config['recaptcha_private'],
$_SERVER['REMOTE_ADDR'],
$_POST['recaptcha_challenge_field'],
$_POST['recaptcha_response_field']);
if (!$resp->is_valid) {
error($config['error']['captcha']);
if (!$dropped_post) {
// Check for CAPTCHA right after opening the board so the "return" link is in there
if ($config['recaptcha']) {
if (!isset($_POST['recaptcha_challenge_field']) || !isset($_POST['recaptcha_response_field']))
error($config['error']['bot']);
// Check what reCAPTCHA has to say...
$resp = recaptcha_check_answer($config['recaptcha_private'],
$_SERVER['REMOTE_ADDR'],
$_POST['recaptcha_challenge_field'],
$_POST['recaptcha_response_field']);
if (!$resp->is_valid) {
error($config['error']['captcha']);
}
}
}
if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) ||
(!$post['op'] && $_POST['post'] == $config['button_reply'])))
error($config['error']['bot']);
if (!(($post['op'] && $_POST['post'] == $config['button_newtopic']) ||
(!$post['op'] && $_POST['post'] == $config['button_reply'])))
error($config['error']['bot']);
// Check the referrer
if ($config['referer_match'] !== false &&
(!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER']))))
error($config['error']['referer']);
// Check the referrer
if ($config['referer_match'] !== false &&
(!isset($_SERVER['HTTP_REFERER']) || !preg_match($config['referer_match'], rawurldecode($_SERVER['HTTP_REFERER']))))
error($config['error']['referer']);
checkDNSBL();
checkDNSBL();
// Check if banned
checkBan($board['uri']);
// Check if banned
checkBan($board['uri']);
if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) {
require 'inc/mod/auth.php';
if (!$mod) {
// Liar. You're not a mod.
error($config['error']['notamod']);
}
if ($post['mod'] = isset($_POST['mod']) && $_POST['mod']) {
check_login(false);
if (!$mod) {
// Liar. You're not a mod.
error($config['error']['notamod']);
}
$post['sticky'] = $post['op'] && isset($_POST['sticky']);
$post['locked'] = $post['op'] && isset($_POST['lock']);
$post['raw'] = isset($_POST['raw']);
$post['sticky'] = $post['op'] && isset($_POST['sticky']);
$post['locked'] = $post['op'] && isset($_POST['lock']);
$post['raw'] = isset($_POST['raw']);
if ($post['sticky'] && !hasPermission($config['mod']['sticky'], $board['uri']))
error($config['error']['noaccess']);
if ($post['locked'] && !hasPermission($config['mod']['lock'], $board['uri']))
error($config['error']['noaccess']);
if ($post['raw'] && !hasPermission($config['mod']['rawhtml'], $board['uri']))
error($config['error']['noaccess']);
}
if ($post['sticky'] && !hasPermission($config['mod']['sticky'], $board['uri']))
error($config['error']['noaccess']);
if ($post['locked'] && !hasPermission($config['mod']['lock'], $board['uri']))
error($config['error']['noaccess']);
if ($post['raw'] && !hasPermission($config['mod']['rawhtml'], $board['uri']))
error($config['error']['noaccess']);
}
if (!$post['mod']) {
$post['antispam_hash'] = checkSpam(array($board['uri'], isset($post['thread']) ? $post['thread'] : ($config['try_smarter'] && isset($_POST['page']) ? 0 - (int)$_POST['page'] : null)));
if ($post['antispam_hash'] === true)
error($config['error']['spam']);
}
if (!$post['mod']) {
$post['antispam_hash'] = checkSpam(array($board['uri'], isset($post['thread']) ? $post['thread'] : ($config['try_smarter'] && isset($_POST['page']) ? 0 - (int)$_POST['page'] : null)));
if ($post['antispam_hash'] === true)
error($config['error']['spam']);
if ($config['robot_enable'] && $config['robot_mute']) {
checkMute();
}
}
if ($config['robot_enable'] && $config['robot_mute']) {
checkMute();
else {
$mod = $post['mod'] = false;
}
//Check if thread exists
if (!$post['op']) {
$query = prepare(sprintf("SELECT `sticky`,`locked`,`sage`,`slug` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL LIMIT 1", $board['uri']));
$query = prepare(sprintf("SELECT `sticky`,`locked`,`cycle`,`sage`,`slug` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL LIMIT 1", $board['uri']));
$query->bindValue(':id', $post['thread'], PDO::PARAM_INT);
$query->execute() or error(db_error());
@ -395,28 +585,36 @@ if (isset($_POST['delete'])) {
$post['email'] = str_replace(' ', '%20', htmlspecialchars($_POST['email']));
$post['body'] = $_POST['body'];
$post['password'] = $_POST['password'];
$post['has_file'] = (!isset($post['embed']) && (($post['op'] && !isset($post['no_longer_require_an_image_for_op']) && $config['force_image_op']) || !empty($_FILES['file']['name'])));
$post['has_file'] = (!isset($post['embed']) && (($post['op'] && !isset($post['no_longer_require_an_image_for_op']) && $config['force_image_op']) || count($_FILES) > 0));
if (!($post['has_file'] || isset($post['embed'])) || (($post['op'] && $config['force_body_op']) || (!$post['op'] && $config['force_body']))) {
$stripped_whitespace = preg_replace('/[\s]/u', '', $post['body']);
if ($stripped_whitespace == '') {
error($config['error']['tooshort_body']);
if (!$dropped_post) {
if (!($post['has_file'] || isset($post['embed'])) || (($post['op'] && $config['force_body_op']) || (!$post['op'] && $config['force_body']))) {
$stripped_whitespace = preg_replace('/[\s]/u', '', $post['body']);
if ($stripped_whitespace == '') {
error($config['error']['tooshort_body']);
}
}
}
if (!$post['op']) {
// Check if thread is locked
// but allow mods to post
if ($thread['locked'] && !hasPermission($config['mod']['postinlocked'], $board['uri']))
error($config['error']['locked']);
if (!$post['op']) {
// Check if thread is locked
// but allow mods to post
if ($thread['locked'] && !hasPermission($config['mod']['postinlocked'], $board['uri']))
error($config['error']['locked']);
$numposts = numPosts($post['thread']);
$numposts = numPosts($post['thread']);
if ($config['reply_hard_limit'] != 0 && $config['reply_hard_limit'] <= $numposts['replies'])
error($config['error']['reply_hard_limit']);
if ($config['reply_hard_limit'] != 0 && $config['reply_hard_limit'] <= $numposts['replies'])
error($config['error']['reply_hard_limit']);
if ($post['has_file'] && $config['image_hard_limit'] != 0 && $config['image_hard_limit'] <= $numposts['images'])
error($config['error']['image_hard_limit']);
if ($post['has_file'] && $config['image_hard_limit'] != 0 && $config['image_hard_limit'] <= $numposts['images'])
error($config['error']['image_hard_limit']);
}
}
else {
if (!$post['op']) {
$numposts = numPosts($post['thread']);
}
}
if ($post['has_file']) {
@ -466,7 +664,7 @@ if (isset($_POST['delete'])) {
$trip = generate_tripcode($post['name']);
$post['name'] = $trip[0];
$post['trip'] = isset($trip[1]) ? $trip[1] : '';
$post['trip'] = isset($trip[1]) ? $trip[1] : ''; // XX: Dropped posts and tripcodes
$noko = false;
if (strtolower($post['email']) == 'noko') {
@ -481,7 +679,7 @@ if (isset($_POST['delete'])) {
$i = 0;
foreach ($_FILES as $key => $file) {
if ($file['size'] && $file['tmp_name']) {
$file['filename'] = urldecode(get_magic_quotes_gpc() ? stripslashes($file['name']) : $file['name']);
$file['filename'] = urldecode($file['name']);
$file['extension'] = strtolower(mb_substr($file['filename'], mb_strrpos($file['filename'], '.') + 1));
if (isset($config['filename_func']))
$file['file_id'] = $config['filename_func']($file);
@ -501,15 +699,17 @@ if (isset($_POST['delete'])) {
if (empty($post['files'])) $post['has_file'] = false;
// Check for a file
if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) {
if (!$post['has_file'] && $config['force_image_op'])
error($config['error']['noimage']);
}
if (!$dropped_post) {
// Check for a file
if ($post['op'] && !isset($post['no_longer_require_an_image_for_op'])) {
if (!$post['has_file'] && $config['force_image_op'])
error($config['error']['noimage']);
}
// Check for too many files
if (sizeof($post['files']) > $config['max_images'])
error($config['error']['toomanyimages']);
// Check for too many files
if (sizeof($post['files']) > $config['max_images'])
error($config['error']['toomanyimages']);
}
if ($config['strip_combining_chars']) {
$post['name'] = strip_combining_chars($post['name']);
@ -518,18 +718,19 @@ if (isset($_POST['delete'])) {
$post['body'] = strip_combining_chars($post['body']);
}
// Check string lengths
if (mb_strlen($post['name']) > 35)
error(sprintf($config['error']['toolong'], 'name'));
if (mb_strlen($post['email']) > 40)
error(sprintf($config['error']['toolong'], 'email'));
if (mb_strlen($post['subject']) > 100)
error(sprintf($config['error']['toolong'], 'subject'));
if (!$mod && mb_strlen($post['body']) > $config['max_body'])
error($config['error']['toolong_body']);
if (mb_strlen($post['password']) > 20)
error(sprintf($config['error']['toolong'], 'password'));
if (!$dropped_post) {
// Check string lengths
if (mb_strlen($post['name']) > 35)
error(sprintf($config['error']['toolong'], 'name'));
if (mb_strlen($post['email']) > 40)
error(sprintf($config['error']['toolong'], 'email'));
if (mb_strlen($post['subject']) > 100)
error(sprintf($config['error']['toolong'], 'subject'));
if (!$mod && mb_strlen($post['body']) > $config['max_body'])
error($config['error']['toolong_body']);
if (mb_strlen($post['password']) > 20)
error(sprintf($config['error']['toolong'], 'password'));
}
wordfilters($post['body']);
$post['body'] = escape_markup_modifiers($post['body']);
@ -538,6 +739,7 @@ if (isset($_POST['delete'])) {
$post['body'] .= "\n<tinyboard raw html>1</tinyboard>";
}
if (!$dropped_post)
if (($config['country_flags'] && !$config['allow_no_country']) || ($config['country_flags'] && $config['allow_no_country'] && !isset($_POST['no_country']))) {
require 'inc/lib/geoip/geoip.inc';
$gi=geoip\geoip_open('inc/lib/geoip/GeoIPv6.dat', GEOIP_STANDARD);
@ -579,6 +781,7 @@ if (isset($_POST['delete'])) {
$post['body'] .= "\n<tinyboard tag>" . $_POST['tag'] . "</tinyboard>";
}
if (!$dropped_post)
if ($config['proxy_save'] && isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$proxy = preg_replace("/[^0-9a-fA-F.,: ]/", '', $_SERVER['HTTP_X_FORWARDED_FOR']);
$post['body'] .= "\n<tinyboard proxy>".$proxy."</tinyboard>";
@ -606,7 +809,12 @@ if (isset($_POST['delete'])) {
if ($post['has_file']) {
$fnarray = array();
$md5cmd = false;
if ($config['bsd_md5']) $md5cmd = '/sbin/md5 -r';
if ($config['gnu_md5']) $md5cmd = 'md5sum';
$allhashes = '';
foreach ($post['files'] as $key => &$file) {
if ($post['op'] && $config['allowed_ext_op']) {
if (!in_array($file['extension'], $config['allowed_ext_op']))
@ -620,51 +828,48 @@ if (isset($_POST['delete'])) {
// Truncate filename if it is too long
$file['filename'] = mb_substr($file['filename'], 0, $config['max_filename_len']);
if (!isset($filenames)) {
$filenames = escapeshellarg($file['tmp_name']);
} else {
$filenames .= (' ' . escapeshellarg($file['tmp_name']));
}
$fnarray[] = $file['tmp_name'];
$upload = $file['tmp_name'];
if (!is_readable($upload))
error($config['error']['nomove']);
if ($md5cmd) {
$output = shell_exec_error($md5cmd . " " . escapeshellarg($upload));
$output = explode(' ', $output);
$hash = $output[0];
}
else {
$hash = md5_file($upload);
}
$file['hash'] = $hash;
$allhashes .= $hash;
}
$md5cmd = $config['bsd_md5'] ? 'md5 -r' : 'md5sum';
if (!$config['php_md5'] && $output = shell_exec_error("cat $filenames | $md5cmd")) {
$explodedvar = explode(' ', $output);
$hash = $explodedvar[0];
if (count ($post['files']) == 1) {
$post['filehash'] = $hash;
} elseif ($config['max_images'] === 1) {
$post['filehash'] = md5_file($upload);
} else {
$str_to_hash = '';
foreach ($fnarray as $i => $f) {
$str_to_hash .= file_get_contents($f);
}
$post['filehash'] = md5($str_to_hash);
}
else {
$post['filehash'] = md5($allhashes);
}
}
if (!hasPermission($config['mod']['bypass_filters'], $board['uri'])) {
require_once 'inc/filters.php';
if (!hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) {
require_once 'inc/filters.php';
do_filters($post);
}
if ($post['has_file']) {
if ($post['has_file']) {
foreach ($post['files'] as $key => &$file) {
if ($file['is_an_image'] && $config['ie_mime_type_detection'] !== false) {
// Check IE MIME type detection XSS exploit
$buffer = file_get_contents($upload, null, null, null, 255);
if (preg_match($config['ie_mime_type_detection'], $buffer)) {
undoImage($post);
error($config['error']['mime_exploit']);
if ($file['is_an_image']) {
if ($config['ie_mime_type_detection'] !== false) {
// Check IE MIME type detection XSS exploit
$buffer = file_get_contents($upload, null, null, null, 255);
if (preg_match($config['ie_mime_type_detection'], $buffer)) {
undoImage($post);
error($config['error']['mime_exploit']);
}
}
require_once 'inc/image.php';
@ -673,6 +878,9 @@ if (isset($_POST['delete'])) {
if (!$size = @getimagesize($file['tmp_name'])) {
error($config['error']['invalidimg']);
}
if (!in_array($size[2], array(IMAGETYPE_PNG, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_BMP))) {
error($config['error']['invalidimg']);
}
if ($size[0] > $config['max_width'] || $size[1] > $config['max_height']) {
error($config['error']['maxsize']);
}
@ -780,6 +988,34 @@ if (isset($_POST['delete'])) {
$file['thumbwidth'] = $size[0];
$file['thumbheight'] = $size[1];
}
if ($config['tesseract_ocr'] && $file['thumb'] != 'file') { // Let's OCR it!
$fname = $file['tmp_name'];
if ($file['height'] > 500 || $file['width'] > 500) {
$fname = $file['thumb'];
}
if ($fname == 'spoiler') { // We don't have that much CPU time, do we?
}
else {
$tmpname = "tmp/tesseract/".rand(0,10000000);
// Preprocess command is an ImageMagick b/w quantization
$error = shell_exec_error(sprintf($config['tesseract_preprocess_command'], escapeshellarg($fname)) . " | " .
'tesseract stdin '.escapeshellarg($tmpname).' '.$config['tesseract_params']);
$tmpname .= ".txt";
$value = @file_get_contents($tmpname);
@unlink($tmpname);
if ($value && trim($value)) {
// This one has an effect, that the body is appended to a post body. So you can write a correct
// spamfilter.
$post['body_nomarkup'] .= "<tinyboard ocr image $key>".htmlspecialchars($value)."</tinyboard>";
}
}
}
if (!isset($dont_copy_file) || !$dont_copy_file) {
if (isset($file['file_tmp'])) {
@ -820,7 +1056,12 @@ if (isset($_POST['delete'])) {
}
}
if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup'])) {
// Do filters again if OCRing
if ($config['tesseract_ocr'] && !hasPermission($config['mod']['bypass_filters'], $board['uri']) && !$dropped_post) {
do_filters($post);
}
if (!hasPermission($config['mod']['postunoriginal'], $board['uri']) && $config['robot_enable'] && checkRobot($post['body_nomarkup']) && !$dropped_post) {
undoImage($post);
if ($config['robot_mute']) {
error(sprintf($config['error']['muted'], mute()));
@ -859,7 +1100,52 @@ if (isset($_POST['delete'])) {
$post['id'] = $id = post($post);
$post['slug'] = slugify($post);
if ($dropped_post && $dropped_post['from_nntp']) {
$query = prepare("INSERT INTO ``nntp_references`` (`board`, `id`, `message_id`, `message_id_digest`, `own`, `headers`) VALUES ".
"(:board , :id , :message_id , :message_id_digest , false, :headers)");
$query->bindValue(':board', $dropped_post['board']);
$query->bindValue(':id', $id);
$query->bindValue(':message_id', $dropped_post['msgid']);
$query->bindValue(':message_id_digest', sha1($dropped_post['msgid']));
$query->bindValue(':headers', $dropped_post['headers']);
$query->execute() or error(db_error($query));
} // ^^^^^ For inbound posts ^^^^^
elseif ($config['nntpchan']['enabled'] && $config['nntpchan']['group']) {
// vvvvv For outbound posts vvvvv
require_once('inc/nntpchan/nntpchan.php');
$msgid = gen_msgid($post['board'], $post['id']);
list($headers, $files) = post2nntp($post, $msgid);
$message = gen_nntp($headers, $files);
$query = prepare("INSERT INTO ``nntp_references`` (`board`, `id`, `message_id`, `message_id_digest`, `own`, `headers`) VALUES ".
"(:board , :id , :message_id , :message_id_digest , true , :headers)");
$query->bindValue(':board', $post['board']);
$query->bindValue(':id', $post['id']);
$query->bindValue(':message_id', $msgid);
$query->bindValue(':message_id_digest', sha1($msgid));
$query->bindValue(':headers', json_encode($headers));
$query->execute() or error(db_error($query));
// Let's broadcast it!
nntp_publish($message, $msgid);
}
insertFloodPost($post);
// Handle cyclical threads
if (!$post['op'] && isset($thread['cycle']) && $thread['cycle']) {
// Query is a bit weird due to "This version of MariaDB doesn't yet support 'LIMIT & IN/ALL/ANY/SOME subquery'" (MariaDB Ver 15.1 Distrib 10.0.17-MariaDB, for Linux (x86_64))
$query = prepare(sprintf('DELETE FROM ``posts_%s`` WHERE `thread` = :thread AND `id` NOT IN (SELECT `id` FROM (SELECT `id` FROM ``posts_%s`` WHERE `thread` = :thread ORDER BY `id` DESC LIMIT :limit) i)', $board['uri'], $board['uri']));
$query->bindValue(':thread', $post['thread']);
$query->bindValue(':limit', $config['cycle_limit'], PDO::PARAM_INT);
$query->execute() or error(db_error($query));
}
if (isset($post['antispam_hash'])) {
incrementSpamHash($post['antispam_hash']);
@ -939,7 +1225,7 @@ if (isset($_POST['delete'])) {
$build_pages = range(1, $config['max_pages']);
if ($post['op'])
clean();
clean($id);
event('post-after', $post);

19
report.php

@ -0,0 +1,19 @@
<?php
include 'inc/functions.php';
$global = isset($_GET['global']);
$post = (isset($_GET['post']) ? $_GET['post'] : false);
$board = (isset($_GET['board']) ? $_GET['board'] : false);
if (!$post || !preg_match('/^delete_\d+$/', $post) || !$board || !openBoard($board)) {
header('HTTP/1.1 400 Bad Request');
error(_('Bad request.'));
}
if ($config['report_captcha']) {
$captcha = generate_captcha($config['captcha']['extra']);
} else {
$captcha = null;
}
$body = Element('report.html', ['global' => $global, 'post' => $post, 'board' => $board, 'captcha' => $captcha, 'config' => $config]);
echo Element('page.html', ['config' => $config, 'body' => $body]);

162
smart_build.php

@ -1,163 +1,30 @@
<?php
require_once("inc/functions.php");
require_once("inc/route.php");
require_once("inc/controller.php");
if (!$config['smart_build']) {
die('You need to enable $config["smart_build"]');
if (!$config["smart_build_helper"]) {
die('You need to enable $config["smart_build_helper"]');
}
$config['smart_build'] = false; // Let's disable it, so we can build the page for real
$config['generation_strategies'] = array('strategy_immediate');
function after_open_board() { global $config;
$config['smart_build'] = false;
$config['generation_strategies'] = array('strategy_immediate');
};
function sb_board($b, $page = 1) { global $config, $build_pages; $page = (int)$page;
if ($page < 1) return false;
if (!openBoard($b)) return false;
if ($page > $config['max_pages']) return false;
$config['try_smarter'] = true;
$build_pages = array($page);
buildIndex("skip");
return true;
}
function sb_api_board($b, $page = 0) { $page = (int)$page;
return sb_board($b, $page + 1);
}
function sb_thread($b, $thread, $slugcheck = false) { global $config; $thread = (int)$thread;
if ($thread < 1) return false;
if (!preg_match('/^'.$config['board_regex'].'$/u', $b)) return false;
if (Cache::get("thread_exists_".$b."_".$thread) == "no") return false;
$query = prepare(sprintf("SELECT MAX(`id`) AS `max` FROM ``posts_%s``", $b));
if (!$query->execute()) return false;
$s = $query->fetch(PDO::FETCH_ASSOC);
$max = $s['max'];
if ($thread > $max) return false;
$query = prepare(sprintf("SELECT `id` FROM ``posts_%s`` WHERE `id` = :id AND `thread` IS NULL", $b));
$query->bindValue(':id', $thread);
if (!$query->execute() || !$query->fetch(PDO::FETCH_ASSOC) ) {
Cache::set("thread_exists_".$b."_".$thread, "no");
return false;
}
if ($slugcheck && $config['slugify']) {
global $request;
$link = link_for(array("id" => $thread), $slugcheck === 50, array("uri" => $b));
$link = "/".$b."/".$config['dir']['res'].$link;
if ($link != $request) {
header("Location: $link", true, 301);
die();
}
}
if ($slugcheck == 50) { // Should we really generate +50 page? Maybe there are not enough posts anyway
global $request;
$r = str_replace("+50", "", $request);
$r = substr($r, 1); // Cut the slash
if (file_exists($r)) return false;
}
if (!openBoard($b)) return false;
buildThread($thread);
return true;
}
function sb_thread_slugcheck($b, $thread) {
return sb_thread($b, $thread, true);
}
function sb_thread_slugcheck50($b, $thread) {
return sb_thread($b, $thread, 50);
}
function sb_api($b) { global $config, $build_pages;
if (!openBoard($b)) return false;
$config['try_smarter'] = true;
$build_pages = array(-1);
buildIndex();
return true;
}
function sb_ukko() {
rebuildTheme("ukko", "post-thread");
return true;
}
function sb_catalog($b) {
if (!openBoard($b)) return false;
rebuildTheme("catalog", "post-thread", $b);
return true;
}
function sb_recent() {
rebuildTheme("recent", "post-thread");
return true;
}
function sb_sitemap() {
rebuildTheme("sitemap", "all");
return true;
}
$entrypoints = array();
$entrypoints['/%b/'] = 'sb_board';
$entrypoints['/%b/'.$config['file_index']] = 'sb_board';
$entrypoints['/%b/'.$config['file_page']] = 'sb_board';
$entrypoints['/%b/%d.json'] = 'sb_api_board';
if ($config['api']['enabled']) {
$entrypoints['/%b/threads.json'] = 'sb_api';
$entrypoints['/%b/catalog.json'] = 'sb_api';
}
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page']] = 'sb_thread_slugcheck';
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page50']] = 'sb_thread_slugcheck50';
if ($config['slugify']) {
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page_slug']] = 'sb_thread_slugcheck';
$entrypoints['/%b/'.$config['dir']['res'].$config['file_page50_slug']] = 'sb_thread_slugcheck50';
}
if ($config['api']['enabled']) {
$entrypoints['/%b/'.$config['dir']['res'].'%d.json'] = 'sb_thread';
}
$entrypoints['/*/'] = 'sb_ukko';
$entrypoints['/*/index.html'] = 'sb_ukko';
$entrypoints['/recent.html'] = 'sb_recent';
$entrypoints['/%b/catalog.html'] = 'sb_catalog';
$entrypoints['/sitemap.xml'] = 'sb_sitemap';
$reached = false;
$request = $_SERVER['REQUEST_URI'];
list($request) = explode('?', $request);
foreach ($entrypoints as $id => $fun) {
$id = '@^' . preg_quote($id, '@') . '$@u';
$id = str_replace('%b', '('.$config['board_regex'].')', $id);
$id = str_replace('%d', '([0-9]+)', $id);
$id = str_replace('%s', '[a-zA-Z0-9-]+', $id);
$matches = null;
$route = route($request);
if (preg_match ($id, $request, $matches)) {
array_shift($matches);
$reached = call_user_func_array($fun, $matches);
break;
}
if (!$route) {
$reached = false;
}
else {
list ($fun, $args) = $route;
$reached = call_user_func_array($fun, $args);
}
function die_404() { global $config;
@ -194,6 +61,9 @@ if ($reached) {
elseif (preg_match('/\.xml$/', $request)) {
header("Content-Type", "application/xml");
}
elseif (preg_match('/\.rss$/', $request)) {
header("Content-Type", "application/rss+xml");
}
else {
header("Content-Type", "text/html; charset=utf-8");
}

168
stylesheets/font-awesome/css/font-awesome.css

@ -1,13 +1,13 @@
/*!
* Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome
* Font Awesome 4.6.1 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
/* FONT PATH
* -------------------------- */
@font-face {
font-family: 'FontAwesome';
src: url('../fonts/fontawesome-webfont.eot?v=4.4.0');
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.4.0') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.4.0') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.4.0') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.4.0') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.4.0#fontawesomeregular') format('svg');
src: url('../fonts/fontawesome-webfont.eot?v=4.6.1');
src: url('../fonts/fontawesome-webfont.eot?#iefix&v=4.6.1') format('embedded-opentype'), url('../fonts/fontawesome-webfont.woff2?v=4.6.1') format('woff2'), url('../fonts/fontawesome-webfont.woff?v=4.6.1') format('woff'), url('../fonts/fontawesome-webfont.ttf?v=4.6.1') format('truetype'), url('../fonts/fontawesome-webfont.svg?v=4.6.1#fontawesomeregular') format('svg');
font-weight: normal;
font-style: normal;
}
@ -118,31 +118,31 @@
}
}
.fa-rotate-90 {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=1);
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=1)";
-webkit-transform: rotate(90deg);
-ms-transform: rotate(90deg);
transform: rotate(90deg);
}
.fa-rotate-180 {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2);
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2)";
-webkit-transform: rotate(180deg);
-ms-transform: rotate(180deg);
transform: rotate(180deg);
}
.fa-rotate-270 {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=3);
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=3)";
-webkit-transform: rotate(270deg);
-ms-transform: rotate(270deg);
transform: rotate(270deg);
}
.fa-flip-horizontal {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1)";
-webkit-transform: scale(-1, 1);
-ms-transform: scale(-1, 1);
transform: scale(-1, 1);
}
.fa-flip-vertical {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1)";
-webkit-transform: scale(1, -1);
-ms-transform: scale(1, -1);
transform: scale(1, -1);
@ -2024,3 +2024,155 @@
.fa-fonticons:before {
content: "\f280";
}
.fa-reddit-alien:before {
content: "\f281";
}
.fa-edge:before {
content: "\f282";
}
.fa-credit-card-alt:before {
content: "\f283";
}
.fa-codiepie:before {
content: "\f284";
}
.fa-modx:before {
content: "\f285";
}
.fa-fort-awesome:before {
content: "\f286";
}
.fa-usb:before {
content: "\f287";
}
.fa-product-hunt:before {
content: "\f288";
}
.fa-mixcloud:before {
content: "\f289";
}
.fa-scribd:before {
content: "\f28a";
}
.fa-pause-circle:before {
content: "\f28b";
}
.fa-pause-circle-o:before {
content: "\f28c";
}
.fa-stop-circle:before {
content: "\f28d";
}
.fa-stop-circle-o:before {
content: "\f28e";
}
.fa-shopping-bag:before {
content: "\f290";
}
.fa-shopping-basket:before {
content: "\f291";
}
.fa-hashtag:before {
content: "\f292";
}
.fa-bluetooth:before {
content: "\f293";
}
.fa-bluetooth-b:before {
content: "\f294";
}
.fa-percent:before {
content: "\f295";
}
.fa-gitlab:before {
content: "\f296";
}
.fa-wpbeginner:before {
content: "\f297";
}
.fa-wpforms:before {
content: "\f298";
}
.fa-envira:before {
content: "\f299";
}
.fa-universal-access:before {
content: "\f29a";
}
.fa-wheelchair-alt:before {
content: "\f29b";
}
.fa-question-circle-o:before {
content: "\f29c";
}
.fa-blind:before {
content: "\f29d";
}
.fa-audio-description:before {
content: "\f29e";
}
.fa-volume-control-phone:before {
content: "\f2a0";
}
.fa-braille:before {
content: "\f2a1";
}
.fa-assistive-listening-systems:before {
content: "\f2a2";
}
.fa-asl-interpreting:before,
.fa-american-sign-language-interpreting:before {
content: "\f2a3";
}
.fa-deafness:before,
.fa-hard-of-hearing:before,
.fa-deaf:before {
content: "\f2a4";
}
.fa-glide:before {
content: "\f2a5";
}
.fa-glide-g:before {
content: "\f2a6";
}
.fa-signing:before,
.fa-sign-language:before {
content: "\f2a7";
}
.fa-low-vision:before {
content: "\f2a8";
}
.fa-viadeo:before {
content: "\f2a9";
}
.fa-viadeo-square:before {
content: "\f2aa";
}
.fa-snapchat:before {
content: "\f2ab";
}
.fa-snapchat-ghost:before {
content: "\f2ac";
}
.fa-snapchat-square:before {
content: "\f2ad";
}
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
border: 0;
}
.sr-only-focusable:active,
.sr-only-focusable:focus {
position: static;
width: auto;
height: auto;
margin: 0;
overflow: visible;
clip: auto;
}

4
stylesheets/font-awesome/css/font-awesome.min.css

File diff suppressed because one or more lines are too long

BIN
stylesheets/font-awesome/fonts/FontAwesome.otf

Binary file not shown.

BIN
stylesheets/font-awesome/fonts/fontawesome-webfont.eot

Binary file not shown.

99
stylesheets/font-awesome/fonts/fontawesome-webfont.svg

@ -1,6 +1,6 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1">
<svg xmlns="http://www.w3.org/2000/svg">
<metadata></metadata>
<defs>
<font id="fontawesomeregular" horiz-adv-x="1536" >
@ -169,7 +169,7 @@
<glyph unicode="&#xf08e;" horiz-adv-x="1792" d="M1408 608v-320q0 -119 -84.5 -203.5t-203.5 -84.5h-832q-119 0 -203.5 84.5t-84.5 203.5v832q0 119 84.5 203.5t203.5 84.5h704q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-704q-66 0 -113 -47t-47 -113v-832q0 -66 47 -113t113 -47h832q66 0 113 47t47 113v320 q0 14 9 23t23 9h64q14 0 23 -9t9 -23zM1792 1472v-512q0 -26 -19 -45t-45 -19t-45 19l-176 176l-652 -652q-10 -10 -23 -10t-23 10l-114 114q-10 10 -10 23t10 23l652 652l-176 176q-19 19 -19 45t19 45t45 19h512q26 0 45 -19t19 -45z" />
<glyph unicode="&#xf090;" d="M1184 640q0 -26 -19 -45l-544 -544q-19 -19 -45 -19t-45 19t-19 45v288h-448q-26 0 -45 19t-19 45v384q0 26 19 45t45 19h448v288q0 26 19 45t45 19t45 -19l544 -544q19 -19 19 -45zM1536 992v-704q0 -119 -84.5 -203.5t-203.5 -84.5h-320q-13 0 -22.5 9.5t-9.5 22.5 q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q66 0 113 47t47 113v704q0 66 -47 113t-113 47h-288h-11h-13t-11.5 1t-11.5 3t-8 5.5t-7 9t-2 13.5q0 4 -1 20t-0.5 26.5t3 23.5t10 19.5t20.5 6.5h320q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf091;" horiz-adv-x="1664" d="M458 653q-74 162 -74 371h-256v-96q0 -78 94.5 -162t235.5 -113zM1536 928v96h-256q0 -209 -74 -371q141 29 235.5 113t94.5 162zM1664 1056v-128q0 -71 -41.5 -143t-112 -130t-173 -97.5t-215.5 -44.5q-42 -54 -95 -95q-38 -34 -52.5 -72.5t-14.5 -89.5q0 -54 30.5 -91 t97.5 -37q75 0 133.5 -45.5t58.5 -114.5v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23v64q0 69 58.5 114.5t133.5 45.5q67 0 97.5 37t30.5 91q0 51 -14.5 89.5t-52.5 72.5q-53 41 -95 95q-113 5 -215.5 44.5t-173 97.5t-112 130t-41.5 143v128q0 40 28 68t68 28h288v96 q0 66 47 113t113 47h576q66 0 113 -47t47 -113v-96h288q40 0 68 -28t28 -68z" />
<glyph unicode="&#xf092;" d="M394 184q-8 -9 -20 3q-13 11 -4 19q8 9 20 -3q12 -11 4 -19zM352 245q9 -12 0 -19q-8 -6 -17 7t0 18q9 7 17 -6zM291 305q-5 -7 -13 -2q-10 5 -7 12q3 5 13 2q10 -5 7 -12zM322 271q-6 -7 -16 3q-9 11 -2 16q6 6 16 -3q9 -11 2 -16zM451 159q-4 -12 -19 -6q-17 4 -13 15 t19 7q16 -5 13 -16zM514 154q0 -11 -16 -11q-17 -2 -17 11q0 11 16 11q17 2 17 -11zM572 164q2 -10 -14 -14t-18 8t14 15q16 2 18 -9zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-224q-16 0 -24.5 1t-19.5 5t-16 14.5t-5 27.5v239q0 97 -52 142q57 6 102.5 18t94 39 t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103 q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -103t0.5 -68q0 -22 -11 -33.5t-22 -13t-33 -1.5 h-224q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf092;" d="M519 336q4 6 -3 13q-9 7 -14 2q-4 -6 3 -13q9 -7 14 -2zM491 377q-5 7 -12 4q-6 -4 0 -12q7 -8 12 -5q6 4 0 13zM450 417q2 4 -5 8q-7 2 -8 -2q-3 -5 4 -8q8 -2 9 2zM471 394q2 1 1.5 4.5t-3.5 5.5q-6 7 -10 3t1 -11q6 -6 11 -2zM557 319q2 7 -9 11q-9 3 -13 -4 q-2 -7 9 -11q9 -3 13 4zM599 316q0 8 -12 8q-10 0 -10 -8t11 -8t11 8zM638 323q-2 7 -13 5t-9 -9q2 -8 12 -6t10 10zM1280 640q0 212 -150 362t-362 150t-362 -150t-150 -362q0 -167 98 -300.5t252 -185.5q18 -3 26.5 5t8.5 20q0 52 -1 95q-6 -1 -15.5 -2.5t-35.5 -2t-48 4 t-43.5 20t-29.5 41.5q-23 59 -57 74q-2 1 -4.5 3.5l-8 8t-7 9.5t4 7.5t19.5 3.5q6 0 15 -2t30 -15.5t33 -35.5q16 -28 37.5 -42t43.5 -14t38 3.5t30 9.5q7 47 33 69q-49 6 -86 18.5t-73 39t-55.5 76t-19.5 119.5q0 79 53 137q-24 62 5 136q19 6 54.5 -7.5t60.5 -29.5l26 -16 q58 17 128 17t128 -17q11 7 28.5 18t55.5 26t57 9q29 -74 5 -136q53 -58 53 -137q0 -57 -14 -100.5t-35.5 -70t-53.5 -44.5t-62.5 -26t-68.5 -12q35 -31 35 -95q0 -40 -0.5 -89t-0.5 -51q0 -12 8.5 -20t26.5 -5q154 52 252 185.5t98 300.5zM1536 1120v-960 q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf093;" horiz-adv-x="1664" d="M1280 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1536 64q0 26 -19 45t-45 19t-45 -19t-19 -45t19 -45t45 -19t45 19t19 45zM1664 288v-320q0 -40 -28 -68t-68 -28h-1472q-40 0 -68 28t-28 68v320q0 40 28 68t68 28h427q21 -56 70.5 -92 t110.5 -36h256q61 0 110.5 36t70.5 92h427q40 0 68 -28t28 -68zM1339 936q-17 -40 -59 -40h-256v-448q0 -26 -19 -45t-45 -19h-256q-26 0 -45 19t-19 45v448h-256q-42 0 -59 40q-17 39 14 69l448 448q18 19 45 19t45 -19l448 -448q31 -30 14 -69z" />
<glyph unicode="&#xf094;" d="M1407 710q0 44 -7 113.5t-18 96.5q-12 30 -17 44t-9 36.5t-4 48.5q0 23 5 68.5t5 67.5q0 37 -10 55q-4 1 -13 1q-19 0 -58 -4.5t-59 -4.5q-60 0 -176 24t-175 24q-43 0 -94.5 -11.5t-85 -23.5t-89.5 -34q-137 -54 -202 -103q-96 -73 -159.5 -189.5t-88 -236t-24.5 -248.5 q0 -40 12.5 -120t12.5 -121q0 -23 -11 -66.5t-11 -65.5t12 -36.5t34 -14.5q24 0 72.5 11t73.5 11q57 0 169.5 -15.5t169.5 -15.5q181 0 284 36q129 45 235.5 152.5t166 245.5t59.5 275zM1535 712q0 -165 -70 -327.5t-196 -288t-281 -180.5q-124 -44 -326 -44 q-57 0 -170 14.5t-169 14.5q-24 0 -72.5 -14.5t-73.5 -14.5q-73 0 -123.5 55.5t-50.5 128.5q0 24 11 68t11 67q0 40 -12.5 120.5t-12.5 121.5q0 111 18 217.5t54.5 209.5t100.5 194t150 156q78 59 232 120q194 78 316 78q60 0 175.5 -24t173.5 -24q19 0 57 5t58 5 q81 0 118 -50.5t37 -134.5q0 -23 -5 -68t-5 -68q0 -10 1 -18.5t3 -17t4 -13.5t6.5 -16t6.5 -17q16 -40 25 -118.5t9 -136.5z" />
<glyph unicode="&#xf095;" horiz-adv-x="1408" d="M1408 296q0 -27 -10 -70.5t-21 -68.5q-21 -50 -122 -106q-94 -51 -186 -51q-27 0 -52.5 3.5t-57.5 12.5t-47.5 14.5t-55.5 20.5t-49 18q-98 35 -175 83q-128 79 -264.5 215.5t-215.5 264.5q-48 77 -83 175q-3 9 -18 49t-20.5 55.5t-14.5 47.5t-12.5 57.5t-3.5 52.5 q0 92 51 186q56 101 106 122q25 11 68.5 21t70.5 10q14 0 21 -3q18 -6 53 -76q11 -19 30 -54t35 -63.5t31 -53.5q3 -4 17.5 -25t21.5 -35.5t7 -28.5q0 -20 -28.5 -50t-62 -55t-62 -53t-28.5 -46q0 -9 5 -22.5t8.5 -20.5t14 -24t11.5 -19q76 -137 174 -235t235 -174 q2 -1 19 -11.5t24 -14t20.5 -8.5t22.5 -5q18 0 46 28.5t53 62t55 62t50 28.5q14 0 28.5 -7t35.5 -21.5t25 -17.5q25 -15 53.5 -31t63.5 -35t54 -30q70 -35 76 -53q3 -7 3 -21z" />
@ -178,7 +178,7 @@
<glyph unicode="&#xf098;" d="M1280 343q0 11 -2 16q-3 8 -38.5 29.5t-88.5 49.5l-53 29q-5 3 -19 13t-25 15t-21 5q-18 0 -47 -32.5t-57 -65.5t-44 -33q-7 0 -16.5 3.5t-15.5 6.5t-17 9.5t-14 8.5q-99 55 -170.5 126.5t-126.5 170.5q-2 3 -8.5 14t-9.5 17t-6.5 15.5t-3.5 16.5q0 13 20.5 33.5t45 38.5 t45 39.5t20.5 36.5q0 10 -5 21t-15 25t-13 19q-3 6 -15 28.5t-25 45.5t-26.5 47.5t-25 40.5t-16.5 18t-16 2q-48 0 -101 -22q-46 -21 -80 -94.5t-34 -130.5q0 -16 2.5 -34t5 -30.5t9 -33t10 -29.5t12.5 -33t11 -30q60 -164 216.5 -320.5t320.5 -216.5q6 -2 30 -11t33 -12.5 t29.5 -10t33 -9t30.5 -5t34 -2.5q57 0 130.5 34t94.5 80q22 53 22 101zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf099;" horiz-adv-x="1664" d="M1620 1128q-67 -98 -162 -167q1 -14 1 -42q0 -130 -38 -259.5t-115.5 -248.5t-184.5 -210.5t-258 -146t-323 -54.5q-271 0 -496 145q35 -4 78 -4q225 0 401 138q-105 2 -188 64.5t-114 159.5q33 -5 61 -5q43 0 85 11q-112 23 -185.5 111.5t-73.5 205.5v4q68 -38 146 -41 q-66 44 -105 115t-39 154q0 88 44 163q121 -149 294.5 -238.5t371.5 -99.5q-8 38 -8 74q0 134 94.5 228.5t228.5 94.5q140 0 236 -102q109 21 205 78q-37 -115 -142 -178q93 10 186 50z" />
<glyph unicode="&#xf09a;" horiz-adv-x="1024" d="M959 1524v-264h-157q-86 0 -116 -36t-30 -108v-189h293l-39 -296h-254v-759h-306v759h-255v296h255v218q0 186 104 288.5t277 102.5q147 0 228 -12z" />
<glyph unicode="&#xf09b;" d="M1536 640q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -39.5 7t-12.5 30v211q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 121 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-86 13.5 q-44 -113 -7 -204q-79 -85 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-40 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23 q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -89t0.5 -54q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
<glyph unicode="&#xf09b;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5q0 -251 -146.5 -451.5t-378.5 -277.5q-27 -5 -40 7t-13 30q0 3 0.5 76.5t0.5 134.5q0 97 -52 142q57 6 102.5 18t94 39t81 66.5t53 105t20.5 150.5q0 119 -79 206q37 91 -8 204q-28 9 -81 -11t-92 -44l-38 -24 q-93 26 -192 26t-192 -26q-16 11 -42.5 27t-83.5 38.5t-85 13.5q-45 -113 -8 -204q-79 -87 -79 -206q0 -85 20.5 -150t52.5 -105t80.5 -67t94 -39t102.5 -18q-39 -36 -49 -103q-21 -10 -45 -15t-57 -5t-65.5 21.5t-55.5 62.5q-19 32 -48.5 52t-49.5 24l-20 3q-21 0 -29 -4.5 t-5 -11.5t9 -14t13 -12l7 -5q22 -10 43.5 -38t31.5 -51l10 -23q13 -38 44 -61.5t67 -30t69.5 -7t55.5 3.5l23 4q0 -38 0.5 -88.5t0.5 -54.5q0 -18 -13 -30t-40 -7q-232 77 -378.5 277.5t-146.5 451.5q0 209 103 385.5t279.5 279.5t385.5 103zM291 305q3 7 -7 12 q-10 3 -13 -2q-3 -7 7 -12q9 -6 13 2zM322 271q7 5 -2 16q-10 9 -16 3q-7 -5 2 -16q10 -10 16 -3zM352 226q9 7 0 19q-8 13 -17 6q-9 -5 0 -18t17 -7zM394 184q8 8 -4 19q-12 12 -20 3q-9 -8 4 -19q12 -12 20 -3zM451 159q3 11 -13 16q-15 4 -19 -7t13 -15q15 -6 19 6z M514 154q0 13 -17 11q-16 0 -16 -11q0 -13 17 -11q16 0 16 11zM572 164q-2 11 -18 9q-16 -3 -14 -15t18 -8t14 14z" />
<glyph unicode="&#xf09c;" horiz-adv-x="1664" d="M1664 960v-256q0 -26 -19 -45t-45 -19h-64q-26 0 -45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-192h96q40 0 68 -28t28 -68v-576q0 -40 -28 -68t-68 -28h-960q-40 0 -68 28t-28 68v576q0 40 28 68t68 28h672v192q0 185 131.5 316.5t316.5 131.5 t316.5 -131.5t131.5 -316.5z" />
<glyph unicode="&#xf09d;" horiz-adv-x="1920" d="M1760 1408q66 0 113 -47t47 -113v-1216q0 -66 -47 -113t-113 -47h-1600q-66 0 -113 47t-47 113v1216q0 66 47 113t113 47h1600zM160 1280q-13 0 -22.5 -9.5t-9.5 -22.5v-224h1664v224q0 13 -9.5 22.5t-22.5 9.5h-1600zM1760 0q13 0 22.5 9.5t9.5 22.5v608h-1664v-608 q0 -13 9.5 -22.5t22.5 -9.5h1600zM256 128v128h256v-128h-256zM640 128v128h384v-128h-384z" />
<glyph unicode="&#xf09e;" horiz-adv-x="1408" d="M384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 69q2 -28 -17 -48q-18 -21 -47 -21h-135q-25 0 -43 16.5t-20 41.5q-22 229 -184.5 391.5t-391.5 184.5q-25 2 -41.5 20t-16.5 43v135q0 29 21 47q17 17 43 17h5q160 -13 306 -80.5 t259 -181.5q114 -113 181.5 -259t80.5 -306zM1408 67q2 -27 -18 -47q-18 -20 -46 -20h-143q-26 0 -44.5 17.5t-19.5 42.5q-12 215 -101 408.5t-231.5 336t-336 231.5t-408.5 102q-25 1 -42.5 19.5t-17.5 43.5v143q0 28 20 46q18 18 44 18h3q262 -13 501.5 -120t425.5 -294 q187 -186 294 -425.5t120 -501.5z" />
@ -219,8 +219,8 @@
<glyph unicode="&#xf0d1;" horiz-adv-x="1792" d="M640 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM256 640h384v256h-158q-13 0 -22 -9l-195 -195q-9 -9 -9 -22v-30zM1536 128q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1792 1216v-1024q0 -15 -4 -26.5t-13.5 -18.5 t-16.5 -11.5t-23.5 -6t-22.5 -2t-25.5 0t-22.5 0.5q0 -106 -75 -181t-181 -75t-181 75t-75 181h-384q0 -106 -75 -181t-181 -75t-181 75t-75 181h-64q-3 0 -22.5 -0.5t-25.5 0t-22.5 2t-23.5 6t-16.5 11.5t-13.5 18.5t-4 26.5q0 26 19 45t45 19v320q0 8 -0.5 35t0 38 t2.5 34.5t6.5 37t14 30.5t22.5 30l198 198q19 19 50.5 32t58.5 13h160v192q0 26 19 45t45 19h1024q26 0 45 -19t19 -45z" />
<glyph unicode="&#xf0d2;" d="M1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103q-111 0 -218 32q59 93 78 164q9 34 54 211q20 -39 73 -67.5t114 -28.5q121 0 216 68.5t147 188.5t52 270q0 114 -59.5 214t-172.5 163t-255 63q-105 0 -196 -29t-154.5 -77t-109 -110.5t-67 -129.5t-21.5 -134 q0 -104 40 -183t117 -111q30 -12 38 20q2 7 8 31t8 30q6 23 -11 43q-51 61 -51 151q0 151 104.5 259.5t273.5 108.5q151 0 235.5 -82t84.5 -213q0 -170 -68.5 -289t-175.5 -119q-61 0 -98 43.5t-23 104.5q8 35 26.5 93.5t30 103t11.5 75.5q0 50 -27 83t-77 33 q-62 0 -105 -57t-43 -142q0 -73 25 -122l-99 -418q-17 -70 -13 -177q-206 91 -333 281t-127 423q0 209 103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
<glyph unicode="&#xf0d3;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-725q85 122 108 210q9 34 53 209q21 -39 73.5 -67t112.5 -28q181 0 295.5 147.5t114.5 373.5q0 84 -35 162.5t-96.5 139t-152.5 97t-197 36.5q-104 0 -194.5 -28.5t-153 -76.5 t-107.5 -109.5t-66.5 -128t-21.5 -132.5q0 -102 39.5 -180t116.5 -110q13 -5 23.5 0t14.5 19q10 44 15 61q6 23 -11 42q-50 62 -50 150q0 150 103.5 256.5t270.5 106.5q149 0 232.5 -81t83.5 -210q0 -168 -67.5 -286t-173.5 -118q-60 0 -97 43.5t-23 103.5q8 34 26.5 92.5 t29.5 102t11 74.5q0 49 -26.5 81.5t-75.5 32.5q-61 0 -103.5 -56.5t-42.5 -139.5q0 -72 24 -121l-98 -414q-24 -100 -7 -254h-183q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960z" />
<glyph unicode="&#xf0d4;" d="M829 318q0 -76 -58.5 -112.5t-139.5 -36.5q-41 0 -80.5 9.5t-75.5 28.5t-58 53t-22 78q0 46 25 80t65.5 51.5t82 25t84.5 7.5q20 0 31 -2q2 -1 23 -16.5t26 -19t23 -18t24.5 -22t19 -22.5t17 -26t9 -26.5t4.5 -31.5zM755 863q0 -60 -33 -99.5t-92 -39.5q-53 0 -93 42.5 t-57.5 96.5t-17.5 106q0 61 32 104t92 43q53 0 93.5 -45t58 -101t17.5 -107zM861 1120l88 64h-265q-85 0 -161 -32t-127.5 -98t-51.5 -153q0 -93 64.5 -154.5t158.5 -61.5q22 0 43 3q-13 -29 -13 -54q0 -44 40 -94q-175 -12 -257 -63q-47 -29 -75.5 -73t-28.5 -95 q0 -43 18.5 -77.5t48.5 -56.5t69 -37t77.5 -21t76.5 -6q60 0 120.5 15.5t113.5 46t86 82.5t33 117q0 49 -20 89.5t-49 66.5t-58 47.5t-49 44t-20 44.5t15.5 42.5t37.5 39.5t44 42t37.5 59.5t15.5 82.5q0 60 -22.5 99.5t-72.5 90.5h83zM1152 672h128v64h-128v128h-64v-128 h-128v-64h128v-160h64v160zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf0d5;" horiz-adv-x="1664" d="M735 740q0 -36 32 -70.5t77.5 -68t90.5 -73.5t77 -104t32 -142q0 -90 -48 -173q-72 -122 -211 -179.5t-298 -57.5q-132 0 -246.5 41.5t-171.5 137.5q-37 60 -37 131q0 81 44.5 150t118.5 115q131 82 404 100q-32 42 -47.5 74t-15.5 73q0 36 21 85q-46 -4 -68 -4 q-148 0 -249.5 96.5t-101.5 244.5q0 82 36 159t99 131q77 66 182.5 98t217.5 32h418l-138 -88h-131q74 -63 112 -133t38 -160q0 -72 -24.5 -129.5t-59 -93t-69.5 -65t-59.5 -61.5t-24.5 -66zM589 836q38 0 78 16.5t66 43.5q53 57 53 159q0 58 -17 125t-48.5 129.5 t-84.5 103.5t-117 41q-42 0 -82.5 -19.5t-65.5 -52.5q-47 -59 -47 -160q0 -46 10 -97.5t31.5 -103t52 -92.5t75 -67t96.5 -26zM591 -37q58 0 111.5 13t99 39t73 73t27.5 109q0 25 -7 49t-14.5 42t-27 41.5t-29.5 35t-38.5 34.5t-36.5 29t-41.5 30t-36.5 26q-16 2 -48 2 q-53 0 -105 -7t-107.5 -25t-97 -46t-68.5 -74.5t-27 -105.5q0 -70 35 -123.5t91.5 -83t119 -44t127.5 -14.5zM1401 839h213v-108h-213v-219h-105v219h-212v108h212v217h105v-217z" />
<glyph unicode="&#xf0d4;" d="M917 631q0 26 -6 64h-362v-132h217q-3 -24 -16.5 -50t-37.5 -53t-66.5 -44.5t-96.5 -17.5q-99 0 -169 71t-70 171t70 171t169 71q92 0 153 -59l104 101q-108 100 -257 100q-160 0 -272 -112.5t-112 -271.5t112 -271.5t272 -112.5q165 0 266.5 105t101.5 270zM1262 585 h109v110h-109v110h-110v-110h-110v-110h110v-110h110v110zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf0d5;" horiz-adv-x="2304" d="M1437 623q0 -208 -87 -370.5t-248 -254t-369 -91.5q-149 0 -285 58t-234 156t-156 234t-58 285t58 285t156 234t234 156t285 58q286 0 491 -192l-199 -191q-117 113 -292 113q-123 0 -227.5 -62t-165.5 -168.5t-61 -232.5t61 -232.5t165.5 -168.5t227.5 -62 q83 0 152.5 23t114.5 57.5t78.5 78.5t49 83t21.5 74h-416v252h692q12 -63 12 -122zM2304 745v-210h-209v-209h-210v209h-209v210h209v209h210v-209h209z" />
<glyph unicode="&#xf0d6;" horiz-adv-x="1920" d="M768 384h384v96h-128v448h-114l-148 -137l77 -80q42 37 55 57h2v-288h-128v-96zM1280 640q0 -70 -21 -142t-59.5 -134t-101.5 -101t-138 -39t-138 39t-101.5 101t-59.5 134t-21 142t21 142t59.5 134t101.5 101t138 39t138 -39t101.5 -101t59.5 -134t21 -142zM1792 384 v512q-106 0 -181 75t-75 181h-1152q0 -106 -75 -181t-181 -75v-512q106 0 181 -75t75 -181h1152q0 106 75 181t181 75zM1920 1216v-1152q0 -26 -19 -45t-45 -19h-1792q-26 0 -45 19t-19 45v1152q0 26 19 45t45 19h1792q26 0 45 -19t19 -45z" />
<glyph unicode="&#xf0d7;" horiz-adv-x="1024" d="M1024 832q0 -26 -19 -45l-448 -448q-19 -19 -45 -19t-45 19l-448 448q-19 19 -19 45t19 45t45 19h896q26 0 45 -19t19 -45z" />
<glyph unicode="&#xf0d8;" horiz-adv-x="1024" d="M1024 320q0 -26 -19 -45t-45 -19h-896q-26 0 -45 19t-19 45t19 45l448 448q19 19 45 19t45 -19l448 -448q19 -19 19 -45z" />
@ -362,7 +362,7 @@
<glyph unicode="&#xf169;" d="M685 771q0 1 -126 222q-21 34 -52 34h-184q-18 0 -26 -11q-7 -12 1 -29l125 -216v-1l-196 -346q-9 -14 0 -28q8 -13 24 -13h185q31 0 50 36zM1309 1268q-7 12 -24 12h-187q-30 0 -49 -35l-411 -729q1 -2 262 -481q20 -35 52 -35h184q18 0 25 12q8 13 -1 28l-260 476v1 l409 723q8 16 0 28zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf16a;" horiz-adv-x="1792" d="M1280 640q0 37 -30 54l-512 320q-31 20 -65 2q-33 -18 -33 -56v-640q0 -38 33 -56q16 -8 31 -8q20 0 34 10l512 320q30 17 30 54zM1792 640q0 -96 -1 -150t-8.5 -136.5t-22.5 -147.5q-16 -73 -69 -123t-124 -58q-222 -25 -671 -25t-671 25q-71 8 -124.5 58t-69.5 123 q-14 65 -21.5 147.5t-8.5 136.5t-1 150t1 150t8.5 136.5t22.5 147.5q16 73 69 123t124 58q222 25 671 25t671 -25q71 -8 124.5 -58t69.5 -123q14 -65 21.5 -147.5t8.5 -136.5t1 -150z" />
<glyph unicode="&#xf16b;" horiz-adv-x="1792" d="M402 829l494 -305l-342 -285l-490 319zM1388 274v-108l-490 -293v-1l-1 1l-1 -1v1l-489 293v108l147 -96l342 284v2l1 -1l1 1v-2l343 -284zM554 1418l342 -285l-494 -304l-338 270zM1390 829l338 -271l-489 -319l-343 285zM1239 1418l489 -319l-338 -270l-494 304z" />
<glyph unicode="&#xf16c;" horiz-adv-x="1408" d="M928 135v-151l-707 -1v151zM1169 481v-701l-1 -35v-1h-1132l-35 1h-1v736h121v-618h928v618h120zM241 393l704 -65l-13 -150l-705 65zM309 709l683 -183l-39 -146l-683 183zM472 1058l609 -360l-77 -130l-609 360zM832 1389l398 -585l-124 -85l-399 584zM1285 1536 l121 -697l-149 -26l-121 697z" />
<glyph unicode="&#xf16c;" d="M1289 -96h-1118v480h-160v-640h1438v640h-160v-480zM347 428l33 157l783 -165l-33 -156zM450 802l67 146l725 -339l-67 -145zM651 1158l102 123l614 -513l-102 -123zM1048 1536l477 -641l-128 -96l-477 641zM330 65v159h800v-159h-800z" />
<glyph unicode="&#xf16d;" d="M1362 110v648h-135q20 -63 20 -131q0 -126 -64 -232.5t-174 -168.5t-240 -62q-197 0 -337 135.5t-140 327.5q0 68 20 131h-141v-648q0 -26 17.5 -43.5t43.5 -17.5h1069q25 0 43 17.5t18 43.5zM1078 643q0 124 -90.5 211.5t-218.5 87.5q-127 0 -217.5 -87.5t-90.5 -211.5 t90.5 -211.5t217.5 -87.5q128 0 218.5 87.5t90.5 211.5zM1362 1003v165q0 28 -20 48.5t-49 20.5h-174q-29 0 -49 -20.5t-20 -48.5v-165q0 -29 20 -49t49 -20h174q29 0 49 20t20 49zM1536 1211v-1142q0 -81 -58 -139t-139 -58h-1142q-81 0 -139 58t-58 139v1142q0 81 58 139 t139 58h1142q81 0 139 -58t58 -139z" />
<glyph unicode="&#xf16e;" d="M1248 1408q119 0 203.5 -84.5t84.5 -203.5v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960zM698 640q0 88 -62 150t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150zM1262 640q0 88 -62 150 t-150 62t-150 -62t-62 -150t62 -150t150 -62t150 62t62 150z" />
<glyph unicode="&#xf170;" d="M768 914l201 -306h-402zM1133 384h94l-459 691l-459 -691h94l104 160h522zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
@ -410,7 +410,7 @@
<glyph unicode="&#xf19c;" horiz-adv-x="2048" d="M960 1536l960 -384v-128h-128q0 -26 -20.5 -45t-48.5 -19h-1526q-28 0 -48.5 19t-20.5 45h-128v128zM256 896h256v-768h128v768h256v-768h128v768h256v-768h128v768h256v-768h59q28 0 48.5 -19t20.5 -45v-64h-1664v64q0 26 20.5 45t48.5 19h59v768zM1851 -64 q28 0 48.5 -19t20.5 -45v-128h-1920v128q0 26 20.5 45t48.5 19h1782z" />
<glyph unicode="&#xf19d;" horiz-adv-x="2304" d="M1774 700l18 -316q4 -69 -82 -128t-235 -93.5t-323 -34.5t-323 34.5t-235 93.5t-82 128l18 316l574 -181q22 -7 48 -7t48 7zM2304 1024q0 -23 -22 -31l-1120 -352q-4 -1 -10 -1t-10 1l-652 206q-43 -34 -71 -111.5t-34 -178.5q63 -36 63 -109q0 -69 -58 -107l58 -433 q2 -14 -8 -25q-9 -11 -24 -11h-192q-15 0 -24 11q-10 11 -8 25l58 433q-58 38 -58 107q0 73 65 111q11 207 98 330l-333 104q-22 8 -22 31t22 31l1120 352q4 1 10 1t10 -1l1120 -352q22 -8 22 -31z" />
<glyph unicode="&#xf19e;" d="M859 579l13 -707q-62 11 -105 11q-41 0 -105 -11l13 707q-40 69 -168.5 295.5t-216.5 374.5t-181 287q58 -15 108 -15q43 0 111 15q63 -111 133.5 -229.5t167 -276.5t138.5 -227q37 61 109.5 177.5t117.5 190t105 176t107 189.5q54 -14 107 -14q56 0 114 14v0 q-28 -39 -60 -88.5t-49.5 -78.5t-56.5 -96t-49 -84q-146 -248 -353 -610z" />
<glyph unicode="&#xf1a0;" horiz-adv-x="1280" d="M981 197q0 25 -7 49t-14.5 42t-27 41.5t-29.5 35t-38.5 34.5t-36.5 29t-41.5 30t-36.5 26q-16 2 -49 2q-53 0 -104.5 -7t-107 -25t-97 -46t-68.5 -74.5t-27 -105.5q0 -56 23.5 -102t61 -75.5t87 -50t100 -29t101.5 -8.5q58 0 111.5 13t99 39t73 73t27.5 109zM864 1055 q0 59 -17 125.5t-48 129t-84 103.5t-117 41q-42 0 -82.5 -19.5t-66.5 -52.5q-46 -59 -46 -160q0 -46 10 -97.5t31.5 -103t52 -92.5t75 -67t96.5 -26q37 0 77.5 16.5t65.5 43.5q53 56 53 159zM752 1536h417l-137 -88h-132q75 -63 113 -133t38 -160q0 -72 -24.5 -129.5 t-59.5 -93t-69.5 -65t-59 -61.5t-24.5 -66q0 -36 32 -70.5t77 -68t90.5 -73.5t77.5 -104t32 -142q0 -91 -49 -173q-71 -122 -209.5 -179.5t-298.5 -57.5q-132 0 -246.5 41.5t-172.5 137.5q-36 59 -36 131q0 81 44.5 150t118.5 115q131 82 404 100q-32 41 -47.5 73.5 t-15.5 73.5q0 40 21 85q-46 -4 -68 -4q-148 0 -249.5 96.5t-101.5 244.5q0 82 36 159t99 131q76 66 182 98t218 32z" />
<glyph unicode="&#xf1a0;" d="M768 750h725q12 -67 12 -128q0 -217 -91 -387.5t-259.5 -266.5t-386.5 -96q-157 0 -299 60.5t-245 163.5t-163.5 245t-60.5 299t60.5 299t163.5 245t245 163.5t299 60.5q300 0 515 -201l-209 -201q-123 119 -306 119q-129 0 -238.5 -65t-173.5 -176.5t-64 -243.5 t64 -243.5t173.5 -176.5t238.5 -65q87 0 160 24t120 60t82 82t51.5 87t22.5 78h-436v264z" />
<glyph unicode="&#xf1a1;" horiz-adv-x="1792" d="M1095 369q16 -16 0 -31q-62 -62 -199 -62t-199 62q-16 15 0 31q6 6 15 6t15 -6q48 -49 169 -49q120 0 169 49q6 6 15 6t15 -6zM788 550q0 -37 -26 -63t-63 -26t-63.5 26t-26.5 63q0 38 26.5 64t63.5 26t63 -26.5t26 -63.5zM1183 550q0 -37 -26.5 -63t-63.5 -26t-63 26 t-26 63t26 63.5t63 26.5t63.5 -26t26.5 -64zM1434 670q0 49 -35 84t-85 35t-86 -36q-130 90 -311 96l63 283l200 -45q0 -37 26 -63t63 -26t63.5 26.5t26.5 63.5t-26.5 63.5t-63.5 26.5q-54 0 -80 -50l-221 49q-19 5 -25 -16l-69 -312q-180 -7 -309 -97q-35 37 -87 37 q-50 0 -85 -35t-35 -84q0 -35 18.5 -64t49.5 -44q-6 -27 -6 -56q0 -142 140 -243t337 -101q198 0 338 101t140 243q0 32 -7 57q30 15 48 43.5t18 63.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191 t348 71t348 -71t286 -191t191 -286t71 -348z" />
<glyph unicode="&#xf1a2;" d="M939 407q13 -13 0 -26q-53 -53 -171 -53t-171 53q-13 13 0 26q5 6 13 6t13 -6q42 -42 145 -42t145 42q5 6 13 6t13 -6zM676 563q0 -31 -23 -54t-54 -23t-54 23t-23 54q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1014 563q0 -31 -23 -54t-54 -23t-54 23t-23 54 q0 32 22.5 54.5t54.5 22.5t54.5 -22.5t22.5 -54.5zM1229 666q0 42 -30 72t-73 30q-42 0 -73 -31q-113 78 -267 82l54 243l171 -39q1 -32 23.5 -54t53.5 -22q32 0 54.5 22.5t22.5 54.5t-22.5 54.5t-54.5 22.5q-48 0 -69 -43l-189 42q-17 5 -21 -13l-60 -268q-154 -6 -265 -83 q-30 32 -74 32q-43 0 -73 -30t-30 -72q0 -30 16 -55t42 -38q-5 -25 -5 -48q0 -122 120 -208.5t289 -86.5q170 0 290 86.5t120 208.5q0 25 -6 49q25 13 40.5 37.5t15.5 54.5zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960 q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf1a3;" d="M866 697l90 27v62q0 79 -58 135t-138 56t-138 -55.5t-58 -134.5v-283q0 -20 -14 -33.5t-33 -13.5t-32.5 13.5t-13.5 33.5v120h-151v-122q0 -82 57.5 -139t139.5 -57q81 0 138.5 56.5t57.5 136.5v280q0 19 13.5 33t33.5 14q19 0 32.5 -14t13.5 -33v-54zM1199 502v122h-150 v-126q0 -20 -13.5 -33.5t-33.5 -13.5q-19 0 -32.5 14t-13.5 33v123l-90 -26l-60 28v-123q0 -80 58 -137t139 -57t138.5 57t57.5 139zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103 t385.5 -103t279.5 -279.5t103 -385.5z" />
@ -454,7 +454,7 @@
<glyph unicode="&#xf1cb;" horiz-adv-x="1792" d="M216 367l603 -402v359l-334 223zM154 511l193 129l-193 129v-258zM973 -35l603 402l-269 180l-334 -223v-359zM896 458l272 182l-272 182l-272 -182zM485 733l334 223v359l-603 -402zM1445 640l193 -129v258zM1307 733l269 180l-603 402v-359zM1792 913v-546 q0 -41 -34 -64l-819 -546q-21 -13 -43 -13t-43 13l-819 546q-34 23 -34 64v546q0 41 34 64l819 546q21 13 43 13t43 -13l819 -546q34 -23 34 -64z" />
<glyph unicode="&#xf1cc;" horiz-adv-x="2048" d="M1800 764q111 -46 179.5 -145.5t68.5 -221.5q0 -164 -118 -280.5t-285 -116.5q-4 0 -11.5 0.5t-10.5 0.5h-1209h-1h-2h-5q-170 10 -288 125.5t-118 280.5q0 110 55 203t147 147q-12 39 -12 82q0 115 82 196t199 81q95 0 172 -58q75 154 222.5 248t326.5 94 q166 0 306 -80.5t221.5 -218.5t81.5 -301q0 -6 -0.5 -18t-0.5 -18zM468 498q0 -122 84 -193t208 -71q137 0 240 99q-16 20 -47.5 56.5t-43.5 50.5q-67 -65 -144 -65q-55 0 -93.5 33.5t-38.5 87.5q0 53 38.5 87t91.5 34q44 0 84.5 -21t73 -55t65 -75t69 -82t77 -75t97 -55 t121.5 -21q121 0 204.5 71.5t83.5 190.5q0 121 -84 192t-207 71q-143 0 -241 -97q14 -16 29.5 -34t34.5 -40t29 -34q66 64 142 64q52 0 92 -33t40 -84q0 -57 -37 -91.5t-94 -34.5q-43 0 -82.5 21t-72 55t-65.5 75t-69.5 82t-77.5 75t-96.5 55t-118.5 21q-122 0 -207 -70.5 t-85 -189.5z" />
<glyph unicode="&#xf1cd;" horiz-adv-x="1792" d="M896 1536q182 0 348 -71t286 -191t191 -286t71 -348t-71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71zM896 1408q-190 0 -361 -90l194 -194q82 28 167 28t167 -28l194 194q-171 90 -361 90zM218 279l194 194 q-28 82 -28 167t28 167l-194 194q-90 -171 -90 -361t90 -361zM896 -128q190 0 361 90l-194 194q-82 -28 -167 -28t-167 28l-194 -194q171 -90 361 -90zM896 256q159 0 271.5 112.5t112.5 271.5t-112.5 271.5t-271.5 112.5t-271.5 -112.5t-112.5 -271.5t112.5 -271.5 t271.5 -112.5zM1380 473l194 -194q90 171 90 361t-90 361l-194 -194q28 -82 28 -167t-28 -167z" />
<glyph unicode="&#xf1ce;" horiz-adv-x="1792" d="M1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348q0 222 101 414.5t276.5 317t390.5 155.5v-260q-221 -45 -366.5 -221t-145.5 -406q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5 q0 230 -145.5 406t-366.5 221v260q215 -31 390.5 -155.5t276.5 -317t101 -414.5z" />
<glyph unicode="&#xf1ce;" horiz-adv-x="1792" d="M1760 640q0 -176 -68.5 -336t-184 -275.5t-275.5 -184t-336 -68.5t-336 68.5t-275.5 184t-184 275.5t-68.5 336q0 213 97 398.5t265 305.5t374 151v-228q-221 -45 -366.5 -221t-145.5 -406q0 -130 51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5 t136.5 204t51 248.5q0 230 -145.5 406t-366.5 221v228q206 -31 374 -151t265 -305.5t97 -398.5z" />
<glyph unicode="&#xf1d0;" horiz-adv-x="1792" d="M19 662q8 217 116 406t305 318h5q0 -1 -1 -3q-8 -8 -28 -33.5t-52 -76.5t-60 -110.5t-44.5 -135.5t-14 -150.5t39 -157.5t108.5 -154q50 -50 102 -69.5t90.5 -11.5t69.5 23.5t47 32.5l16 16q39 51 53 116.5t6.5 122.5t-21 107t-26.5 80l-14 29q-10 25 -30.5 49.5t-43 41 t-43.5 29.5t-35 19l-13 6l104 115q39 -17 78 -52t59 -61l19 -27q1 48 -18.5 103.5t-40.5 87.5l-20 31l161 183l160 -181q-33 -46 -52.5 -102.5t-22.5 -90.5l-4 -33q22 37 61.5 72.5t67.5 52.5l28 17l103 -115q-44 -14 -85 -50t-60 -65l-19 -29q-31 -56 -48 -133.5t-7 -170 t57 -156.5q33 -45 77.5 -60.5t85 -5.5t76 26.5t57.5 33.5l21 16q60 53 96.5 115t48.5 121.5t10 121.5t-18 118t-37 107.5t-45.5 93t-45 72t-34.5 47.5l-13 17q-14 13 -7 13l10 -3q40 -29 62.5 -46t62 -50t64 -58t58.5 -65t55.5 -77t45.5 -88t38 -103t23.5 -117t10.5 -136 q3 -259 -108 -465t-312 -321t-456 -115q-185 0 -351 74t-283.5 198t-184 293t-60.5 353z" />
<glyph unicode="&#xf1d1;" horiz-adv-x="1792" d="M874 -102v-66q-208 6 -385 109.5t-283 275.5l58 34q29 -49 73 -99l65 57q148 -168 368 -212l-17 -86q65 -12 121 -13zM276 428l-83 -28q22 -60 49 -112l-57 -33q-98 180 -98 385t98 385l57 -33q-30 -56 -49 -112l82 -28q-35 -100 -35 -212q0 -109 36 -212zM1528 251 l58 -34q-106 -172 -283 -275.5t-385 -109.5v66q56 1 121 13l-17 86q220 44 368 212l65 -57q44 50 73 99zM1377 805l-233 -80q14 -42 14 -85t-14 -85l232 -80q-31 -92 -98 -169l-185 162q-57 -67 -147 -85l48 -241q-52 -10 -98 -10t-98 10l48 241q-90 18 -147 85l-185 -162 q-67 77 -98 169l232 80q-14 42 -14 85t14 85l-233 80q33 93 99 169l185 -162q59 68 147 86l-48 240q44 10 98 10t98 -10l-48 -240q88 -18 147 -86l185 162q66 -76 99 -169zM874 1448v-66q-65 -2 -121 -13l17 -86q-220 -42 -368 -211l-65 56q-38 -42 -73 -98l-57 33 q106 172 282 275.5t385 109.5zM1705 640q0 -205 -98 -385l-57 33q27 52 49 112l-83 28q36 103 36 212q0 112 -35 212l82 28q-19 56 -49 112l57 33q98 -180 98 -385zM1585 1063l-57 -33q-35 56 -73 98l-65 -56q-148 169 -368 211l17 86q-56 11 -121 13v66q209 -6 385 -109.5 t282 -275.5zM1748 640q0 173 -67.5 331t-181.5 272t-272 181.5t-331 67.5t-331 -67.5t-272 -181.5t-181.5 -272t-67.5 -331t67.5 -331t181.5 -272t272 -181.5t331 -67.5t331 67.5t272 181.5t181.5 272t67.5 331zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71 t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
<glyph unicode="&#xf1d2;" d="M582 228q0 -66 -93 -66q-107 0 -107 63q0 64 98 64q102 0 102 -61zM546 694q0 -85 -74 -85q-77 0 -77 84q0 90 77 90q36 0 55 -25.5t19 -63.5zM712 769v125q-78 -29 -135 -29q-50 29 -110 29q-86 0 -145 -57t-59 -143q0 -50 29.5 -102t73.5 -67v-3q-38 -17 -38 -85 q0 -53 41 -77v-3q-113 -37 -113 -139q0 -45 20 -78.5t54 -51t72 -25.5t81 -8q224 0 224 188q0 67 -48 99t-126 46q-27 5 -51.5 20.5t-24.5 39.5q0 44 49 52q77 15 122 70t45 134q0 24 -10 52q37 9 49 13zM771 350h137q-2 27 -2 82v387q0 46 2 69h-137q3 -23 3 -71v-392 q0 -50 -3 -75zM1280 366v121q-30 -21 -68 -21q-53 0 -53 82v225h52q9 0 26.5 -1t26.5 -1v117h-105q0 82 3 102h-140q4 -24 4 -55v-47h-60v-117q36 3 37 3q3 0 11 -0.5t12 -0.5v-2h-2v-217q0 -37 2.5 -64t11.5 -56.5t24.5 -48.5t43.5 -31t66 -12q64 0 108 24zM924 1072 q0 36 -24 63.5t-60 27.5t-60.5 -27t-24.5 -64q0 -36 25 -62.5t60 -26.5t59.5 27t24.5 62zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
@ -484,7 +484,7 @@
<glyph unicode="&#xf1eb;" horiz-adv-x="2048" d="M1024 13q-20 0 -93 73.5t-73 93.5q0 32 62.5 54t103.5 22t103.5 -22t62.5 -54q0 -20 -73 -93.5t-93 -73.5zM1294 284q-2 0 -40 25t-101.5 50t-128.5 25t-128.5 -25t-101 -50t-40.5 -25q-18 0 -93.5 75t-75.5 93q0 13 10 23q78 77 196 121t233 44t233 -44t196 -121 q10 -10 10 -23q0 -18 -75.5 -93t-93.5 -75zM1567 556q-11 0 -23 8q-136 105 -252 154.5t-268 49.5q-85 0 -170.5 -22t-149 -53t-113.5 -62t-79 -53t-31 -22q-17 0 -92 75t-75 93q0 12 10 22q132 132 320 205t380 73t380 -73t320 -205q10 -10 10 -22q0 -18 -75 -93t-92 -75z M1838 827q-11 0 -22 9q-179 157 -371.5 236.5t-420.5 79.5t-420.5 -79.5t-371.5 -236.5q-11 -9 -22 -9q-17 0 -92.5 75t-75.5 93q0 13 10 23q187 186 445 288t527 102t527 -102t445 -288q10 -10 10 -23q0 -18 -75.5 -93t-92.5 -75z" />
<glyph unicode="&#xf1ec;" horiz-adv-x="1792" d="M384 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM384 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5 t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 0q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5 t37.5 90.5zM384 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1152 384q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM768 768q0 53 -37.5 90.5t-90.5 37.5 t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1536 0v384q0 52 -38 90t-90 38t-90 -38t-38 -90v-384q0 -52 38 -90t90 -38t90 38t38 90zM1152 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z M1536 1088v256q0 26 -19 45t-45 19h-1280q-26 0 -45 -19t-19 -45v-256q0 -26 19 -45t45 -19h1280q26 0 45 19t19 45zM1536 768q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5zM1664 1408v-1536q0 -52 -38 -90t-90 -38 h-1408q-52 0 -90 38t-38 90v1536q0 52 38 90t90 38h1408q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf1ed;" d="M1519 890q18 -84 -4 -204q-87 -444 -565 -444h-44q-25 0 -44 -16.5t-24 -42.5l-4 -19l-55 -346l-2 -15q-5 -26 -24.5 -42.5t-44.5 -16.5h-251q-21 0 -33 15t-9 36q9 56 26.5 168t26.5 168t27 167.5t27 167.5q5 37 43 37h131q133 -2 236 21q175 39 287 144q102 95 155 246 q24 70 35 133q1 6 2.5 7.5t3.5 1t6 -3.5q79 -59 98 -162zM1347 1172q0 -107 -46 -236q-80 -233 -302 -315q-113 -40 -252 -42q0 -1 -90 -1l-90 1q-100 0 -118 -96q-2 -8 -85 -530q-1 -10 -12 -10h-295q-22 0 -36.5 16.5t-11.5 38.5l232 1471q5 29 27.5 48t51.5 19h598 q34 0 97.5 -13t111.5 -32q107 -41 163.5 -123t56.5 -196z" />
<glyph unicode="&#xf1ee;" horiz-adv-x="1792" d="M602 949q19 -61 31 -123.5t17 -141.5t-14 -159t-62 -145q-21 81 -67 157t-95.5 127t-99 90.5t-78.5 57.5t-33 19q-62 34 -81.5 100t14.5 128t101 81.5t129 -14.5q138 -83 238 -177zM927 1236q11 -25 20.5 -46t36.5 -100.5t42.5 -150.5t25.5 -179.5t0 -205.5t-47.5 -209.5 t-105.5 -208.5q-51 -72 -138 -72q-54 0 -98 31q-57 40 -69 109t28 127q60 85 81 195t13 199.5t-32 180.5t-39 128t-22 52q-31 63 -8.5 129.5t85.5 97.5q34 17 75 17q47 0 88.5 -25t63.5 -69zM1248 567q-17 -160 -72 -311q-17 131 -63 246q25 174 -5 361q-27 178 -94 342 q114 -90 212 -211q9 -37 15 -80q26 -179 7 -347zM1520 1440q9 -17 23.5 -49.5t43.5 -117.5t50.5 -178t34 -227.5t5 -269t-47 -300t-112.5 -323.5q-22 -48 -66 -75.5t-95 -27.5q-39 0 -74 16q-67 31 -92.5 100t4.5 136q58 126 90 257.5t37.5 239.5t-3.5 213.5t-26.5 180.5 t-38.5 138.5t-32.5 90t-15.5 32.5q-34 65 -11.5 135.5t87.5 104.5q37 20 81 20q49 0 91.5 -25.5t66.5 -70.5z" />
<glyph unicode="&#xf1ee;" horiz-adv-x="1792" d="M441 864q32 0 52 -26q266 -364 362 -774h-446q-127 441 -367 749q-12 16 -3 33.5t29 17.5h373zM1000 507q-49 -199 -125 -393q-79 310 -256 594q40 221 44 449q211 -340 337 -650zM1099 1216q235 -324 384.5 -698.5t184.5 -773.5h-451q-41 665 -553 1472h435zM1792 640 q0 -424 -101 -812q-67 560 -359 1083q-25 301 -106 584q-4 16 5.5 28.5t25.5 12.5h359q21 0 38.5 -13t22.5 -33q115 -409 115 -850z" />
<glyph unicode="&#xf1f0;" horiz-adv-x="2304" d="M1975 546h-138q14 37 66 179l3 9q4 10 10 26t9 26l12 -55zM531 611l-58 295q-11 54 -75 54h-268l-2 -13q311 -79 403 -336zM710 960l-162 -438l-17 89q-26 70 -85 129.5t-131 88.5l135 -510h175l261 641h-176zM849 318h166l104 642h-166zM1617 944q-69 27 -149 27 q-123 0 -201 -59t-79 -153q-1 -102 145 -174q48 -23 67 -41t19 -39q0 -30 -30 -46t-69 -16q-86 0 -156 33l-22 11l-23 -144q74 -34 185 -34q130 -1 208.5 59t80.5 160q0 106 -140 174q-49 25 -71 42t-22 38q0 22 24.5 38.5t70.5 16.5q70 1 124 -24l15 -8zM2042 960h-128 q-65 0 -87 -54l-246 -588h174l35 96h212q5 -22 20 -96h154zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf1f1;" horiz-adv-x="2304" d="M671 603h-13q-47 0 -47 -32q0 -22 20 -22q17 0 28 15t12 39zM1066 639h62v3q1 4 0.5 6.5t-1 7t-2 8t-4.5 6.5t-7.5 5t-11.5 2q-28 0 -36 -38zM1606 603h-12q-48 0 -48 -32q0 -22 20 -22q17 0 28 15t12 39zM1925 629q0 41 -30 41q-19 0 -31 -20t-12 -51q0 -42 28 -42 q20 0 32.5 20t12.5 52zM480 770h87l-44 -262h-56l32 201l-71 -201h-39l-4 200l-34 -200h-53l44 262h81l2 -163zM733 663q0 -6 -4 -42q-16 -101 -17 -113h-47l1 22q-20 -26 -58 -26q-23 0 -37.5 16t-14.5 42q0 39 26 60.5t73 21.5q14 0 23 -1q0 3 0.5 5.5t1 4.5t0.5 3 q0 20 -36 20q-29 0 -59 -10q0 4 7 48q38 11 67 11q74 0 74 -62zM889 721l-8 -49q-22 3 -41 3q-27 0 -27 -17q0 -8 4.5 -12t21.5 -11q40 -19 40 -60q0 -72 -87 -71q-34 0 -58 6q0 2 7 49q29 -8 51 -8q32 0 32 19q0 7 -4.5 11.5t-21.5 12.5q-43 20 -43 59q0 72 84 72 q30 0 50 -4zM977 721h28l-7 -52h-29q-2 -17 -6.5 -40.5t-7 -38.5t-2.5 -18q0 -16 19 -16q8 0 16 2l-8 -47q-21 -7 -40 -7q-43 0 -45 47q0 12 8 56q3 20 25 146h55zM1180 648q0 -23 -7 -52h-111q-3 -22 10 -33t38 -11q30 0 58 14l-9 -54q-30 -8 -57 -8q-95 0 -95 95 q0 55 27.5 90.5t69.5 35.5q35 0 55.5 -21t20.5 -56zM1319 722q-13 -23 -22 -62q-22 2 -31 -24t-25 -128h-56l3 14q22 130 29 199h51l-3 -33q14 21 25.5 29.5t28.5 4.5zM1506 763l-9 -57q-28 14 -50 14q-31 0 -51 -27.5t-20 -70.5q0 -30 13.5 -47t38.5 -17q21 0 48 13 l-10 -59q-28 -8 -50 -8q-45 0 -71.5 30.5t-26.5 82.5q0 70 35.5 114.5t91.5 44.5q26 0 61 -13zM1668 663q0 -18 -4 -42q-13 -79 -17 -113h-46l1 22q-20 -26 -59 -26q-23 0 -37 16t-14 42q0 39 25.5 60.5t72.5 21.5q15 0 23 -1q2 7 2 13q0 20 -36 20q-29 0 -59 -10q0 4 8 48 q38 11 67 11q73 0 73 -62zM1809 722q-14 -24 -21 -62q-23 2 -31.5 -23t-25.5 -129h-56l3 14q19 104 29 199h52q0 -11 -4 -33q15 21 26.5 29.5t27.5 4.5zM1950 770h56l-43 -262h-53l3 19q-23 -23 -52 -23q-31 0 -49.5 24t-18.5 64q0 53 27.5 92t64.5 39q31 0 53 -29z M2061 640q0 148 -72.5 273t-198 198t-273.5 73q-181 0 -328 -110q127 -116 171 -284h-50q-44 150 -158 253q-114 -103 -158 -253h-50q44 168 171 284q-147 110 -328 110q-148 0 -273.5 -73t-198 -198t-72.5 -273t72.5 -273t198 -198t273.5 -73q181 0 328 110 q-120 111 -165 264h50q46 -138 152 -233q106 95 152 233h50q-45 -153 -165 -264q147 -110 328 -110q148 0 273.5 73t198 198t72.5 273zM2304 1280v-1280q0 -52 -38 -90t-90 -38h-2048q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h2048q52 0 90 -38t38 -90z" />
<glyph unicode="&#xf1f2;" horiz-adv-x="2304" d="M313 759q0 -51 -36 -84q-29 -26 -89 -26h-17v220h17q61 0 89 -27q36 -31 36 -83zM2089 824q0 -52 -64 -52h-19v101h20q63 0 63 -49zM380 759q0 74 -50 120.5t-129 46.5h-95v-333h95q74 0 119 38q60 51 60 128zM410 593h65v333h-65v-333zM730 694q0 40 -20.5 62t-75.5 42 q-29 10 -39.5 19t-10.5 23q0 16 13.5 26.5t34.5 10.5q29 0 53 -27l34 44q-41 37 -98 37q-44 0 -74 -27.5t-30 -67.5q0 -35 18 -55.5t64 -36.5q37 -13 45 -19q19 -12 19 -34q0 -20 -14 -33.5t-36 -13.5q-48 0 -71 44l-42 -40q44 -64 115 -64q51 0 83 30.5t32 79.5zM1008 604 v77q-37 -37 -78 -37q-49 0 -80.5 32.5t-31.5 82.5q0 48 31.5 81.5t77.5 33.5q43 0 81 -38v77q-40 20 -80 20q-74 0 -125.5 -50.5t-51.5 -123.5t51 -123.5t125 -50.5q42 0 81 19zM2240 0v527q-65 -40 -144.5 -84t-237.5 -117t-329.5 -137.5t-417.5 -134.5t-504 -118h1569 q26 0 45 19t19 45zM1389 757q0 75 -53 128t-128 53t-128 -53t-53 -128t53 -128t128 -53t128 53t53 128zM1541 584l144 342h-71l-90 -224l-89 224h-71l142 -342h35zM1714 593h184v56h-119v90h115v56h-115v74h119v57h-184v-333zM2105 593h80l-105 140q76 16 76 94q0 47 -31 73 t-87 26h-97v-333h65v133h9zM2304 1274v-1268q0 -56 -38.5 -95t-93.5 -39h-2040q-55 0 -93.5 39t-38.5 95v1268q0 56 38.5 95t93.5 39h2040q55 0 93.5 -39t38.5 -95z" />
@ -555,7 +555,7 @@
<glyph unicode="&#xf237;" d="M1536 1536l-192 -448h192v-192h-274l-55 -128h329v-192h-411l-357 -832l-357 832h-411v192h329l-55 128h-274v192h192l-192 448h256l323 -768h378l323 768h256zM768 320l108 256h-216z" />
<glyph unicode="&#xf238;" d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM768 192q80 0 136 56t56 136t-56 136t-136 56 t-136 -56t-56 -136t56 -136t136 -56zM1344 768v512h-1152v-512h1152z" />
<glyph unicode="&#xf239;" d="M1088 1536q185 0 316.5 -93.5t131.5 -226.5v-896q0 -130 -125.5 -222t-305.5 -97l213 -202q16 -15 8 -35t-30 -20h-1056q-22 0 -30 20t8 35l213 202q-180 5 -305.5 97t-125.5 222v896q0 133 131.5 226.5t316.5 93.5h640zM288 224q66 0 113 47t47 113t-47 113t-113 47 t-113 -47t-47 -113t47 -113t113 -47zM704 768v512h-544v-512h544zM1248 224q66 0 113 47t47 113t-47 113t-113 47t-113 -47t-47 -113t47 -113t113 -47zM1408 768v512h-576v-512h576z" />
<glyph unicode="&#xf23a;" horiz-adv-x="1792" d="M1792 204v-209h-642v209h134v926h-6l-314 -1135h-243l-310 1135h-8v-926h135v-209h-538v209h69q21 0 43 19.5t22 37.5v881q0 18 -22 40t-43 22h-69v209h672l221 -821h6l223 821h670v-209h-71q-19 0 -41 -22t-22 -40v-881q0 -18 21.5 -37.5t41.5 -19.5h71z" />
<glyph unicode="&#xf23a;" horiz-adv-x="1792" d="M597 1115v-1173q0 -25 -12.5 -42.5t-36.5 -17.5q-17 0 -33 8l-465 233q-21 10 -35.5 33.5t-14.5 46.5v1140q0 20 10 34t29 14q14 0 44 -15l511 -256q3 -3 3 -5zM661 1014l534 -866l-534 266v600zM1792 996v-1054q0 -25 -14 -40.5t-38 -15.5t-47 13l-441 220zM1789 1116 q0 -3 -256.5 -419.5t-300.5 -487.5l-390 634l324 527q17 28 52 28q14 0 26 -6l541 -270q4 -2 4 -6z" />
<glyph unicode="&#xf23b;" d="M809 532l266 499h-112l-157 -312q-24 -48 -44 -92l-42 92l-155 312h-120l263 -493v-324h101v318zM1536 1408v-1536h-1536v1536h1536z" />
<glyph unicode="&#xf23c;" horiz-adv-x="2296" d="M478 -139q-8 -16 -27 -34.5t-37 -25.5q-25 -9 -51.5 3.5t-28.5 31.5q-1 22 40 55t68 38q23 4 34 -21.5t2 -46.5zM1819 -139q7 -16 26 -34.5t38 -25.5q25 -9 51.5 3.5t27.5 31.5q2 22 -39.5 55t-68.5 38q-22 4 -33 -21.5t-2 -46.5zM1867 -30q13 -27 56.5 -59.5t77.5 -41.5 q45 -13 82 4.5t37 50.5q0 46 -67.5 100.5t-115.5 59.5q-40 5 -63.5 -37.5t-6.5 -76.5zM428 -30q-13 -27 -56 -59.5t-77 -41.5q-45 -13 -82 4.5t-37 50.5q0 46 67.5 100.5t115.5 59.5q40 5 63 -37.5t6 -76.5zM1158 1094h1q-41 0 -76 -15q27 -8 44 -30.5t17 -49.5 q0 -35 -27 -60t-65 -25q-52 0 -80 43q-5 -23 -5 -42q0 -74 56 -126.5t135 -52.5q80 0 136 52.5t56 126.5t-56 126.5t-136 52.5zM1462 1312q-99 109 -220.5 131.5t-245.5 -44.5q27 60 82.5 96.5t118 39.5t121.5 -17t99.5 -74.5t44.5 -131.5zM2212 73q8 -11 -11 -42 q7 -23 7 -40q1 -56 -44.5 -112.5t-109.5 -91.5t-118 -37q-48 -2 -92 21.5t-66 65.5q-687 -25 -1259 0q-23 -41 -66.5 -65t-92.5 -22q-86 3 -179.5 80.5t-92.5 160.5q2 22 7 40q-19 31 -11 42q6 10 31 1q14 22 41 51q-7 29 2 38q11 10 39 -4q29 20 59 34q0 29 13 37 q23 12 51 -16q35 5 61 -2q18 -4 38 -19v73q-11 0 -18 2q-53 10 -97 44.5t-55 87.5q-9 38 0 81q15 62 93 95q2 17 19 35.5t36 23.5t33 -7.5t19 -30.5h13q46 -5 60 -23q3 -3 5 -7q10 1 30.5 3.5t30.5 3.5q-15 11 -30 17q-23 40 -91 43q0 6 1 10q-62 2 -118.5 18.5t-84.5 47.5 q-32 36 -42.5 92t-2.5 112q16 126 90 179q23 16 52 4.5t32 -40.5q0 -1 1.5 -14t2.5 -21t3 -20t5.5 -19t8.5 -10q27 -14 76 -12q48 46 98 74q-40 4 -162 -14l47 46q61 58 163 111q145 73 282 86q-20 8 -41 15.5t-47 14t-42.5 10.5t-47.5 11t-43 10q595 126 904 -139 q98 -84 158 -222q85 -10 121 9h1q5 3 8.5 10t5.5 19t3 19.5t3 21.5l1 14q3 28 32 40t52 -5q73 -52 91 -178q7 -57 -3.5 -113t-42.5 -91q-28 -32 -83.5 -48.5t-115.5 -18.5v-10q-71 -2 -95 -43q-14 -5 -31 -17q11 -1 32 -3.5t30 -3.5q1 4 5 8q16 18 60 23h13q5 18 19 30t33 8 t36 -23t19 -36q79 -32 93 -95q9 -40 1 -81q-12 -53 -56 -88t-97 -44q-10 -2 -17 -2q0 -49 -1 -73q20 15 38 19q26 7 61 2q28 28 51 16q14 -9 14 -37q33 -16 59 -34q27 13 38 4q10 -10 2 -38q28 -30 41 -51q23 8 31 -1zM1937 1025q0 -29 -9 -54q82 -32 112 -132 q4 37 -9.5 98.5t-41.5 90.5q-20 19 -36 17t-16 -20zM1859 925q35 -42 47.5 -108.5t-0.5 -124.5q67 13 97 45q13 14 18 28q-3 64 -31 114.5t-79 66.5q-15 -15 -52 -21zM1822 921q-30 0 -44 1q42 -115 53 -239q21 0 43 3q16 68 1 135t-53 100zM258 839q30 100 112 132 q-9 25 -9 54q0 18 -16.5 20t-35.5 -17q-28 -29 -41.5 -90.5t-9.5 -98.5zM294 737q29 -31 97 -45q-13 58 -0.5 124.5t47.5 108.5v0q-37 6 -52 21q-51 -16 -78.5 -66t-31.5 -115q9 -17 18 -28zM471 683q14 124 73 235q-19 -4 -55 -18l-45 -19v1q-46 -89 -20 -196q25 -3 47 -3z M1434 644q8 -38 16.5 -108.5t11.5 -89.5q3 -18 9.5 -21.5t23.5 4.5q40 20 62 85.5t23 125.5q-24 2 -146 4zM1152 1285q-116 0 -199 -82.5t-83 -198.5q0 -117 83 -199.5t199 -82.5t199 82.5t83 199.5q0 116 -83 198.5t-199 82.5zM1380 646q-106 2 -211 0v1q-1 -27 2.5 -86 t13.5 -66q29 -14 93.5 -14.5t95.5 10.5q9 3 11 39t-0.5 69.5t-4.5 46.5zM1112 447q8 4 9.5 48t-0.5 88t-4 63v1q-212 -3 -214 -3q-4 -20 -7 -62t0 -83t14 -46q34 -15 101 -16t101 10zM718 636q-16 -59 4.5 -118.5t77.5 -84.5q15 -8 24 -5t12 21q3 16 8 90t10 103 q-69 -2 -136 -6zM591 510q3 -23 -34 -36q132 -141 271.5 -240t305.5 -154q172 49 310.5 146t293.5 250q-33 13 -30 34l3 9v1v-1q-17 2 -50 5.5t-48 4.5q-26 -90 -82 -132q-51 -38 -82 1q-5 6 -9 14q-7 13 -17 62q-2 -5 -5 -9t-7.5 -7t-8 -5.5t-9.5 -4l-10 -2.5t-12 -2 l-12 -1.5t-13.5 -1t-13.5 -0.5q-106 -9 -163 11q-4 -17 -10 -26.5t-21 -15t-23 -7t-36 -3.5q-2 0 -3 -0.5t-3 -0.5h-3q-179 -17 -203 40q-2 -63 -56 -54q-47 8 -91 54q-12 13 -20 26q-17 29 -26 65q-58 -6 -87 -10q1 -2 4 -10zM507 -118q3 14 3 30q-17 71 -51 130t-73 70 q-41 12 -101.5 -14.5t-104.5 -80t-39 -107.5q35 -53 100 -93t119 -42q51 -2 94 28t53 79zM510 53q23 -63 27 -119q195 113 392 174q-98 52 -180.5 120t-179.5 165q-6 -4 -29 -13q0 -2 -1 -5t-1 -4q31 -18 22 -37q-12 -23 -56 -34q-10 -13 -29 -24h-1q-2 -83 1 -150 q19 -34 35 -73zM579 -113q532 -21 1145 0q-254 147 -428 196q-76 -35 -156 -57q-8 -3 -16 0q-65 21 -129 49q-208 -60 -416 -188h-1v-1q1 0 1 1zM1763 -67q4 54 28 120q14 38 33 71l-1 -1q3 77 3 153q-15 8 -30 25q-42 9 -56 33q-9 20 22 38q-2 4 -2 9q-16 4 -28 12 q-204 -190 -383 -284q198 -59 414 -176zM2155 -90q5 54 -39 107.5t-104 80t-102 14.5q-38 -11 -72.5 -70.5t-51.5 -129.5q0 -16 3 -30q10 -49 53 -79t94 -28q54 2 119 42t100 93z" />
<glyph unicode="&#xf23d;" horiz-adv-x="2304" d="M1524 -25q0 -68 -48 -116t-116 -48t-116.5 48t-48.5 116t48.5 116.5t116.5 48.5t116 -48.5t48 -116.5zM775 -25q0 -68 -48.5 -116t-116.5 -48t-116 48t-48 116t48 116.5t116 48.5t116.5 -48.5t48.5 -116.5zM0 1469q57 -60 110.5 -104.5t121 -82t136 -63t166 -45.5 t200 -31.5t250 -18.5t304 -9.5t372.5 -2.5q139 0 244.5 -5t181 -16.5t124 -27.5t71 -39.5t24 -51.5t-19.5 -64t-56.5 -76.5t-89.5 -91t-116 -104.5t-139 -119q-185 -157 -286 -247q29 51 76.5 109t94 105.5t94.5 98.5t83 91.5t54 80.5t13 70t-45.5 55.5t-116.5 41t-204 23.5 t-304 5q-168 -2 -314 6t-256 23t-204.5 41t-159.5 51.5t-122.5 62.5t-91.5 66.5t-68 71.5t-50.5 69.5t-40 68t-36.5 59.5z" />
@ -600,11 +600,11 @@
<glyph unicode="&#xf267;" horiz-adv-x="1792" d="M949 643q0 -26 -16.5 -45t-41.5 -19q-26 0 -45 16.5t-19 41.5q0 26 17 45t42 19t44 -16.5t19 -41.5zM964 585l350 581q-9 -8 -67.5 -62.5t-125.5 -116.5t-136.5 -127t-117 -110.5t-50.5 -51.5l-349 -580q7 7 67 62t126 116.5t136 127t117 111t50 50.5zM1611 640 q0 -201 -104 -371q-3 2 -17 11t-26.5 16.5t-16.5 7.5q-13 0 -13 -13q0 -10 59 -44q-74 -112 -184.5 -190.5t-241.5 -110.5l-16 67q-1 10 -15 10q-5 0 -8 -5.5t-2 -9.5l16 -68q-72 -15 -146 -15q-199 0 -372 105q1 2 13 20.5t21.5 33.5t9.5 19q0 13 -13 13q-6 0 -17 -14.5 t-22.5 -34.5t-13.5 -23q-113 75 -192 187.5t-110 244.5l69 15q10 3 10 15q0 5 -5.5 8t-10.5 2l-68 -15q-14 72 -14 139q0 206 109 379q2 -1 18.5 -12t30 -19t17.5 -8q13 0 13 12q0 6 -12.5 15.5t-32.5 21.5l-20 12q77 112 189 189t244 107l15 -67q2 -10 15 -10q5 0 8 5.5 t2 10.5l-15 66q71 13 134 13q204 0 379 -109q-39 -56 -39 -65q0 -13 12 -13q11 0 48 64q111 -75 187.5 -186t107.5 -241l-56 -12q-10 -2 -10 -16q0 -5 5.5 -8t9.5 -2l57 13q14 -72 14 -140zM1696 640q0 163 -63.5 311t-170.5 255t-255 170.5t-311 63.5t-311 -63.5 t-255 -170.5t-170.5 -255t-63.5 -311t63.5 -311t170.5 -255t255 -170.5t311 -63.5t311 63.5t255 170.5t170.5 255t63.5 311zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191 t191 -286t71 -348z" />
<glyph unicode="&#xf268;" horiz-adv-x="1792" d="M893 1536q240 2 451 -120q232 -134 352 -372l-742 39q-160 9 -294 -74.5t-185 -229.5l-276 424q128 159 311 245.5t383 87.5zM146 1131l337 -663q72 -143 211 -217t293 -45l-230 -451q-212 33 -385 157.5t-272.5 316t-99.5 411.5q0 267 146 491zM1732 962 q58 -150 59.5 -310.5t-48.5 -306t-153 -272t-246 -209.5q-230 -133 -498 -119l405 623q88 131 82.5 290.5t-106.5 277.5zM896 942q125 0 213.5 -88.5t88.5 -213.5t-88.5 -213.5t-213.5 -88.5t-213.5 88.5t-88.5 213.5t88.5 213.5t213.5 88.5z" />
<glyph unicode="&#xf269;" horiz-adv-x="1792" d="M903 -256q-283 0 -504.5 150.5t-329.5 398.5q-58 131 -67 301t26 332.5t111 312t179 242.5l-11 -281q11 14 68 15.5t70 -15.5q42 81 160.5 138t234.5 59q-54 -45 -119.5 -148.5t-58.5 -163.5q25 -8 62.5 -13.5t63 -7.5t68 -4t50.5 -3q15 -5 9.5 -45.5t-30.5 -75.5 q-5 -7 -16.5 -18.5t-56.5 -35.5t-101 -34l15 -189l-139 67q-18 -43 -7.5 -81.5t36 -66.5t65.5 -41.5t81 -6.5q51 9 98 34.5t83.5 45t73.5 17.5q61 -4 89.5 -33t19.5 -65q-1 -2 -2.5 -5.5t-8.5 -12.5t-18 -15.5t-31.5 -10.5t-46.5 -1q-60 -95 -144.5 -135.5t-209.5 -29.5 q74 -61 162.5 -82.5t168.5 -6t154.5 52t128 87.5t80.5 104q43 91 39 192.5t-37.5 188.5t-78.5 125q87 -38 137 -79.5t77 -112.5q15 170 -57.5 343t-209.5 284q265 -77 412 -279.5t151 -517.5q2 -127 -40.5 -255t-123.5 -238t-189 -196t-247.5 -135.5t-288.5 -49.5z" />
<glyph unicode="&#xf26a;" d="M768 -92q77 0 139.5 63t100.5 166t59 234.5t21 268.5t-21 268.5t-59 234.5t-100.5 166t-139.5 63t-139.5 -63t-100.5 -166t-59 -234.5t-21 -268.5t21 -268.5t59 -234.5t100.5 -166t139.5 -63zM768 -256q-184 0 -333 77t-240 203t-141 287t-50 329t50 329t141 287t240 203 t333 77q148 0 274 -50t214.5 -136t151.5 -201t92.5 -244t29.5 -265t-29.5 -265t-92.5 -244t-151.5 -201t-214.5 -136t-274 -50z" />
<glyph unicode="&#xf26b;" horiz-adv-x="1792" d="M716 -69q-143 35 -261.5 114t-197.5 191q-139 -300 -17 -398q26 -21 85 -24.5t127.5 9.5t141 41.5t122.5 66.5zM693 762h452q0 108 -61.5 169t-168.5 61q-103 0 -162.5 -62.5t-59.5 -167.5zM1724 1137h-34q26 102 22.5 170t-25 110t-63.5 57t-93.5 11t-115 -26.5 t-128.5 -56.5t-134 -79q129 -37 238.5 -113.5t185 -179t110 -231.5t15.5 -262h-1005q0 -60 10 -106t34 -85t69.5 -60t112.5 -21q87 0 142.5 44t72.5 122h540q-71 -230 -281.5 -377t-477.5 -147q-83 0 -159 15q-35 -40 -151 -94t-248 -78t-219 35q-78 60 -100 159t7 214 t88 242t143.5 248t173.5 226.5t177.5 183.5t156.5 112v24q-120 -37 -258.5 -137.5t-240.5 -207t-159 -195.5q4 106 34 201t80 169t118 135.5t147.5 100.5t168 65.5t180.5 29.5t185 -8q310 186 503 189h7q57 0 103 -18q80 -30 98 -132.5t-30 -248.5z" />
<glyph unicode="&#xf26a;" horiz-adv-x="1792" d="M1493 1308q-165 110 -359 110q-155 0 -293 -73t-240 -200q-75 -93 -119.5 -218t-48.5 -266v-42q4 -141 48.5 -266t119.5 -218q102 -127 240 -200t293 -73q194 0 359 110q-121 -108 -274.5 -168t-322.5 -60q-29 0 -43 1q-175 8 -333 82t-272 193t-181 281t-67 339 q0 182 71 348t191 286t286 191t348 71h3q168 -1 320.5 -60.5t273.5 -167.5zM1792 640q0 -192 -77 -362.5t-213 -296.5q-104 -63 -222 -63q-137 0 -255 84q154 56 253.5 233t99.5 405q0 227 -99 404t-253 234q119 83 254 83q119 0 226 -65q135 -125 210.5 -295t75.5 -361z " />
<glyph unicode="&#xf26b;" horiz-adv-x="1792" d="M1792 599q0 -56 -7 -104h-1151q0 -146 109.5 -244.5t257.5 -98.5q99 0 185.5 46.5t136.5 130.5h423q-56 -159 -170.5 -281t-267.5 -188.5t-321 -66.5q-187 0 -356 83q-228 -116 -394 -116q-237 0 -237 263q0 115 45 275q17 60 109 229q199 360 475 606 q-184 -79 -427 -354q63 274 283.5 449.5t501.5 175.5q30 0 45 -1q255 117 433 117q64 0 116 -13t94.5 -40.5t66.5 -76.5t24 -115q0 -116 -75 -286q101 -182 101 -390zM1722 1239q0 83 -53 132t-137 49q-108 0 -254 -70q121 -47 222.5 -131.5t170.5 -195.5q51 135 51 216z M128 2q0 -86 48.5 -132.5t134.5 -46.5q115 0 266 83q-122 72 -213.5 183t-137.5 245q-98 -205 -98 -332zM632 715h728q-5 142 -113 237t-251 95q-144 0 -251.5 -95t-112.5 -237z" />
<glyph unicode="&#xf26c;" horiz-adv-x="2048" d="M1792 288v960q0 13 -9.5 22.5t-22.5 9.5h-1600q-13 0 -22.5 -9.5t-9.5 -22.5v-960q0 -13 9.5 -22.5t22.5 -9.5h1600q13 0 22.5 9.5t9.5 22.5zM1920 1248v-960q0 -66 -47 -113t-113 -47h-736v-128h352q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-832q-14 0 -23 9t-9 23 v64q0 14 9 23t23 9h352v128h-736q-66 0 -113 47t-47 113v960q0 66 47 113t113 47h1600q66 0 113 -47t47 -113z" />
<glyph unicode="&#xf26d;" horiz-adv-x="1792" d="M138 1408h197q-70 -64 -126 -149q-36 -56 -59 -115t-30 -125.5t-8.5 -120t10.5 -132t21 -126t28 -136.5q4 -19 6 -28q51 -238 81 -329q57 -171 152 -275h-272q-48 0 -82 34t-34 82v1304q0 48 34 82t82 34zM1346 1408h308q48 0 82 -34t34 -82v-1304q0 -48 -34 -82t-82 -34 h-178q212 210 196 565l-469 -101q-2 -45 -12 -82t-31 -72t-59.5 -59.5t-93.5 -36.5q-123 -26 -199 40q-32 27 -53 61t-51.5 129t-64.5 258q-35 163 -45.5 263t-5.5 139t23 77q20 41 62.5 73t102.5 45q45 12 83.5 6.5t67 -17t54 -35t43 -48t34.5 -56.5l468 100 q-68 175 -180 287z" />
<glyph unicode="&#xf26e;" horiz-adv-x="2304" d="M1391 390v0l-1 1q-15 18 -34.5 37.5t-62.5 57.5t-93.5 62t-95.5 24q-48 0 -83 -21.5t-51 -54t-23 -59t-7 -47.5v0v0q0 -21 7 -48t23 -59t51 -53.5t83 -21.5q45 0 95.5 24t94 62.5t62 57t34.5 37.5zM2103 390q0 21 -7 47.5t-23 59t-51 54t-83 21.5q-45 0 -95.5 -24 t-94 -62.5t-62 -57t-34.5 -37.5l-1 -1v0v0l1 -1q15 -18 34.5 -37.5t62.5 -57.5t93.5 -62t95.5 -24q48 0 83 21.5t51 53.5t23 59t7 48zM2304 393q0 -69 -24 -137.5t-68 -126t-116 -93.5t-159 -36q-68 0 -134 24t-113.5 58.5t-84.5 69.5t-59.5 59t-25.5 24t-22.5 -24 t-54.5 -58.5t-81.5 -69.5t-115 -59t-143.5 -24q-65 0 -123.5 22.5t-96.5 54t-66.5 66.5t-41 59.5t-12.5 32.5q0 -8 -8.5 -26.5t-25 -45.5t-47 -55t-69 -52.5t-96.5 -40t-125 -15.5q-71 0 -130 15.5t-98.5 39.5t-70.5 56.5t-48 63.5t-27.5 63.5t-14 54t-3.5 36.5h217 q0 -55 49 -107.5t126 -52.5q79 0 134.5 67t55.5 148q0 80 -52 136.5t-138 56.5q-5 0 -13 -0.5t-31 -5t-43 -12t-42 -24.5t-34 -40h-195l102 583h602v-174h-445q-27 -159 -41 -248q4 0 16.5 13t31.5 28.5t65 28.5t108 13t114 -20.5t82.5 -49.5t51.5 -58.5t31 -50t11 -20.5 t13 25t36.5 60.5t60.5 71.5t97 61t133 25t140.5 -25t115.5 -60.5t83.5 -71.5t56.5 -61t21 -25q2 0 22 25t56 60.5t83.5 71.5t115.5 61t140 25q92 0 164.5 -35t115.5 -93t65 -125t22 -137z" />
<glyph unicode="&#xf26e;" d="M1401 -11l-6 -6q-113 -114 -259 -175q-154 -64 -317 -64q-165 0 -317 64q-148 63 -259 175q-113 112 -175 258q-42 103 -54 189q-4 28 48 36q51 8 56 -20q1 -1 1 -4q18 -90 46 -159q50 -124 152 -226q98 -98 226 -152q132 -56 276 -56q143 0 276 56q128 55 225 152l6 6 q10 10 25 6q12 -3 33 -22q36 -37 17 -58zM929 604l-66 -66l63 -63q21 -21 -7 -49q-17 -17 -32 -17q-10 0 -19 10l-62 61l-66 -66q-5 -5 -15 -5q-15 0 -31 16l-2 2q-18 15 -18 29q0 7 8 17l66 65l-66 66q-16 16 14 45q18 18 31 18q6 0 13 -5l65 -66l65 65q18 17 48 -13 q27 -27 11 -44zM1400 547q0 -118 -46 -228q-45 -105 -126 -186q-80 -80 -187 -126t-228 -46t-228 46t-187 126q-82 82 -125 186q-15 32 -15 40h-1q-9 27 43 44q50 16 60 -12q37 -99 97 -167h1v339v2q3 136 102 232q105 103 253 103q147 0 251 -103t104 -249 q0 -147 -104.5 -251t-250.5 -104q-58 0 -112 16q-28 11 -13 61q16 51 44 43l14 -3q14 -3 32.5 -6t30.5 -3q104 0 176 71.5t72 174.5q0 101 -72 171q-71 71 -175 71q-107 0 -178 -80q-64 -72 -64 -160v-413q110 -67 242 -67q96 0 185 36.5t156 103.5t103.5 155t36.5 183 q0 198 -141 339q-140 140 -339 140q-200 0 -340 -140q-53 -53 -77 -87l-2 -2q-8 -11 -13 -15.5t-21.5 -9.5t-38.5 3q-21 5 -36.5 16.5t-15.5 26.5v680q0 15 10.5 26.5t27.5 11.5h877q30 0 30 -55t-30 -55h-811v-483h1q40 42 102 84t108 61q109 46 231 46q121 0 228 -46 t187 -126q81 -81 126 -186q46 -112 46 -229zM1369 1128q9 -8 9 -18t-5.5 -18t-16.5 -21q-26 -26 -39 -26q-9 0 -16 7q-106 91 -207 133q-128 56 -276 56q-133 0 -262 -49q-27 -10 -45 37q-9 25 -8 38q3 16 16 20q130 57 299 57q164 0 316 -64q137 -58 235 -152z" />
<glyph unicode="&#xf270;" horiz-adv-x="1792" d="M1551 60q15 6 26 3t11 -17.5t-15 -33.5q-13 -16 -44 -43.5t-95.5 -68t-141 -74t-188 -58t-229.5 -24.5q-119 0 -238 31t-209 76.5t-172.5 104t-132.5 105t-84 87.5q-8 9 -10 16.5t1 12t8 7t11.5 2t11.5 -4.5q192 -117 300 -166q389 -176 799 -90q190 40 391 135z M1758 175q11 -16 2.5 -69.5t-28.5 -102.5q-34 -83 -85 -124q-17 -14 -26 -9t0 24q21 45 44.5 121.5t6.5 98.5q-5 7 -15.5 11.5t-27 6t-29.5 2.5t-35 0t-31.5 -2t-31 -3t-22.5 -2q-6 -1 -13 -1.5t-11 -1t-8.5 -1t-7 -0.5h-5.5h-4.5t-3 0.5t-2 1.5l-1.5 3q-6 16 47 40t103 30 q46 7 108 1t76 -24zM1364 618q0 -31 13.5 -64t32 -58t37.5 -46t33 -32l13 -11l-227 -224q-40 37 -79 75.5t-58 58.5l-19 20q-11 11 -25 33q-38 -59 -97.5 -102.5t-127.5 -63.5t-140 -23t-137.5 21t-117.5 65.5t-83 113t-31 162.5q0 84 28 154t72 116.5t106.5 83t122.5 57 t130 34.5t119.5 18.5t99.5 6.5v127q0 65 -21 97q-34 53 -121 53q-6 0 -16.5 -1t-40.5 -12t-56 -29.5t-56 -59.5t-48 -96l-294 27q0 60 22 119t67 113t108 95t151.5 65.5t190.5 24.5q100 0 181 -25t129.5 -61.5t81 -83t45 -86t12.5 -73.5v-589zM692 597q0 -86 70 -133 q66 -44 139 -22q84 25 114 123q14 45 14 101v162q-59 -2 -111 -12t-106.5 -33.5t-87 -71t-32.5 -114.5z" />
<glyph unicode="&#xf271;" horiz-adv-x="1792" d="M1536 1280q52 0 90 -38t38 -90v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h128zM1152 1376v-288q0 -14 9 -23t23 -9 h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM384 1376v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23zM1536 -128v1024h-1408v-1024h1408zM896 448h224q14 0 23 -9t9 -23v-64q0 -14 -9 -23t-23 -9h-224 v-224q0 -14 -9 -23t-23 -9h-64q-14 0 -23 9t-9 23v224h-224q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h224v224q0 14 9 23t23 9h64q14 0 23 -9t9 -23v-224z" />
<glyph unicode="&#xf272;" horiz-adv-x="1792" d="M1152 416v-64q0 -14 -9 -23t-23 -9h-576q-14 0 -23 9t-9 23v64q0 14 9 23t23 9h576q14 0 23 -9t9 -23zM128 -128h1408v1024h-1408v-1024zM512 1088v288q0 14 -9 23t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1280 1088v288q0 14 -9 23 t-23 9h-64q-14 0 -23 -9t-9 -23v-288q0 -14 9 -23t23 -9h64q14 0 23 9t9 23zM1664 1152v-1280q0 -52 -38 -90t-90 -38h-1408q-52 0 -90 38t-38 90v1280q0 52 38 90t90 38h128v96q0 66 47 113t113 47h64q66 0 113 -47t47 -113v-96h384v96q0 66 47 113t113 47h64q66 0 113 -47 t47 -113v-96h128q52 0 90 -38t38 -90z" />
@ -621,20 +621,65 @@
<glyph unicode="&#xf27d;" horiz-adv-x="1792" d="M1709 1018q-10 -236 -332 -651q-333 -431 -562 -431q-142 0 -240 263q-44 160 -132 482q-72 262 -157 262q-18 0 -127 -76l-77 98q24 21 108 96.5t130 115.5q156 138 241 146q95 9 153 -55.5t81 -203.5q44 -287 66 -373q55 -249 120 -249q51 0 154 161q101 161 109 246 q13 139 -109 139q-57 0 -121 -26q120 393 459 382q251 -8 236 -326z" />
<glyph unicode="&#xf27e;" d="M0 1408h1536v-1536h-1536v1536zM1085 293l-221 631l221 297h-634l221 -297l-221 -631l317 -304z" />
<glyph unicode="&#xf280;" d="M0 1408h1536v-1536h-1536v1536zM908 1088l-12 -33l75 -83l-31 -114l25 -25l107 57l107 -57l25 25l-31 114l75 83l-12 33h-95l-53 96h-32l-53 -96h-95zM641 925q32 0 44.5 -16t11.5 -63l174 21q0 55 -17.5 92.5t-50.5 56t-69 25.5t-85 7q-133 0 -199 -57.5t-66 -182.5v-72 h-96v-128h76q20 0 20 -8v-382q0 -14 -5 -20t-18 -7l-73 -7v-88h448v86l-149 14q-6 1 -8.5 1.5t-3.5 2.5t-0.5 4t1 7t0.5 10v387h191l38 128h-231q-6 0 -2 6t4 9v80q0 27 1.5 40.5t7.5 28t19.5 20t36.5 5.5zM1248 96v86l-54 9q-7 1 -9.5 2.5t-2.5 3t1 7.5t1 12v520h-275 l-23 -101l83 -22q23 -7 23 -27v-370q0 -14 -6 -18.5t-20 -6.5l-70 -9v-86h352z" />
<glyph unicode="&#xf281;" horiz-adv-x="1792" />
<glyph unicode="&#xf282;" horiz-adv-x="1792" />
<glyph unicode="&#xf283;" horiz-adv-x="1792" />
<glyph unicode="&#xf284;" horiz-adv-x="1792" />
<glyph unicode="&#xf285;" horiz-adv-x="1792" />
<glyph unicode="&#xf286;" horiz-adv-x="1792" />
<glyph unicode="&#xf287;" horiz-adv-x="1792" />
<glyph unicode="&#xf288;" horiz-adv-x="1792" />
<glyph unicode="&#xf289;" horiz-adv-x="1792" />
<glyph unicode="&#xf28a;" horiz-adv-x="1792" />
<glyph unicode="&#xf28b;" horiz-adv-x="1792" />
<glyph unicode="&#xf28c;" horiz-adv-x="1792" />
<glyph unicode="&#xf28d;" horiz-adv-x="1792" />
<glyph unicode="&#xf28e;" horiz-adv-x="1792" />
<glyph unicode="&#xf281;" horiz-adv-x="1792" d="M1792 690q0 -58 -29.5 -105.5t-79.5 -72.5q12 -46 12 -96q0 -155 -106.5 -287t-290.5 -208.5t-400 -76.5t-399.5 76.5t-290 208.5t-106.5 287q0 47 11 94q-51 25 -82 73.5t-31 106.5q0 82 58 140.5t141 58.5q85 0 145 -63q218 152 515 162l116 521q3 13 15 21t26 5 l369 -81q18 37 54 59.5t79 22.5q62 0 106 -43.5t44 -105.5t-44 -106t-106 -44t-105.5 43.5t-43.5 105.5l-334 74l-104 -472q300 -9 519 -160q58 61 143 61q83 0 141 -58.5t58 -140.5zM418 491q0 -62 43.5 -106t105.5 -44t106 44t44 106t-44 105.5t-106 43.5q-61 0 -105 -44 t-44 -105zM1228 136q11 11 11 26t-11 26q-10 10 -25 10t-26 -10q-41 -42 -121 -62t-160 -20t-160 20t-121 62q-11 10 -26 10t-25 -10q-11 -10 -11 -25.5t11 -26.5q43 -43 118.5 -68t122.5 -29.5t91 -4.5t91 4.5t122.5 29.5t118.5 68zM1225 341q62 0 105.5 44t43.5 106 q0 61 -44 105t-105 44q-62 0 -106 -43.5t-44 -105.5t44 -106t106 -44z" />
<glyph unicode="&#xf282;" horiz-adv-x="1792" d="M69 741h1q16 126 58.5 241.5t115 217t167.5 176t223.5 117.5t276.5 43q231 0 414 -105.5t294 -303.5q104 -187 104 -442v-188h-1125q1 -111 53.5 -192.5t136.5 -122.5t189.5 -57t213 -3t208 46.5t173.5 84.5v-377q-92 -55 -229.5 -92t-312.5 -38t-316 53 q-189 73 -311.5 249t-124.5 372q-3 242 111 412t325 268q-48 -60 -78 -125.5t-46 -159.5h635q8 77 -8 140t-47 101.5t-70.5 66.5t-80.5 41t-75 20.5t-56 8.5l-22 1q-135 -5 -259.5 -44.5t-223.5 -104.5t-176 -140.5t-138 -163.5z" />
<glyph unicode="&#xf283;" horiz-adv-x="2304" d="M0 32v608h2304v-608q0 -66 -47 -113t-113 -47h-1984q-66 0 -113 47t-47 113zM640 256v-128h384v128h-384zM256 256v-128h256v128h-256zM2144 1408q66 0 113 -47t47 -113v-224h-2304v224q0 66 47 113t113 47h1984z" />
<glyph unicode="&#xf284;" horiz-adv-x="1792" d="M1549 857q55 0 85.5 -28.5t30.5 -83.5t-34 -82t-91 -27h-136v-177h-25v398h170zM1710 267l-4 -11l-5 -10q-113 -230 -330.5 -366t-474.5 -136q-182 0 -348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71q244 0 454.5 -124t329.5 -338l2 -4l8 -16 q-30 -15 -136.5 -68.5t-163.5 -84.5q-6 -3 -479 -268q384 -183 799 -366zM896 -234q250 0 462.5 132.5t322.5 357.5l-287 129q-72 -140 -206 -222t-292 -82q-151 0 -280 75t-204 204t-75 280t75 280t204 204t280 75t280 -73.5t204 -204.5l280 143q-116 208 -321 329 t-443 121q-119 0 -232.5 -31.5t-209 -87.5t-176.5 -137t-137 -176.5t-87.5 -209t-31.5 -232.5t31.5 -232.5t87.5 -209t137 -176.5t176.5 -137t209 -87.5t232.5 -31.5z" />
<glyph unicode="&#xf285;" horiz-adv-x="1792" d="M1427 827l-614 386l92 151h855zM405 562l-184 116v858l1183 -743zM1424 697l147 -95v-858l-532 335zM1387 718l-500 -802h-855l356 571z" />
<glyph unicode="&#xf286;" horiz-adv-x="1792" d="M640 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1152 528v224q0 16 -16 16h-96q-16 0 -16 -16v-224q0 -16 16 -16h96q16 0 16 16zM1664 496v-752h-640v320q0 80 -56 136t-136 56t-136 -56t-56 -136v-320h-640v752q0 16 16 16h96 q16 0 16 -16v-112h128v624q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h16v393q-32 19 -32 55q0 26 19 45t45 19t45 -19t19 -45q0 -36 -32 -55v-9h272q16 0 16 -16v-224q0 -16 -16 -16h-272v-128h16q16 0 16 -16v-112h128 v112q0 16 16 16h96q16 0 16 -16v-112h128v112q0 16 16 16h96q16 0 16 -16v-624h128v112q0 16 16 16h96q16 0 16 -16z" />
<glyph unicode="&#xf287;" horiz-adv-x="2304" d="M2288 731q16 -8 16 -27t-16 -27l-320 -192q-8 -5 -16 -5q-9 0 -16 4q-16 10 -16 28v128h-858q37 -58 83 -165q16 -37 24.5 -55t24 -49t27 -47t27 -34t31.5 -26t33 -8h96v96q0 14 9 23t23 9h320q14 0 23 -9t9 -23v-320q0 -14 -9 -23t-23 -9h-320q-14 0 -23 9t-9 23v96h-96 q-32 0 -61 10t-51 23.5t-45 40.5t-37 46t-33.5 57t-28.5 57.5t-28 60.5q-23 53 -37 81.5t-36 65t-44.5 53.5t-46.5 17h-360q-22 -84 -91 -138t-157 -54q-106 0 -181 75t-75 181t75 181t181 75q88 0 157 -54t91 -138h104q24 0 46.5 17t44.5 53.5t36 65t37 81.5q19 41 28 60.5 t28.5 57.5t33.5 57t37 46t45 40.5t51 23.5t61 10h107q21 57 70 92.5t111 35.5q80 0 136 -56t56 -136t-56 -136t-136 -56q-62 0 -111 35.5t-70 92.5h-107q-17 0 -33 -8t-31.5 -26t-27 -34t-27 -47t-24 -49t-24.5 -55q-46 -107 -83 -165h1114v128q0 18 16 28t32 -1z" />
<glyph unicode="&#xf288;" horiz-adv-x="1792" d="M1150 774q0 -56 -39.5 -95t-95.5 -39h-253v269h253q56 0 95.5 -39.5t39.5 -95.5zM1329 774q0 130 -91.5 222t-222.5 92h-433v-896h180v269h253q130 0 222 91.5t92 221.5zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348 t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
<glyph unicode="&#xf289;" horiz-adv-x="2304" d="M1645 438q0 59 -34 106.5t-87 68.5q-7 -45 -23 -92q-7 -24 -27.5 -38t-44.5 -14q-12 0 -24 3q-31 10 -45 38.5t-4 58.5q23 71 23 143q0 123 -61 227.5t-166 165.5t-228 61q-134 0 -247 -73t-167 -194q108 -28 188 -106q22 -23 22 -55t-22 -54t-54 -22t-55 22 q-75 75 -180 75q-106 0 -181 -74.5t-75 -180.5t75 -180.5t181 -74.5h1046q79 0 134.5 55.5t55.5 133.5zM1798 438q0 -142 -100.5 -242t-242.5 -100h-1046q-169 0 -289 119.5t-120 288.5q0 153 100 267t249 136q62 184 221 298t354 114q235 0 408.5 -158.5t196.5 -389.5 q116 -25 192.5 -118.5t76.5 -214.5zM2048 438q0 -175 -97 -319q-23 -33 -64 -33q-24 0 -43 13q-26 17 -32 48.5t12 57.5q71 104 71 233t-71 233q-18 26 -12 57t32 49t57.5 11.5t49.5 -32.5q97 -142 97 -318zM2304 438q0 -244 -134 -443q-23 -34 -64 -34q-23 0 -42 13 q-26 18 -32.5 49t11.5 57q108 164 108 358q0 195 -108 357q-18 26 -11.5 57.5t32.5 48.5q26 18 57 12t49 -33q134 -198 134 -442z" />
<glyph unicode="&#xf28a;" d="M1500 -13q0 -89 -63 -152.5t-153 -63.5t-153.5 63.5t-63.5 152.5q0 90 63.5 153.5t153.5 63.5t153 -63.5t63 -153.5zM1267 268q-115 -15 -192.5 -102.5t-77.5 -205.5q0 -74 33 -138q-146 -78 -379 -78q-109 0 -201 21t-153.5 54.5t-110.5 76.5t-76 85t-44.5 83 t-23.5 66.5t-6 39.5q0 19 4.5 42.5t18.5 56t36.5 58t64 43.5t94.5 18t94 -17.5t63 -41t35.5 -53t17.5 -49t4 -33.5q0 -34 -23 -81q28 -27 82 -42t93 -17l40 -1q115 0 190 51t75 133q0 26 -9 48.5t-31.5 44.5t-49.5 41t-74 44t-93.5 47.5t-119.5 56.5q-28 13 -43 20 q-116 55 -187 100t-122.5 102t-72 125.5t-20.5 162.5q0 78 20.5 150t66 137.5t112.5 114t166.5 77t221.5 28.5q120 0 220 -26t164.5 -67t109.5 -94t64 -105.5t19 -103.5q0 -46 -15 -82.5t-36.5 -58t-48.5 -36t-49 -19.5t-39 -5h-8h-32t-39 5t-44 14t-41 28t-37 46t-24 70.5 t-10 97.5q-15 16 -59 25.5t-81 10.5l-37 1q-68 0 -117.5 -31t-70.5 -70t-21 -76q0 -24 5 -43t24 -46t53 -51t97 -53.5t150 -58.5q76 -25 138.5 -53.5t109 -55.5t83 -59t60.5 -59.5t41 -62.5t26.5 -62t14.5 -63.5t6 -62t1 -62.5z" />
<glyph unicode="&#xf28b;" d="M704 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1152 352v576q0 14 -9 23t-23 9h-256q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h256q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103 t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
<glyph unicode="&#xf28c;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273 t73 -273t198 -198t273 -73zM864 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h192q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-192z" />
<glyph unicode="&#xf28d;" d="M1088 352v576q0 14 -9 23t-23 9h-576q-14 0 -23 -9t-9 -23v-576q0 -14 9 -23t23 -9h576q14 0 23 9t9 23zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
<glyph unicode="&#xf28e;" d="M768 1408q209 0 385.5 -103t279.5 -279.5t103 -385.5t-103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103zM768 96q148 0 273 73t198 198t73 273t-73 273t-198 198t-273 73t-273 -73t-198 -198t-73 -273 t73 -273t198 -198t273 -73zM480 320q-14 0 -23 9t-9 23v576q0 14 9 23t23 9h576q14 0 23 -9t9 -23v-576q0 -14 -9 -23t-23 -9h-576z" />
<glyph unicode="&#xf290;" horiz-adv-x="1792" d="M1757 128l35 -313q3 -28 -16 -50q-19 -21 -48 -21h-1664q-29 0 -48 21q-19 22 -16 50l35 313h1722zM1664 967l86 -775h-1708l86 775q3 24 21 40.5t43 16.5h256v-128q0 -53 37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5v128h384v-128q0 -53 37.5 -90.5t90.5 -37.5 t90.5 37.5t37.5 90.5v128h256q25 0 43 -16.5t21 -40.5zM1280 1152v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 106 -75 181t-181 75t-181 -75t-75 -181v-256q0 -26 -19 -45t-45 -19t-45 19t-19 45v256q0 159 112.5 271.5t271.5 112.5t271.5 -112.5t112.5 -271.5z" />
<glyph unicode="&#xf291;" horiz-adv-x="2048" d="M1920 768q53 0 90.5 -37.5t37.5 -90.5t-37.5 -90.5t-90.5 -37.5h-15l-115 -662q-8 -46 -44 -76t-82 -30h-1280q-46 0 -82 30t-44 76l-115 662h-15q-53 0 -90.5 37.5t-37.5 90.5t37.5 90.5t90.5 37.5h1792zM485 -32q26 2 43.5 22.5t15.5 46.5l-32 416q-2 26 -22.5 43.5 t-46.5 15.5t-43.5 -22.5t-15.5 -46.5l32 -416q2 -25 20.5 -42t43.5 -17h5zM896 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1280 32v416q0 26 -19 45t-45 19t-45 -19t-19 -45v-416q0 -26 19 -45t45 -19t45 19t19 45zM1632 27l32 416 q2 26 -15.5 46.5t-43.5 22.5t-46.5 -15.5t-22.5 -43.5l-32 -416q-2 -26 15.5 -46.5t43.5 -22.5h5q25 0 43.5 17t20.5 42zM476 1244l-93 -412h-132l101 441q19 88 89 143.5t160 55.5h167q0 26 19 45t45 19h384q26 0 45 -19t19 -45h167q90 0 160 -55.5t89 -143.5l101 -441 h-132l-93 412q-11 44 -45.5 72t-79.5 28h-167q0 -26 -19 -45t-45 -19h-384q-26 0 -45 19t-19 45h-167q-45 0 -79.5 -28t-45.5 -72z" />
<glyph unicode="&#xf292;" horiz-adv-x="1792" d="M991 512l64 256h-254l-64 -256h254zM1759 1016l-56 -224q-7 -24 -31 -24h-327l-64 -256h311q15 0 25 -12q10 -14 6 -28l-56 -224q-5 -24 -31 -24h-327l-81 -328q-7 -24 -31 -24h-224q-16 0 -26 12q-9 12 -6 28l78 312h-254l-81 -328q-7 -24 -31 -24h-225q-15 0 -25 12 q-9 12 -6 28l78 312h-311q-15 0 -25 12q-9 12 -6 28l56 224q7 24 31 24h327l64 256h-311q-15 0 -25 12q-10 14 -6 28l56 224q5 24 31 24h327l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h254l81 328q7 24 32 24h224q15 0 25 -12q9 -12 6 -28l-78 -312h311 q15 0 25 -12q9 -12 6 -28z" />
<glyph unicode="&#xf293;" d="M841 483l148 -148l-149 -149zM840 1094l149 -149l-148 -148zM710 -130l464 464l-306 306l306 306l-464 464v-611l-255 255l-93 -93l320 -321l-320 -321l93 -93l255 255v-611zM1429 640q0 -209 -32 -365.5t-87.5 -257t-140.5 -162.5t-181.5 -86.5t-219.5 -24.5 t-219.5 24.5t-181.5 86.5t-140.5 162.5t-87.5 257t-32 365.5t32 365.5t87.5 257t140.5 162.5t181.5 86.5t219.5 24.5t219.5 -24.5t181.5 -86.5t140.5 -162.5t87.5 -257t32 -365.5z" />
<glyph unicode="&#xf294;" horiz-adv-x="1024" d="M596 113l173 172l-173 172v-344zM596 823l173 172l-173 172v-344zM628 640l356 -356l-539 -540v711l-297 -296l-108 108l372 373l-372 373l108 108l297 -296v711l539 -540z" />
<glyph unicode="&#xf295;" d="M1280 256q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM512 1024q0 52 -38 90t-90 38t-90 -38t-38 -90t38 -90t90 -38t90 38t38 90zM1536 256q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5t271.5 -112.5 t112.5 -271.5zM1440 1344q0 -20 -13 -38l-1056 -1408q-19 -26 -51 -26h-160q-26 0 -45 19t-19 45q0 20 13 38l1056 1408q19 26 51 26h160q26 0 45 -19t19 -45zM768 1024q0 -159 -112.5 -271.5t-271.5 -112.5t-271.5 112.5t-112.5 271.5t112.5 271.5t271.5 112.5 t271.5 -112.5t112.5 -271.5z" />
<glyph unicode="&#xf296;" horiz-adv-x="1792" d="M104 830l792 -1015l-868 630q-18 13 -25 34.5t0 42.5l101 308v0zM566 830h660l-330 -1015v0zM368 1442l198 -612h-462l198 612q8 23 33 23t33 -23zM1688 830l101 -308q7 -21 0 -42.5t-25 -34.5l-868 -630l792 1015v0zM1688 830h-462l198 612q8 23 33 23t33 -23z" />
<glyph unicode="&#xf297;" horiz-adv-x="1792" d="M384 704h160v224h-160v-224zM1221 372v92q-104 -36 -243 -38q-135 -1 -259.5 46.5t-220.5 122.5l1 -96q88 -80 212 -128.5t272 -47.5q129 0 238 49zM640 704h640v224h-640v-224zM1792 736q0 -187 -99 -352q89 -102 89 -229q0 -157 -129.5 -268t-313.5 -111 q-122 0 -225 52.5t-161 140.5q-19 -1 -57 -1t-57 1q-58 -88 -161 -140.5t-225 -52.5q-184 0 -313.5 111t-129.5 268q0 127 89 229q-99 165 -99 352q0 209 120 385.5t326.5 279.5t449.5 103t449.5 -103t326.5 -279.5t120 -385.5z" />
<glyph unicode="&#xf298;" d="M515 625v-128h-252v128h252zM515 880v-127h-252v127h252zM1273 369v-128h-341v128h341zM1273 625v-128h-672v128h672zM1273 880v-127h-672v127h672zM1408 20v1240q0 8 -6 14t-14 6h-32l-378 -256l-210 171l-210 -171l-378 256h-32q-8 0 -14 -6t-6 -14v-1240q0 -8 6 -14 t14 -6h1240q8 0 14 6t6 14zM553 1130l185 150h-406zM983 1130l221 150h-406zM1536 1260v-1240q0 -62 -43 -105t-105 -43h-1240q-62 0 -105 43t-43 105v1240q0 62 43 105t105 43h1240q62 0 105 -43t43 -105z" />
<glyph unicode="&#xf299;" horiz-adv-x="1792" d="M896 720q-104 196 -160 278q-139 202 -347 318q-34 19 -70 36q-89 40 -94 32t34 -38l39 -31q62 -43 112.5 -93.5t94.5 -116.5t70.5 -113t70.5 -131q9 -17 13 -25q44 -84 84 -153t98 -154t115.5 -150t131 -123.5t148.5 -90.5q153 -66 154 -60q1 3 -49 37q-53 36 -81 57 q-77 58 -179 211t-185 310zM549 177q-76 60 -132.5 125t-98 143.5t-71 154.5t-58.5 186t-52 209t-60.5 252t-76.5 289q273 0 497.5 -36t379 -92t271 -144.5t185.5 -172.5t110 -198.5t56 -199.5t12.5 -198.5t-9.5 -173t-20 -143.5t-13 -107l323 -327h-104l-281 285 q-22 -2 -91.5 -14t-121.5 -19t-138 -6t-160.5 17t-167.5 59t-179 111z" />
<glyph unicode="&#xf29a;" horiz-adv-x="1792" d="M1374 879q-6 26 -28.5 39.5t-48.5 7.5q-261 -62 -401 -62t-401 62q-26 6 -48.5 -7.5t-28.5 -39.5t7.5 -48.5t39.5 -28.5q194 -46 303 -58q-2 -158 -15.5 -269t-26.5 -155.5t-41 -115.5l-9 -21q-10 -25 1 -49t36 -34q9 -4 23 -4q44 0 60 41l8 20q54 139 71 259h42 q17 -120 71 -259l8 -20q16 -41 60 -41q14 0 23 4q25 10 36 34t1 49l-9 21q-28 71 -41 115.5t-26.5 155.5t-15.5 269q109 12 303 58q26 6 39.5 28.5t7.5 48.5zM1024 1024q0 53 -37.5 90.5t-90.5 37.5t-90.5 -37.5t-37.5 -90.5t37.5 -90.5t90.5 -37.5t90.5 37.5t37.5 90.5z M1600 640q0 -143 -55.5 -273.5t-150 -225t-225 -150t-273.5 -55.5t-273.5 55.5t-225 150t-150 225t-55.5 273.5t55.5 273.5t150 225t225 150t273.5 55.5t273.5 -55.5t225 -150t150 -225t55.5 -273.5zM896 1408q-156 0 -298 -61t-245 -164t-164 -245t-61 -298t61 -298 t164 -245t245 -164t298 -61t298 61t245 164t164 245t61 298t-61 298t-164 245t-245 164t-298 61zM1792 640q0 -182 -71 -348t-191 -286t-286 -191t-348 -71t-348 71t-286 191t-191 286t-71 348t71 348t191 286t286 191t348 71t348 -71t286 -191t191 -286t71 -348z" />
<glyph unicode="&#xf29b;" d="M1438 723q34 -35 29 -82l-44 -551q-4 -42 -34.5 -70t-71.5 -28q-6 0 -9 1q-44 3 -72.5 36.5t-25.5 77.5l35 429l-143 -8q55 -113 55 -240q0 -216 -148 -372l-137 137q91 101 91 235q0 145 -102.5 248t-247.5 103q-134 0 -236 -92l-137 138q120 114 284 141l264 300 l-149 87l-181 -161q-33 -30 -77 -27.5t-73 35.5t-26.5 77t34.5 73l239 213q26 23 60 26.5t64 -14.5l488 -283q36 -21 48 -68q17 -67 -26 -117l-205 -232l371 20q49 3 83 -32zM1240 1180q-74 0 -126 52t-52 126t52 126t126 52t126.5 -52t52.5 -126t-52.5 -126t-126.5 -52z M613 -62q106 0 196 61l139 -139q-146 -116 -335 -116q-148 0 -273.5 73t-198.5 198t-73 273q0 188 116 336l139 -139q-60 -88 -60 -197q0 -145 102.5 -247.5t247.5 -102.5z" />
<glyph unicode="&#xf29c;" d="M880 336v-160q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v160q0 14 9 23t23 9h160q14 0 23 -9t9 -23zM1136 832q0 -50 -15 -90t-45.5 -69t-52 -44t-59.5 -36q-32 -18 -46.5 -28t-26 -24t-11.5 -29v-32q0 -14 -9 -23t-23 -9h-160q-14 0 -23 9t-9 23v68q0 35 10.5 64.5 t24 47.5t39 35.5t41 25.5t44.5 21q53 25 75 43t22 49q0 42 -43.5 71.5t-95.5 29.5q-56 0 -95 -27q-29 -20 -80 -83q-9 -12 -25 -12q-11 0 -19 6l-108 82q-10 7 -12 20t5 23q122 192 349 192q129 0 238.5 -89.5t109.5 -214.5zM768 1280q-130 0 -248.5 -51t-204 -136.5 t-136.5 -204t-51 -248.5t51 -248.5t136.5 -204t204 -136.5t248.5 -51t248.5 51t204 136.5t136.5 204t51 248.5t-51 248.5t-136.5 204t-204 136.5t-248.5 51zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5 t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5t103 -385.5z" />
<glyph unicode="&#xf29d;" horiz-adv-x="1408" d="M366 1225q-64 0 -110 45.5t-46 110.5q0 64 46 109.5t110 45.5t109.5 -45.5t45.5 -109.5q0 -65 -45.5 -110.5t-109.5 -45.5zM917 583q0 -50 -30 -67.5t-63.5 -6.5t-47.5 34l-367 438q-7 12 -14 15.5t-11 1.5l-3 -3q-7 -8 4 -21l122 -139l1 -354l-161 -457 q-67 -192 -92 -234q-16 -26 -28 -32q-50 -26 -103 -1q-29 13 -41.5 43t-9.5 57q2 17 197 618l5 416l-85 -164l35 -222q4 -24 -1 -42t-14 -27.5t-19 -16t-17 -7.5l-7 -2q-19 -3 -34.5 3t-24 16t-14 22t-7.5 19.5t-2 9.5l-46 299l211 381q23 34 113 34q75 0 107 -40l424 -521 q7 -5 14 -17l3 -3l-1 -1q7 -13 7 -29zM514 433q43 -113 88.5 -225t69.5 -168l24 -55q36 -93 42 -125q11 -70 -36 -97q-35 -22 -66 -16t-51 22t-29 35h-1q-6 16 -8 25l-124 351zM1338 -159q31 -49 31 -57q0 -5 -3 -7q-9 -5 -14.5 0.5t-15.5 26t-16 30.5q-114 172 -423 661 q3 -1 7 1t7 4l3 2q11 9 11 17z" />
<glyph unicode="&#xf29e;" horiz-adv-x="2304" d="M504 542h171l-1 265zM1530 641q0 87 -50.5 140t-146.5 53h-54v-388h52q91 0 145 57t54 138zM956 1018l1 -756q0 -14 -9.5 -24t-23.5 -10h-216q-14 0 -23.5 10t-9.5 24v62h-291l-55 -81q-10 -15 -28 -15h-267q-21 0 -30.5 18t3.5 35l556 757q9 14 27 14h332q14 0 24 -10 t10 -24zM1783 641q0 -193 -125.5 -303t-324.5 -110h-270q-14 0 -24 10t-10 24v756q0 14 10 24t24 10h268q200 0 326 -109t126 -302zM1939 640q0 -11 -0.5 -29t-8 -71.5t-21.5 -102t-44.5 -108t-73.5 -102.5h-51q38 45 66.5 104.5t41.5 112t21 98t9 72.5l1 27q0 8 -0.5 22.5 t-7.5 60t-20 91.5t-41 111.5t-66 124.5h43q41 -47 72 -107t45.5 -111.5t23 -96t10.5 -70.5zM2123 640q0 -11 -0.5 -29t-8 -71.5t-21.5 -102t-45 -108t-74 -102.5h-51q38 45 66.5 104.5t41.5 112t21 98t9 72.5l1 27q0 8 -0.5 22.5t-7.5 60t-19.5 91.5t-40.5 111.5t-66 124.5 h43q41 -47 72 -107t45.5 -111.5t23 -96t10.5 -70.5zM2304 640q0 -11 -0.5 -29t-8 -71.5t-21.5 -102t-44.5 -108t-73.5 -102.5h-51q38 45 66 104.5t41 112t21 98t9 72.5l1 27q0 8 -0.5 22.5t-7.5 60t-19.5 91.5t-40.5 111.5t-66 124.5h43q41 -47 72 -107t45.5 -111.5t23 -96 t9.5 -70.5z" />
<glyph unicode="&#xf2a0;" horiz-adv-x="1408" d="M617 -153q0 11 -13 58t-31 107t-20 69q-1 4 -5 26.5t-8.5 36t-13.5 21.5q-15 14 -51 14q-23 0 -70 -5.5t-71 -5.5q-34 0 -47 11q-6 5 -11 15.5t-7.5 20t-6.5 24t-5 18.5q-37 128 -37 255t37 255q1 4 5 18.5t6.5 24t7.5 20t11 15.5q13 11 47 11q24 0 71 -5.5t70 -5.5 q36 0 51 14q9 8 13.5 21.5t8.5 36t5 26.5q2 9 20 69t31 107t13 58q0 22 -43.5 52.5t-75.5 42.5q-20 8 -45 8q-34 0 -98 -18q-57 -17 -96.5 -40.5t-71 -66t-46 -70t-45.5 -94.5q-6 -12 -9 -19q-49 -107 -68 -216t-19 -244t19 -244t68 -216q56 -122 83 -161q63 -91 179 -127 l6 -2q64 -18 98 -18q25 0 45 8q32 12 75.5 42.5t43.5 52.5zM776 760q-26 0 -45 19t-19 45.5t19 45.5q37 37 37 90q0 52 -37 91q-19 19 -19 45t19 45t45 19t45 -19q75 -75 75 -181t-75 -181q-21 -19 -45 -19zM957 579q-27 0 -45 19q-19 19 -19 45t19 45q112 114 112 272 t-112 272q-19 19 -19 45t19 45t45 19t45 -19q150 -150 150 -362t-150 -362q-18 -19 -45 -19zM1138 398q-27 0 -45 19q-19 19 -19 45t19 45q90 91 138.5 208t48.5 245t-48.5 245t-138.5 208q-19 19 -19 45t19 45t45 19t45 -19q109 -109 167 -249t58 -294t-58 -294t-167 -249 q-18 -19 -45 -19z" />
<glyph unicode="&#xf2a1;" horiz-adv-x="2176" d="M192 352q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM704 352q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM704 864q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1472 352 q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1984 352q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1472 864q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1984 864 q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM1984 1376q-66 0 -113 -47t-47 -113t47 -113t113 -47t113 47t47 113t-47 113t-113 47zM384 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 192q0 -80 -56 -136 t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM384 1216q0 -80 -56 -136t-136 -56 t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 192q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM896 1216q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM2176 192q0 -80 -56 -136t-136 -56t-136 56 t-56 136t56 136t136 56t136 -56t56 -136zM1664 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM2176 704q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136zM1664 1216q0 -80 -56 -136t-136 -56t-136 56t-56 136 t56 136t136 56t136 -56t56 -136zM2176 1216q0 -80 -56 -136t-136 -56t-136 56t-56 136t56 136t136 56t136 -56t56 -136z" />
<glyph unicode="&#xf2a2;" horiz-adv-x="1792" d="M128 -192q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM320 0q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45zM365 365l256 -256l-90 -90l-256 256zM704 384q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45t45 19t45 -19t19 -45z M1411 704q0 -59 -11.5 -108.5t-37.5 -93.5t-44 -67.5t-53 -64.5q-31 -35 -45.5 -54t-33.5 -50t-26.5 -64t-7.5 -74q0 -159 -112.5 -271.5t-271.5 -112.5q-26 0 -45 19t-19 45t19 45t45 19q106 0 181 75t75 181q0 57 11.5 105.5t37 91t43.5 66.5t52 63q40 46 59.5 72 t37.5 74.5t18 103.5q0 185 -131.5 316.5t-316.5 131.5t-316.5 -131.5t-131.5 -316.5q0 -26 -19 -45t-45 -19t-45 19t-19 45q0 117 45.5 223.5t123 184t184 123t223.5 45.5t223.5 -45.5t184 -123t123 -184t45.5 -223.5zM896 576q0 -26 -19 -45t-45 -19t-45 19t-19 45t19 45 t45 19t45 -19t19 -45zM1184 704q0 -26 -19 -45t-45 -19t-45 19t-19 45q0 93 -65.5 158.5t-158.5 65.5q-92 0 -158 -65.5t-66 -158.5q0 -26 -19 -45t-45 -19t-45 19t-19 45q0 146 103 249t249 103t249 -103t103 -249zM1578 993q10 -25 -1 -49t-36 -34q-9 -4 -23 -4 q-19 0 -35.5 11t-23.5 30q-68 178 -224 295q-21 16 -25 42t12 47q17 21 43 25t47 -12q183 -137 266 -351zM1788 1074q9 -25 -1.5 -49t-35.5 -34q-11 -4 -23 -4q-44 0 -60 41q-92 238 -297 393q-22 16 -25.5 42t12.5 47q16 22 42 25.5t47 -12.5q235 -175 341 -449z" />
<glyph unicode="&#xf2a3;" horiz-adv-x="2304" d="M1032 576q-59 2 -84 55q-17 34 -48 53.5t-68 19.5q-53 0 -90.5 -37.5t-37.5 -90.5q0 -56 36 -89l10 -8q34 -31 82 -31q37 0 68 19.5t48 53.5q25 53 84 55zM1600 704q0 56 -36 89l-10 8q-34 31 -82 31q-37 0 -68 -19.5t-48 -53.5q-25 -53 -84 -55q59 -2 84 -55 q17 -34 48 -53.5t68 -19.5q53 0 90.5 37.5t37.5 90.5zM1174 925q-17 -35 -55 -48t-73 4q-62 31 -134 31q-51 0 -99 -17q3 0 9.5 0.5t9.5 0.5q92 0 170.5 -50t118.5 -133q17 -36 3.5 -73.5t-49.5 -54.5q-18 -9 -39 -9q21 0 39 -9q36 -17 49.5 -54.5t-3.5 -73.5 q-40 -83 -118.5 -133t-170.5 -50h-6q-16 2 -44 4l-290 27l-239 -120q-14 -7 -29 -7q-40 0 -57 35l-160 320q-11 23 -4 47.5t29 37.5l209 119l148 267q17 155 91.5 291.5t195.5 236.5q31 25 70.5 21.5t64.5 -34.5t21.5 -70t-34.5 -65q-70 -59 -117 -128q123 84 267 101 q40 5 71.5 -19t35.5 -64q5 -40 -19 -71.5t-64 -35.5q-84 -10 -159 -55q46 10 99 10q115 0 218 -50q36 -18 49 -55.5t-5 -73.5zM2137 1085l160 -320q11 -23 4 -47.5t-29 -37.5l-209 -119l-148 -267q-17 -155 -91.5 -291.5t-195.5 -236.5q-26 -22 -61 -22q-45 0 -74 35 q-25 31 -21.5 70t34.5 65q70 59 117 128q-123 -84 -267 -101q-4 -1 -12 -1q-36 0 -63.5 24t-31.5 60q-5 40 19 71.5t64 35.5q84 10 159 55q-46 -10 -99 -10q-115 0 -218 50q-36 18 -49 55.5t5 73.5q17 35 55 48t73 -4q62 -31 134 -31q51 0 99 17q-3 0 -9.5 -0.5t-9.5 -0.5 q-92 0 -170.5 50t-118.5 133q-17 36 -3.5 73.5t49.5 54.5q18 9 39 9q-21 0 -39 9q-36 17 -49.5 54.5t3.5 73.5q40 83 118.5 133t170.5 50h6h1q14 -2 42 -4l291 -27l239 120q14 7 29 7q40 0 57 -35z" />
<glyph unicode="&#xf2a4;" horiz-adv-x="1792" d="M1056 704q0 -26 19 -45t45 -19t45 19t19 45q0 146 -103 249t-249 103t-249 -103t-103 -249q0 -26 19 -45t45 -19t45 19t19 45q0 93 66 158.5t158 65.5t158 -65.5t66 -158.5zM835 1280q-117 0 -223.5 -45.5t-184 -123t-123 -184t-45.5 -223.5q0 -26 19 -45t45 -19t45 19 t19 45q0 185 131.5 316.5t316.5 131.5t316.5 -131.5t131.5 -316.5q0 -55 -18 -103.5t-37.5 -74.5t-59.5 -72q-34 -39 -52 -63t-43.5 -66.5t-37 -91t-11.5 -105.5q0 -106 -75 -181t-181 -75q-26 0 -45 -19t-19 -45t19 -45t45 -19q159 0 271.5 112.5t112.5 271.5q0 41 7.5 74 t26.5 64t33.5 50t45.5 54q35 41 53 64.5t44 67.5t37.5 93.5t11.5 108.5q0 117 -45.5 223.5t-123 184t-184 123t-223.5 45.5zM591 561l226 -226l-579 -579q-12 -12 -29 -12t-29 12l-168 168q-12 12 -12 29t12 29zM1612 1524l168 -168q12 -12 12 -29t-12 -30l-233 -233 l-26 -25l-71 -71q-66 153 -195 258l91 91l207 207q13 12 30 12t29 -12z" />
<glyph unicode="&#xf2a5;" d="M866 1021q0 -27 -13 -94q-11 -50 -31.5 -150t-30.5 -150q-2 -11 -4.5 -12.5t-13.5 -2.5q-20 -2 -31 -2q-58 0 -84 49.5t-26 113.5q0 88 35 174t103 124q28 14 51 14q28 0 36.5 -16.5t8.5 -47.5zM1352 597q0 14 -39 75.5t-52 66.5q-21 8 -34 8q-91 0 -226 -77l-2 2 q3 22 27.5 135t24.5 178q0 233 -242 233q-24 0 -68 -6q-94 -17 -168.5 -89.5t-111.5 -166.5t-37 -189q0 -146 80.5 -225t227.5 -79q25 0 25 -3t-1 -5q-4 -34 -26 -117q-14 -52 -51.5 -101t-82.5 -49q-42 0 -42 47q0 24 10.5 47.5t25 39.5t29.5 28.5t26 20t11 8.5q0 3 -7 10 q-24 22 -58.5 36.5t-65.5 14.5q-35 0 -63.5 -34t-41 -75t-12.5 -75q0 -88 51.5 -142t138.5 -54q82 0 155 53t117.5 126t65.5 153q6 22 15.5 66.5t14.5 66.5q3 12 14 18q118 60 227 60q48 0 127 -18q1 -1 4 -1q5 0 9.5 4.5t4.5 8.5zM1536 1120v-960q0 -119 -84.5 -203.5 t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf2a6;" horiz-adv-x="1535" d="M744 1231q0 24 -2 38.5t-8.5 30t-21 23t-37.5 7.5q-39 0 -78 -23q-105 -58 -159 -190.5t-54 -269.5q0 -44 8.5 -85.5t26.5 -80.5t52.5 -62.5t81.5 -23.5q4 0 18 -0.5t20 0t16 3t15 8.5t7 16q16 77 48 231.5t48 231.5q19 91 19 146zM1498 575q0 -7 -7.5 -13.5t-15.5 -6.5 l-6 1q-22 3 -62 11t-72 12.5t-63 4.5q-167 0 -351 -93q-15 -8 -21 -27q-10 -36 -24.5 -105.5t-22.5 -100.5q-23 -91 -70 -179.5t-112.5 -164.5t-154.5 -123t-185 -47q-135 0 -214.5 83.5t-79.5 219.5q0 53 19.5 117t63 116.5t97.5 52.5q38 0 120 -33.5t83 -61.5 q0 -1 -16.5 -12.5t-39.5 -31t-46 -44.5t-39 -61t-16 -74q0 -33 16.5 -53t48.5 -20q45 0 85 31.5t66.5 78t48 105.5t32.5 107t16 90v9q0 2 -3.5 3.5t-8.5 1.5h-10t-10 -0.5t-6 -0.5q-227 0 -352 122.5t-125 348.5q0 108 34.5 221t96 210t156 167.5t204.5 89.5q52 9 106 9 q374 0 374 -360q0 -98 -38 -273t-43 -211l3 -3q101 57 182.5 88t167.5 31q22 0 53 -13q19 -7 80 -102.5t61 -116.5z" />
<glyph unicode="&#xf2a7;" horiz-adv-x="1664" d="M831 863q32 0 59 -18l222 -148q61 -40 110 -97l146 -170q40 -46 29 -106l-72 -413q-6 -32 -29.5 -53.5t-55.5 -25.5l-527 -56l-352 -32h-9q-39 0 -67.5 28t-28.5 68q0 37 27 64t65 32l260 32h-448q-41 0 -69.5 30t-26.5 71q2 39 32 65t69 26l442 1l-521 64q-41 5 -66 37 t-19 73q6 35 34.5 57.5t65.5 22.5h10l481 -60l-351 94q-38 10 -62 41.5t-18 68.5q6 36 33 58.5t62 22.5q6 0 20 -2l448 -96l217 -37q1 0 3 -0.5t3 -0.5q23 0 30.5 23t-12.5 36l-186 125q-35 23 -42 63.5t18 73.5q27 38 76 38zM761 661l186 -125l-218 37l-5 2l-36 38 l-238 262q-1 1 -2.5 3.5t-2.5 3.5q-24 31 -18.5 70t37.5 64q31 23 68 17.5t64 -33.5l142 -147l-4 -4t-5 -4q-32 -45 -23 -99t55 -85zM1648 1115l15 -266q4 -73 -11 -147l-48 -219q-12 -59 -67 -87l-106 -54q2 62 -39 109l-146 170q-53 61 -117 103l-222 148q-34 23 -76 23 q-51 0 -88 -37l-235 312q-25 33 -18 73.5t41 63.5q33 22 71.5 14t62.5 -40l266 -352l-262 455q-21 35 -10.5 75t47.5 59q35 18 72.5 6t57.5 -46l241 -420l-136 337q-15 35 -4.5 74t44.5 56q37 19 76 6t56 -51l193 -415l101 -196q8 -15 23 -17.5t27 7.5t11 26l-12 224 q-2 41 26 71t69 31q39 0 67 -28.5t30 -67.5z" />
<glyph unicode="&#xf2a8;" horiz-adv-x="1792" d="M335 180q-2 0 -6 2q-86 57 -168.5 145t-139.5 180q-21 30 -21 69q0 9 2 19t4 18t7 18t8.5 16t10.5 17t10 15t12 15.5t11 14.5q184 251 452 365q-110 198 -110 211q0 19 17 29q116 64 128 64q18 0 28 -16l124 -229q92 19 192 19q266 0 497.5 -137.5t378.5 -369.5 q20 -31 20 -69t-20 -69q-91 -142 -218.5 -253.5t-278.5 -175.5q110 -198 110 -211q0 -20 -17 -29q-116 -64 -127 -64q-19 0 -29 16l-124 229l-64 119l-444 820l7 7q-58 -24 -99 -47q3 -5 127 -234t243 -449t119 -223q0 -7 -9 -9q-13 -3 -72 -3q-57 0 -60 7l-456 841 q-39 -28 -82 -68q24 -43 214 -393.5t190 -354.5q0 -10 -11 -10q-14 0 -82.5 22t-72.5 28l-106 197l-224 413q-44 -53 -78 -106q2 -3 18 -25t23 -34l176 -327q0 -10 -10 -10zM1165 282l49 -91q273 111 450 385q-180 277 -459 389q67 -64 103 -148.5t36 -176.5 q0 -106 -47 -200.5t-132 -157.5zM848 896q0 -20 14 -34t34 -14q86 0 147 -61t61 -147q0 -20 14 -34t34 -14t34 14t14 34q0 126 -89 215t-215 89q-20 0 -34 -14t-14 -34zM1214 961l-9 4l7 -7z" />
<glyph unicode="&#xf2a9;" horiz-adv-x="1280" d="M1050 430q0 -215 -147 -374q-148 -161 -378 -161q-232 0 -378 161q-147 159 -147 374q0 147 68 270.5t189 196.5t268 73q96 0 182 -31q-32 -62 -39 -126q-66 28 -143 28q-167 0 -280.5 -123t-113.5 -291q0 -170 112.5 -288.5t281.5 -118.5t281 118.5t112 288.5 q0 89 -32 166q66 13 123 49q41 -98 41 -212zM846 619q0 -192 -79.5 -345t-238.5 -253l-14 -1q-29 0 -62 5q83 32 146.5 102.5t99.5 154.5t58.5 189t30 192.5t7.5 178.5q0 69 -3 103q55 -160 55 -326zM791 947v-2q-73 214 -206 440q88 -59 142.5 -186.5t63.5 -251.5z M1035 744q-83 0 -160 75q218 120 290 247q19 37 21 56q-42 -94 -139.5 -166.5t-204.5 -97.5q-35 54 -35 113q0 37 17 79t43 68q46 44 157 74q59 16 106 58.5t74 100.5q74 -105 74 -253q0 -109 -24 -170q-32 -77 -88.5 -130.5t-130.5 -53.5z" />
<glyph unicode="&#xf2aa;" d="M1050 495q0 78 -28 147q-41 -25 -85 -34q22 -50 22 -114q0 -117 -77 -198.5t-193 -81.5t-193.5 81.5t-77.5 198.5q0 115 78 199.5t193 84.5q53 0 98 -19q4 43 27 87q-60 21 -125 21q-154 0 -257.5 -108.5t-103.5 -263.5t103.5 -261t257.5 -106t257.5 106.5t103.5 260.5z M872 850q2 -24 2 -71q0 -63 -5 -123t-20.5 -132.5t-40.5 -130t-68.5 -106t-100.5 -70.5q21 -3 42 -3h10q219 139 219 411q0 116 -38 225zM872 850q-4 80 -44 171.5t-98 130.5q92 -156 142 -302zM1207 955q0 102 -51 174q-41 -86 -124 -109q-69 -19 -109 -53.5t-40 -99.5 q0 -40 24 -77q74 17 140.5 67t95.5 115q-4 -52 -74.5 -111.5t-138.5 -97.5q52 -52 110 -52q51 0 90 37t60 90q17 43 17 117zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960q119 0 203.5 -84.5 t84.5 -203.5z" />
<glyph unicode="&#xf2ab;" d="M1279 388q0 22 -22 27q-67 15 -118 59t-80 108q-7 19 -7 25q0 15 19.5 26t43 17t43 20.5t19.5 36.5q0 19 -18.5 31.5t-38.5 12.5q-12 0 -32 -8t-31 -8q-4 0 -12 2q5 95 5 114q0 79 -17 114q-36 78 -103 121.5t-152 43.5q-199 0 -275 -165q-17 -35 -17 -114q0 -19 5 -114 q-4 -2 -14 -2q-12 0 -32 7.5t-30 7.5q-21 0 -38.5 -12t-17.5 -32q0 -21 19.5 -35.5t43 -20.5t43 -17t19.5 -26q0 -6 -7 -25q-64 -138 -198 -167q-22 -5 -22 -27q0 -46 137 -68q2 -5 6 -26t11.5 -30.5t23.5 -9.5q12 0 37.5 4.5t39.5 4.5q35 0 67 -15t54 -32.5t57.5 -32.5 t76.5 -15q43 0 79 15t57.5 32.5t53.5 32.5t67 15q14 0 39.5 -4t38.5 -4q16 0 23 10t11 30t6 25q137 22 137 68zM1536 640q0 -209 -103 -385.5t-279.5 -279.5t-385.5 -103t-385.5 103t-279.5 279.5t-103 385.5t103 385.5t279.5 279.5t385.5 103t385.5 -103t279.5 -279.5 t103 -385.5z" />
<glyph unicode="&#xf2ac;" horiz-adv-x="1664" d="M848 1408q134 1 240.5 -68.5t163.5 -192.5q27 -58 27 -179q0 -47 -9 -191q14 -7 28 -7q18 0 51 13.5t51 13.5q29 0 56 -18t27 -46q0 -32 -31.5 -54t-69 -31.5t-69 -29t-31.5 -47.5q0 -15 12 -43q37 -82 102.5 -150t144.5 -101q28 -12 80 -23q28 -6 28 -35 q0 -70 -219 -103q-7 -11 -11 -39t-14 -46.5t-33 -18.5q-20 0 -62 6.5t-64 6.5q-37 0 -62 -5q-32 -5 -63 -22.5t-58 -38t-58 -40.5t-76 -33.5t-99 -13.5q-52 0 -96.5 13.5t-75 33.5t-57.5 40.5t-58 38t-62 22.5q-26 5 -63 5q-24 0 -65.5 -7.5t-58.5 -7.5q-25 0 -35 18.5 t-14 47.5t-11 40q-219 33 -219 103q0 29 28 35q52 11 80 23q78 32 144.5 101t102.5 150q12 28 12 43q0 28 -31.5 47.5t-69.5 29.5t-69.5 31.5t-31.5 52.5q0 27 26 45.5t55 18.5q15 0 48 -13t53 -13q18 0 32 7q-9 142 -9 190q0 122 27 180q64 137 172 198t264 63z" />
<glyph unicode="&#xf2ad;" d="M1280 388q0 22 -22 27q-67 14 -118 58t-80 109q-7 14 -7 25q0 15 19.5 26t42.5 17t42.5 20.5t19.5 36.5q0 19 -18.5 31.5t-38.5 12.5q-11 0 -31 -8t-32 -8q-4 0 -12 2q5 63 5 115q0 78 -17 114q-36 78 -102.5 121.5t-152.5 43.5q-198 0 -275 -165q-18 -38 -18 -115 q0 -38 6 -114q-10 -2 -15 -2q-11 0 -31.5 8t-30.5 8q-20 0 -37.5 -12.5t-17.5 -32.5q0 -21 19.5 -35.5t42.5 -20.5t42.5 -17t19.5 -26q0 -11 -7 -25q-64 -138 -198 -167q-22 -5 -22 -27q0 -47 138 -69q2 -5 6 -26t11 -30.5t23 -9.5q13 0 38.5 5t38.5 5q35 0 67.5 -15 t54.5 -32.5t57.5 -32.5t76.5 -15q43 0 79 15t57.5 32.5t54 32.5t67.5 15q13 0 39 -4.5t39 -4.5q15 0 22.5 9.5t11.5 31t5 24.5q138 22 138 69zM1536 1120v-960q0 -119 -84.5 -203.5t-203.5 -84.5h-960q-119 0 -203.5 84.5t-84.5 203.5v960q0 119 84.5 203.5t203.5 84.5h960 q119 0 203.5 -84.5t84.5 -203.5z" />
<glyph unicode="&#xf2ae;" horiz-adv-x="1792" />
<glyph unicode="&#xf2b0;" horiz-adv-x="1792" />
<glyph unicode="&#xf2b1;" horiz-adv-x="1792" />
<glyph unicode="&#xf2b2;" horiz-adv-x="1792" />
<glyph unicode="&#xf2b3;" horiz-adv-x="1792" />
<glyph unicode="&#xf2b4;" horiz-adv-x="1792" />
<glyph unicode="&#xf2b5;" horiz-adv-x="1792" />
<glyph unicode="&#xf2b6;" horiz-adv-x="1792" />
<glyph unicode="&#xf2b7;" horiz-adv-x="1792" />
<glyph unicode="&#xf2b8;" horiz-adv-x="1792" />
<glyph unicode="&#xf2b9;" horiz-adv-x="1792" />
<glyph unicode="&#xf2ba;" horiz-adv-x="1792" />
<glyph unicode="&#xf2bb;" horiz-adv-x="1792" />
<glyph unicode="&#xf2bc;" horiz-adv-x="1792" />
<glyph unicode="&#xf2bd;" horiz-adv-x="1792" />
<glyph unicode="&#xf2be;" horiz-adv-x="1792" />
<glyph unicode="&#xf500;" horiz-adv-x="1792" />
</font>
</defs></svg>

Before

Width:  |  Height:  |  Size: 348 KiB

After

Width:  |  Height:  |  Size: 377 KiB

BIN
stylesheets/font-awesome/fonts/fontawesome-webfont.ttf

Binary file not shown.

BIN
stylesheets/font-awesome/fonts/fontawesome-webfont.woff

Binary file not shown.

BIN
stylesheets/font-awesome/fonts/fontawesome-webfont.woff2

Binary file not shown.

3
stylesheets/font-awesome/less/font-awesome.less

@ -1,5 +1,5 @@
/*!
* Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome
* Font Awesome 4.6.1 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
@ -15,3 +15,4 @@
@import "rotated-flipped.less";
@import "stacked.less";
@import "icons.less";
@import "screen-reader.less";

47
stylesheets/font-awesome/less/icons.less

@ -675,3 +675,50 @@
.@{fa-css-prefix}-vimeo:before { content: @fa-var-vimeo; }
.@{fa-css-prefix}-black-tie:before { content: @fa-var-black-tie; }
.@{fa-css-prefix}-fonticons:before { content: @fa-var-fonticons; }
.@{fa-css-prefix}-reddit-alien:before { content: @fa-var-reddit-alien; }
.@{fa-css-prefix}-edge:before { content: @fa-var-edge; }
.@{fa-css-prefix}-credit-card-alt:before { content: @fa-var-credit-card-alt; }
.@{fa-css-prefix}-codiepie:before { content: @fa-var-codiepie; }
.@{fa-css-prefix}-modx:before { content: @fa-var-modx; }
.@{fa-css-prefix}-fort-awesome:before { content: @fa-var-fort-awesome; }
.@{fa-css-prefix}-usb:before { content: @fa-var-usb; }
.@{fa-css-prefix}-product-hunt:before { content: @fa-var-product-hunt; }
.@{fa-css-prefix}-mixcloud:before { content: @fa-var-mixcloud; }
.@{fa-css-prefix}-scribd:before { content: @fa-var-scribd; }
.@{fa-css-prefix}-pause-circle:before { content: @fa-var-pause-circle; }
.@{fa-css-prefix}-pause-circle-o:before { content: @fa-var-pause-circle-o; }
.@{fa-css-prefix}-stop-circle:before { content: @fa-var-stop-circle; }
.@{fa-css-prefix}-stop-circle-o:before { content: @fa-var-stop-circle-o; }
.@{fa-css-prefix}-shopping-bag:before { content: @fa-var-shopping-bag; }
.@{fa-css-prefix}-shopping-basket:before { content: @fa-var-shopping-basket; }
.@{fa-css-prefix}-hashtag:before { content: @fa-var-hashtag; }
.@{fa-css-prefix}-bluetooth:before { content: @fa-var-bluetooth; }
.@{fa-css-prefix}-bluetooth-b:before { content: @fa-var-bluetooth-b; }
.@{fa-css-prefix}-percent:before { content: @fa-var-percent; }
.@{fa-css-prefix}-gitlab:before { content: @fa-var-gitlab; }
.@{fa-css-prefix}-wpbeginner:before { content: @fa-var-wpbeginner; }
.@{fa-css-prefix}-wpforms:before { content: @fa-var-wpforms; }
.@{fa-css-prefix}-envira:before { content: @fa-var-envira; }
.@{fa-css-prefix}-universal-access:before { content: @fa-var-universal-access; }
.@{fa-css-prefix}-wheelchair-alt:before { content: @fa-var-wheelchair-alt; }
.@{fa-css-prefix}-question-circle-o:before { content: @fa-var-question-circle-o; }
.@{fa-css-prefix}-blind:before { content: @fa-var-blind; }
.@{fa-css-prefix}-audio-description:before { content: @fa-var-audio-description; }
.@{fa-css-prefix}-volume-control-phone:before { content: @fa-var-volume-control-phone; }
.@{fa-css-prefix}-braille:before { content: @fa-var-braille; }
.@{fa-css-prefix}-assistive-listening-systems:before { content: @fa-var-assistive-listening-systems; }
.@{fa-css-prefix}-asl-interpreting:before,
.@{fa-css-prefix}-american-sign-language-interpreting:before { content: @fa-var-american-sign-language-interpreting; }
.@{fa-css-prefix}-deafness:before,
.@{fa-css-prefix}-hard-of-hearing:before,
.@{fa-css-prefix}-deaf:before { content: @fa-var-deaf; }
.@{fa-css-prefix}-glide:before { content: @fa-var-glide; }
.@{fa-css-prefix}-glide-g:before { content: @fa-var-glide-g; }
.@{fa-css-prefix}-signing:before,
.@{fa-css-prefix}-sign-language:before { content: @fa-var-sign-language; }
.@{fa-css-prefix}-low-vision:before { content: @fa-var-low-vision; }
.@{fa-css-prefix}-viadeo:before { content: @fa-var-viadeo; }
.@{fa-css-prefix}-viadeo-square:before { content: @fa-var-viadeo-square; }
.@{fa-css-prefix}-snapchat:before { content: @fa-var-snapchat; }
.@{fa-css-prefix}-snapchat-ghost:before { content: @fa-var-snapchat-ghost; }
.@{fa-css-prefix}-snapchat-square:before { content: @fa-var-snapchat-square; }

38
stylesheets/font-awesome/less/mixins.less

@ -12,15 +12,49 @@
}
.fa-icon-rotate(@degrees, @rotation) {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation);
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation})";
-webkit-transform: rotate(@degrees);
-ms-transform: rotate(@degrees);
transform: rotate(@degrees);
}
.fa-icon-flip(@horiz, @vert, @rotation) {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1);
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=@{rotation}, mirror=1)";
-webkit-transform: scale(@horiz, @vert);
-ms-transform: scale(@horiz, @vert);
transform: scale(@horiz, @vert);
}
// Only display content to screen readers. A la Bootstrap 4.
//
// See: http://a11yproject.com/posts/how-to-hide-content/
.sr-only() {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
border: 0;
}
// Use in conjunction with .sr-only to only display content when it's focused.
//
// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
//
// Credit: HTML5 Boilerplate
.sr-only-focusable() {
&:active,
&:focus {
position: static;
width: auto;
height: auto;
margin: 0;
overflow: visible;
clip: auto;
}
}

2
stylesheets/font-awesome/less/path.less

@ -9,7 +9,7 @@
url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff'),
url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype'),
url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg');
// src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
// src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts
font-weight: normal;
font-style: normal;
}

5
stylesheets/font-awesome/less/screen-reader.less

@ -0,0 +1,5 @@
// Screen Readers
// -------------------------
.sr-only { .sr-only(); }
.sr-only-focusable { .sr-only-focusable(); }

51
stylesheets/font-awesome/less/variables.less

@ -4,9 +4,9 @@
@fa-font-path: "../fonts";
@fa-font-size-base: 14px;
@fa-line-height-base: 1;
//@fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.4.0/fonts"; // for referencing Bootstrap CDN font files directly
//@fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.6.1/fonts"; // for referencing Bootstrap CDN font files directly
@fa-css-prefix: fa;
@fa-version: "4.4.0";
@fa-version: "4.6.1";
@fa-border-color: #eee;
@fa-inverse: #fff;
@fa-li-width: (30em / 14);
@ -20,6 +20,7 @@
@fa-var-align-right: "\f038";
@fa-var-amazon: "\f270";
@fa-var-ambulance: "\f0f9";
@fa-var-american-sign-language-interpreting: "\f2a3";
@fa-var-anchor: "\f13d";
@fa-var-android: "\f17b";
@fa-var-angellist: "\f209";
@ -50,8 +51,11 @@
@fa-var-arrows-alt: "\f0b2";
@fa-var-arrows-h: "\f07e";
@fa-var-arrows-v: "\f07d";
@fa-var-asl-interpreting: "\f2a3";
@fa-var-assistive-listening-systems: "\f2a2";
@fa-var-asterisk: "\f069";
@fa-var-at: "\f1fa";
@fa-var-audio-description: "\f29e";
@fa-var-automobile: "\f1b9";
@fa-var-backward: "\f04a";
@fa-var-balance-scale: "\f24e";
@ -86,12 +90,16 @@
@fa-var-bitbucket-square: "\f172";
@fa-var-bitcoin: "\f15a";
@fa-var-black-tie: "\f27e";
@fa-var-blind: "\f29d";
@fa-var-bluetooth: "\f293";
@fa-var-bluetooth-b: "\f294";
@fa-var-bold: "\f032";
@fa-var-bolt: "\f0e7";
@fa-var-bomb: "\f1e2";
@fa-var-book: "\f02d";
@fa-var-bookmark: "\f02e";
@fa-var-bookmark-o: "\f097";
@fa-var-braille: "\f2a1";
@fa-var-briefcase: "\f0b1";
@fa-var-btc: "\f15a";
@fa-var-bug: "\f188";
@ -164,6 +172,7 @@
@fa-var-code: "\f121";
@fa-var-code-fork: "\f126";
@fa-var-codepen: "\f1cb";
@fa-var-codiepie: "\f284";
@fa-var-coffee: "\f0f4";
@fa-var-cog: "\f013";
@fa-var-cogs: "\f085";
@ -182,6 +191,7 @@
@fa-var-copyright: "\f1f9";
@fa-var-creative-commons: "\f25e";
@fa-var-credit-card: "\f09d";
@fa-var-credit-card-alt: "\f283";
@fa-var-crop: "\f125";
@fa-var-crosshairs: "\f05b";
@fa-var-css3: "\f13c";
@ -192,6 +202,8 @@
@fa-var-dashboard: "\f0e4";
@fa-var-dashcube: "\f210";
@fa-var-database: "\f1c0";
@fa-var-deaf: "\f2a4";
@fa-var-deafness: "\f2a4";
@fa-var-dedent: "\f03b";
@fa-var-delicious: "\f1a5";
@fa-var-desktop: "\f108";
@ -204,6 +216,7 @@
@fa-var-dribbble: "\f17d";
@fa-var-dropbox: "\f16b";
@fa-var-drupal: "\f1a9";
@fa-var-edge: "\f282";
@fa-var-edit: "\f044";
@fa-var-eject: "\f052";
@fa-var-ellipsis-h: "\f141";
@ -212,6 +225,7 @@
@fa-var-envelope: "\f0e0";
@fa-var-envelope-o: "\f003";
@fa-var-envelope-square: "\f199";
@fa-var-envira: "\f299";
@fa-var-eraser: "\f12d";
@fa-var-eur: "\f153";
@fa-var-euro: "\f153";
@ -273,6 +287,7 @@
@fa-var-folder-open-o: "\f115";
@fa-var-font: "\f031";
@fa-var-fonticons: "\f280";
@fa-var-fort-awesome: "\f286";
@fa-var-forumbee: "\f211";
@fa-var-forward: "\f04e";
@fa-var-foursquare: "\f180";
@ -294,8 +309,11 @@
@fa-var-github: "\f09b";
@fa-var-github-alt: "\f113";
@fa-var-github-square: "\f092";
@fa-var-gitlab: "\f296";
@fa-var-gittip: "\f184";
@fa-var-glass: "\f000";
@fa-var-glide: "\f2a5";
@fa-var-glide-g: "\f2a6";
@fa-var-globe: "\f0ac";
@fa-var-google: "\f1a0";
@fa-var-google-plus: "\f0d5";
@ -319,6 +337,8 @@
@fa-var-hand-scissors-o: "\f257";
@fa-var-hand-spock-o: "\f259";
@fa-var-hand-stop-o: "\f256";
@fa-var-hard-of-hearing: "\f2a4";
@fa-var-hashtag: "\f292";
@fa-var-hdd-o: "\f0a0";
@fa-var-header: "\f1dc";
@fa-var-headphones: "\f025";
@ -390,6 +410,7 @@
@fa-var-long-arrow-left: "\f177";
@fa-var-long-arrow-right: "\f178";
@fa-var-long-arrow-up: "\f176";
@fa-var-low-vision: "\f2a8";
@fa-var-magic: "\f0d0";
@fa-var-magnet: "\f076";
@fa-var-mail-forward: "\f064";
@ -418,8 +439,10 @@
@fa-var-minus-circle: "\f056";
@fa-var-minus-square: "\f146";
@fa-var-minus-square-o: "\f147";
@fa-var-mixcloud: "\f289";
@fa-var-mobile: "\f10b";
@fa-var-mobile-phone: "\f10b";
@fa-var-modx: "\f285";
@fa-var-money: "\f0d6";
@fa-var-moon-o: "\f186";
@fa-var-mortar-board: "\f19d";
@ -446,11 +469,14 @@
@fa-var-paragraph: "\f1dd";
@fa-var-paste: "\f0ea";
@fa-var-pause: "\f04c";
@fa-var-pause-circle: "\f28b";
@fa-var-pause-circle-o: "\f28c";
@fa-var-paw: "\f1b0";
@fa-var-paypal: "\f1ed";
@fa-var-pencil: "\f040";
@fa-var-pencil-square: "\f14b";
@fa-var-pencil-square-o: "\f044";
@fa-var-percent: "\f295";
@fa-var-phone: "\f095";
@fa-var-phone-square: "\f098";
@fa-var-photo: "\f03e";
@ -472,11 +498,13 @@
@fa-var-plus-square-o: "\f196";
@fa-var-power-off: "\f011";
@fa-var-print: "\f02f";
@fa-var-product-hunt: "\f288";
@fa-var-puzzle-piece: "\f12e";
@fa-var-qq: "\f1d6";
@fa-var-qrcode: "\f029";
@fa-var-question: "\f128";
@fa-var-question-circle: "\f059";
@fa-var-question-circle-o: "\f29c";
@fa-var-quote-left: "\f10d";
@fa-var-quote-right: "\f10e";
@fa-var-ra: "\f1d0";
@ -484,6 +512,7 @@
@fa-var-rebel: "\f1d0";
@fa-var-recycle: "\f1b8";
@fa-var-reddit: "\f1a1";
@fa-var-reddit-alien: "\f281";
@fa-var-reddit-square: "\f1a2";
@fa-var-refresh: "\f021";
@fa-var-registered: "\f25d";
@ -508,6 +537,7 @@
@fa-var-safari: "\f267";
@fa-var-save: "\f0c7";
@fa-var-scissors: "\f0c4";
@fa-var-scribd: "\f28a";
@fa-var-search: "\f002";
@fa-var-search-minus: "\f010";
@fa-var-search-plus: "\f00e";
@ -525,10 +555,14 @@
@fa-var-shield: "\f132";
@fa-var-ship: "\f21a";
@fa-var-shirtsinbulk: "\f214";
@fa-var-shopping-bag: "\f290";
@fa-var-shopping-basket: "\f291";
@fa-var-shopping-cart: "\f07a";
@fa-var-sign-in: "\f090";
@fa-var-sign-language: "\f2a7";
@fa-var-sign-out: "\f08b";
@fa-var-signal: "\f012";
@fa-var-signing: "\f2a7";
@fa-var-simplybuilt: "\f215";
@fa-var-sitemap: "\f0e8";
@fa-var-skyatlas: "\f216";
@ -537,6 +571,9 @@
@fa-var-sliders: "\f1de";
@fa-var-slideshare: "\f1e7";
@fa-var-smile-o: "\f118";
@fa-var-snapchat: "\f2ab";
@fa-var-snapchat-ghost: "\f2ac";
@fa-var-snapchat-square: "\f2ad";
@fa-var-soccer-ball-o: "\f1e3";
@fa-var-sort: "\f0dc";
@fa-var-sort-alpha-asc: "\f15d";
@ -572,6 +609,8 @@
@fa-var-sticky-note: "\f249";
@fa-var-sticky-note-o: "\f24a";
@fa-var-stop: "\f04d";
@fa-var-stop-circle: "\f28d";
@fa-var-stop-circle-o: "\f28e";
@fa-var-street-view: "\f21d";
@fa-var-strikethrough: "\f0cc";
@fa-var-stumbleupon: "\f1a4";
@ -636,12 +675,14 @@
@fa-var-umbrella: "\f0e9";
@fa-var-underline: "\f0cd";
@fa-var-undo: "\f0e2";
@fa-var-universal-access: "\f29a";
@fa-var-university: "\f19c";
@fa-var-unlink: "\f127";
@fa-var-unlock: "\f09c";
@fa-var-unlock-alt: "\f13e";
@fa-var-unsorted: "\f0dc";
@fa-var-upload: "\f093";
@fa-var-usb: "\f287";
@fa-var-usd: "\f155";
@fa-var-user: "\f007";
@fa-var-user-md: "\f0f0";
@ -653,11 +694,14 @@
@fa-var-venus-double: "\f226";
@fa-var-venus-mars: "\f228";
@fa-var-viacoin: "\f237";
@fa-var-viadeo: "\f2a9";
@fa-var-viadeo-square: "\f2aa";
@fa-var-video-camera: "\f03d";
@fa-var-vimeo: "\f27d";
@fa-var-vimeo-square: "\f194";
@fa-var-vine: "\f1ca";
@fa-var-vk: "\f189";
@fa-var-volume-control-phone: "\f2a0";
@fa-var-volume-down: "\f027";
@fa-var-volume-off: "\f026";
@fa-var-volume-up: "\f028";
@ -667,11 +711,14 @@
@fa-var-weixin: "\f1d7";
@fa-var-whatsapp: "\f232";
@fa-var-wheelchair: "\f193";
@fa-var-wheelchair-alt: "\f29b";
@fa-var-wifi: "\f1eb";
@fa-var-wikipedia-w: "\f266";
@fa-var-windows: "\f17a";
@fa-var-won: "\f159";
@fa-var-wordpress: "\f19a";
@fa-var-wpbeginner: "\f297";
@fa-var-wpforms: "\f298";
@fa-var-wrench: "\f0ad";
@fa-var-xing: "\f168";
@fa-var-xing-square: "\f169";

47
stylesheets/font-awesome/scss/_icons.scss

@ -675,3 +675,50 @@
.#{$fa-css-prefix}-vimeo:before { content: $fa-var-vimeo; }
.#{$fa-css-prefix}-black-tie:before { content: $fa-var-black-tie; }
.#{$fa-css-prefix}-fonticons:before { content: $fa-var-fonticons; }
.#{$fa-css-prefix}-reddit-alien:before { content: $fa-var-reddit-alien; }
.#{$fa-css-prefix}-edge:before { content: $fa-var-edge; }
.#{$fa-css-prefix}-credit-card-alt:before { content: $fa-var-credit-card-alt; }
.#{$fa-css-prefix}-codiepie:before { content: $fa-var-codiepie; }
.#{$fa-css-prefix}-modx:before { content: $fa-var-modx; }
.#{$fa-css-prefix}-fort-awesome:before { content: $fa-var-fort-awesome; }
.#{$fa-css-prefix}-usb:before { content: $fa-var-usb; }
.#{$fa-css-prefix}-product-hunt:before { content: $fa-var-product-hunt; }
.#{$fa-css-prefix}-mixcloud:before { content: $fa-var-mixcloud; }
.#{$fa-css-prefix}-scribd:before { content: $fa-var-scribd; }
.#{$fa-css-prefix}-pause-circle:before { content: $fa-var-pause-circle; }
.#{$fa-css-prefix}-pause-circle-o:before { content: $fa-var-pause-circle-o; }
.#{$fa-css-prefix}-stop-circle:before { content: $fa-var-stop-circle; }
.#{$fa-css-prefix}-stop-circle-o:before { content: $fa-var-stop-circle-o; }
.#{$fa-css-prefix}-shopping-bag:before { content: $fa-var-shopping-bag; }
.#{$fa-css-prefix}-shopping-basket:before { content: $fa-var-shopping-basket; }
.#{$fa-css-prefix}-hashtag:before { content: $fa-var-hashtag; }
.#{$fa-css-prefix}-bluetooth:before { content: $fa-var-bluetooth; }
.#{$fa-css-prefix}-bluetooth-b:before { content: $fa-var-bluetooth-b; }
.#{$fa-css-prefix}-percent:before { content: $fa-var-percent; }
.#{$fa-css-prefix}-gitlab:before { content: $fa-var-gitlab; }
.#{$fa-css-prefix}-wpbeginner:before { content: $fa-var-wpbeginner; }
.#{$fa-css-prefix}-wpforms:before { content: $fa-var-wpforms; }
.#{$fa-css-prefix}-envira:before { content: $fa-var-envira; }
.#{$fa-css-prefix}-universal-access:before { content: $fa-var-universal-access; }
.#{$fa-css-prefix}-wheelchair-alt:before { content: $fa-var-wheelchair-alt; }
.#{$fa-css-prefix}-question-circle-o:before { content: $fa-var-question-circle-o; }
.#{$fa-css-prefix}-blind:before { content: $fa-var-blind; }
.#{$fa-css-prefix}-audio-description:before { content: $fa-var-audio-description; }
.#{$fa-css-prefix}-volume-control-phone:before { content: $fa-var-volume-control-phone; }
.#{$fa-css-prefix}-braille:before { content: $fa-var-braille; }
.#{$fa-css-prefix}-assistive-listening-systems:before { content: $fa-var-assistive-listening-systems; }
.#{$fa-css-prefix}-asl-interpreting:before,
.#{$fa-css-prefix}-american-sign-language-interpreting:before { content: $fa-var-american-sign-language-interpreting; }
.#{$fa-css-prefix}-deafness:before,
.#{$fa-css-prefix}-hard-of-hearing:before,
.#{$fa-css-prefix}-deaf:before { content: $fa-var-deaf; }
.#{$fa-css-prefix}-glide:before { content: $fa-var-glide; }
.#{$fa-css-prefix}-glide-g:before { content: $fa-var-glide-g; }
.#{$fa-css-prefix}-signing:before,
.#{$fa-css-prefix}-sign-language:before { content: $fa-var-sign-language; }
.#{$fa-css-prefix}-low-vision:before { content: $fa-var-low-vision; }
.#{$fa-css-prefix}-viadeo:before { content: $fa-var-viadeo; }
.#{$fa-css-prefix}-viadeo-square:before { content: $fa-var-viadeo-square; }
.#{$fa-css-prefix}-snapchat:before { content: $fa-var-snapchat; }
.#{$fa-css-prefix}-snapchat-ghost:before { content: $fa-var-snapchat-ghost; }
.#{$fa-css-prefix}-snapchat-square:before { content: $fa-var-snapchat-square; }

38
stylesheets/font-awesome/scss/_mixins.scss

@ -12,15 +12,49 @@
}
@mixin fa-icon-rotate($degrees, $rotation) {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})";
-webkit-transform: rotate($degrees);
-ms-transform: rotate($degrees);
transform: rotate($degrees);
}
@mixin fa-icon-flip($horiz, $vert, $rotation) {
filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation});
-ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)";
-webkit-transform: scale($horiz, $vert);
-ms-transform: scale($horiz, $vert);
transform: scale($horiz, $vert);
}
// Only display content to screen readers. A la Bootstrap 4.
//
// See: http://a11yproject.com/posts/how-to-hide-content/
@mixin sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0,0,0,0);
border: 0;
}
// Use in conjunction with .sr-only to only display content when it's focused.
//
// Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1
//
// Credit: HTML5 Boilerplate
@mixin sr-only-focusable {
&:active,
&:focus {
position: static;
width: auto;
height: auto;
margin: 0;
overflow: visible;
clip: auto;
}
}

5
stylesheets/font-awesome/scss/_screen-reader.scss

@ -0,0 +1,5 @@
// Screen Readers
// -------------------------
.sr-only { @include sr-only(); }
.sr-only-focusable { @include sr-only-focusable(); }

51
stylesheets/font-awesome/scss/_variables.scss

@ -4,9 +4,9 @@
$fa-font-path: "../fonts" !default;
$fa-font-size-base: 14px !default;
$fa-line-height-base: 1 !default;
//$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.4.0/fonts" !default; // for referencing Bootstrap CDN font files directly
//$fa-font-path: "//netdna.bootstrapcdn.com/font-awesome/4.6.1/fonts" !default; // for referencing Bootstrap CDN font files directly
$fa-css-prefix: fa !default;
$fa-version: "4.4.0" !default;
$fa-version: "4.6.1" !default;
$fa-border-color: #eee !default;
$fa-inverse: #fff !default;
$fa-li-width: (30em / 14) !default;
@ -20,6 +20,7 @@ $fa-var-align-left: "\f036";
$fa-var-align-right: "\f038";
$fa-var-amazon: "\f270";
$fa-var-ambulance: "\f0f9";
$fa-var-american-sign-language-interpreting: "\f2a3";
$fa-var-anchor: "\f13d";
$fa-var-android: "\f17b";
$fa-var-angellist: "\f209";
@ -50,8 +51,11 @@ $fa-var-arrows: "\f047";
$fa-var-arrows-alt: "\f0b2";
$fa-var-arrows-h: "\f07e";
$fa-var-arrows-v: "\f07d";
$fa-var-asl-interpreting: "\f2a3";
$fa-var-assistive-listening-systems: "\f2a2";
$fa-var-asterisk: "\f069";
$fa-var-at: "\f1fa";
$fa-var-audio-description: "\f29e";
$fa-var-automobile: "\f1b9";
$fa-var-backward: "\f04a";
$fa-var-balance-scale: "\f24e";
@ -86,12 +90,16 @@ $fa-var-bitbucket: "\f171";
$fa-var-bitbucket-square: "\f172";
$fa-var-bitcoin: "\f15a";
$fa-var-black-tie: "\f27e";
$fa-var-blind: "\f29d";
$fa-var-bluetooth: "\f293";
$fa-var-bluetooth-b: "\f294";
$fa-var-bold: "\f032";
$fa-var-bolt: "\f0e7";
$fa-var-bomb: "\f1e2";
$fa-var-book: "\f02d";
$fa-var-bookmark: "\f02e";
$fa-var-bookmark-o: "\f097";
$fa-var-braille: "\f2a1";
$fa-var-briefcase: "\f0b1";
$fa-var-btc: "\f15a";
$fa-var-bug: "\f188";
@ -164,6 +172,7 @@ $fa-var-cny: "\f157";
$fa-var-code: "\f121";
$fa-var-code-fork: "\f126";
$fa-var-codepen: "\f1cb";
$fa-var-codiepie: "\f284";
$fa-var-coffee: "\f0f4";
$fa-var-cog: "\f013";
$fa-var-cogs: "\f085";
@ -182,6 +191,7 @@ $fa-var-copy: "\f0c5";
$fa-var-copyright: "\f1f9";
$fa-var-creative-commons: "\f25e";
$fa-var-credit-card: "\f09d";
$fa-var-credit-card-alt: "\f283";
$fa-var-crop: "\f125";
$fa-var-crosshairs: "\f05b";
$fa-var-css3: "\f13c";
@ -192,6 +202,8 @@ $fa-var-cutlery: "\f0f5";
$fa-var-dashboard: "\f0e4";
$fa-var-dashcube: "\f210";
$fa-var-database: "\f1c0";
$fa-var-deaf: "\f2a4";
$fa-var-deafness: "\f2a4";
$fa-var-dedent: "\f03b";
$fa-var-delicious: "\f1a5";
$fa-var-desktop: "\f108";
@ -204,6 +216,7 @@ $fa-var-download: "\f019";
$fa-var-dribbble: "\f17d";
$fa-var-dropbox: "\f16b";
$fa-var-drupal: "\f1a9";
$fa-var-edge: "\f282";
$fa-var-edit: "\f044";
$fa-var-eject: "\f052";
$fa-var-ellipsis-h: "\f141";
@ -212,6 +225,7 @@ $fa-var-empire: "\f1d1";
$fa-var-envelope: "\f0e0";
$fa-var-envelope-o: "\f003";
$fa-var-envelope-square: "\f199";
$fa-var-envira: "\f299";
$fa-var-eraser: "\f12d";
$fa-var-eur: "\f153";
$fa-var-euro: "\f153";
@ -273,6 +287,7 @@ $fa-var-folder-open: "\f07c";
$fa-var-folder-open-o: "\f115";
$fa-var-font: "\f031";
$fa-var-fonticons: "\f280";
$fa-var-fort-awesome: "\f286";
$fa-var-forumbee: "\f211";
$fa-var-forward: "\f04e";
$fa-var-foursquare: "\f180";
@ -294,8 +309,11 @@ $fa-var-git-square: "\f1d2";
$fa-var-github: "\f09b";
$fa-var-github-alt: "\f113";
$fa-var-github-square: "\f092";
$fa-var-gitlab: "\f296";
$fa-var-gittip: "\f184";
$fa-var-glass: "\f000";
$fa-var-glide: "\f2a5";
$fa-var-glide-g: "\f2a6";
$fa-var-globe: "\f0ac";
$fa-var-google: "\f1a0";
$fa-var-google-plus: "\f0d5";
@ -319,6 +337,8 @@ $fa-var-hand-rock-o: "\f255";
$fa-var-hand-scissors-o: "\f257";
$fa-var-hand-spock-o: "\f259";
$fa-var-hand-stop-o: "\f256";
$fa-var-hard-of-hearing: "\f2a4";
$fa-var-hashtag: "\f292";
$fa-var-hdd-o: "\f0a0";
$fa-var-header: "\f1dc";
$fa-var-headphones: "\f025";
@ -390,6 +410,7 @@ $fa-var-long-arrow-down: "\f175";
$fa-var-long-arrow-left: "\f177";
$fa-var-long-arrow-right: "\f178";
$fa-var-long-arrow-up: "\f176";
$fa-var-low-vision: "\f2a8";
$fa-var-magic: "\f0d0";
$fa-var-magnet: "\f076";
$fa-var-mail-forward: "\f064";
@ -418,8 +439,10 @@ $fa-var-minus: "\f068";
$fa-var-minus-circle: "\f056";
$fa-var-minus-square: "\f146";
$fa-var-minus-square-o: "\f147";
$fa-var-mixcloud: "\f289";
$fa-var-mobile: "\f10b";
$fa-var-mobile-phone: "\f10b";
$fa-var-modx: "\f285";
$fa-var-money: "\f0d6";
$fa-var-moon-o: "\f186";
$fa-var-mortar-board: "\f19d";
@ -446,11 +469,14 @@ $fa-var-paperclip: "\f0c6";
$fa-var-paragraph: "\f1dd";
$fa-var-paste: "\f0ea";
$fa-var-pause: "\f04c";
$fa-var-pause-circle: "\f28b";
$fa-var-pause-circle-o: "\f28c";
$fa-var-paw: "\f1b0";
$fa-var-paypal: "\f1ed";
$fa-var-pencil: "\f040";
$fa-var-pencil-square: "\f14b";
$fa-var-pencil-square-o: "\f044";
$fa-var-percent: "\f295";
$fa-var-phone: "\f095";
$fa-var-phone-square: "\f098";
$fa-var-photo: "\f03e";
@ -472,11 +498,13 @@ $fa-var-plus-square: "\f0fe";
$fa-var-plus-square-o: "\f196";
$fa-var-power-off: "\f011";
$fa-var-print: "\f02f";
$fa-var-product-hunt: "\f288";
$fa-var-puzzle-piece: "\f12e";
$fa-var-qq: "\f1d6";
$fa-var-qrcode: "\f029";
$fa-var-question: "\f128";
$fa-var-question-circle: "\f059";
$fa-var-question-circle-o: "\f29c";
$fa-var-quote-left: "\f10d";
$fa-var-quote-right: "\f10e";
$fa-var-ra: "\f1d0";
@ -484,6 +512,7 @@ $fa-var-random: "\f074";
$fa-var-rebel: "\f1d0";
$fa-var-recycle: "\f1b8";
$fa-var-reddit: "\f1a1";
$fa-var-reddit-alien: "\f281";
$fa-var-reddit-square: "\f1a2";
$fa-var-refresh: "\f021";
$fa-var-registered: "\f25d";
@ -508,6 +537,7 @@ $fa-var-rupee: "\f156";
$fa-var-safari: "\f267";
$fa-var-save: "\f0c7";
$fa-var-scissors: "\f0c4";
$fa-var-scribd: "\f28a";
$fa-var-search: "\f002";
$fa-var-search-minus: "\f010";
$fa-var-search-plus: "\f00e";
@ -525,10 +555,14 @@ $fa-var-sheqel: "\f20b";
$fa-var-shield: "\f132";
$fa-var-ship: "\f21a";
$fa-var-shirtsinbulk: "\f214";
$fa-var-shopping-bag: "\f290";
$fa-var-shopping-basket: "\f291";
$fa-var-shopping-cart: "\f07a";
$fa-var-sign-in: "\f090";
$fa-var-sign-language: "\f2a7";
$fa-var-sign-out: "\f08b";
$fa-var-signal: "\f012";
$fa-var-signing: "\f2a7";
$fa-var-simplybuilt: "\f215";
$fa-var-sitemap: "\f0e8";
$fa-var-skyatlas: "\f216";
@ -537,6 +571,9 @@ $fa-var-slack: "\f198";
$fa-var-sliders: "\f1de";
$fa-var-slideshare: "\f1e7";
$fa-var-smile-o: "\f118";
$fa-var-snapchat: "\f2ab";
$fa-var-snapchat-ghost: "\f2ac";
$fa-var-snapchat-square: "\f2ad";
$fa-var-soccer-ball-o: "\f1e3";
$fa-var-sort: "\f0dc";
$fa-var-sort-alpha-asc: "\f15d";
@ -572,6 +609,8 @@ $fa-var-stethoscope: "\f0f1";
$fa-var-sticky-note: "\f249";
$fa-var-sticky-note-o: "\f24a";
$fa-var-stop: "\f04d";
$fa-var-stop-circle: "\f28d";
$fa-var-stop-circle-o: "\f28e";
$fa-var-street-view: "\f21d";
$fa-var-strikethrough: "\f0cc";
$fa-var-stumbleupon: "\f1a4";
@ -636,12 +675,14 @@ $fa-var-twitter-square: "\f081";
$fa-var-umbrella: "\f0e9";
$fa-var-underline: "\f0cd";
$fa-var-undo: "\f0e2";
$fa-var-universal-access: "\f29a";
$fa-var-university: "\f19c";
$fa-var-unlink: "\f127";
$fa-var-unlock: "\f09c";
$fa-var-unlock-alt: "\f13e";
$fa-var-unsorted: "\f0dc";
$fa-var-upload: "\f093";
$fa-var-usb: "\f287";
$fa-var-usd: "\f155";
$fa-var-user: "\f007";
$fa-var-user-md: "\f0f0";
@ -653,11 +694,14 @@ $fa-var-venus: "\f221";
$fa-var-venus-double: "\f226";
$fa-var-venus-mars: "\f228";
$fa-var-viacoin: "\f237";
$fa-var-viadeo: "\f2a9";
$fa-var-viadeo-square: "\f2aa";
$fa-var-video-camera: "\f03d";
$fa-var-vimeo: "\f27d";
$fa-var-vimeo-square: "\f194";
$fa-var-vine: "\f1ca";
$fa-var-vk: "\f189";
$fa-var-volume-control-phone: "\f2a0";
$fa-var-volume-down: "\f027";
$fa-var-volume-off: "\f026";
$fa-var-volume-up: "\f028";
@ -667,11 +711,14 @@ $fa-var-weibo: "\f18a";
$fa-var-weixin: "\f1d7";
$fa-var-whatsapp: "\f232";
$fa-var-wheelchair: "\f193";
$fa-var-wheelchair-alt: "\f29b";
$fa-var-wifi: "\f1eb";
$fa-var-wikipedia-w: "\f266";
$fa-var-windows: "\f17a";
$fa-var-won: "\f159";
$fa-var-wordpress: "\f19a";
$fa-var-wpbeginner: "\f297";
$fa-var-wpforms: "\f298";
$fa-var-wrench: "\f0ad";
$fa-var-xing: "\f168";
$fa-var-xing-square: "\f169";

3
stylesheets/font-awesome/scss/font-awesome.scss

@ -1,5 +1,5 @@
/*!
* Font Awesome 4.4.0 by @davegandy - http://fontawesome.io - @fontawesome
* Font Awesome 4.6.1 by @davegandy - http://fontawesome.io - @fontawesome
* License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License)
*/
@ -15,3 +15,4 @@
@import "rotated-flipped";
@import "stacked";
@import "icons";
@import "screen-reader";

249
stylesheets/northboard_cb.css

@ -0,0 +1,249 @@
/**
* northboard.css
* for circleboard by kotiakrobaatti
*/
body {
background: #191919;
color: #d4d4d4;
font-size: 13px;
font: 12px/1.5em Verdana,"DejaVu Sans",Sans-serif;
}
a:link, a:visited, .intro a.email {
color: #60a0dc !important;
text-decoration: none;
}
a:link:hover {
outline: 0;
color: #82c2fe !important;
}
a.post_no, a.watchThread {
color: #60a0dc;
}
div.post.reply {
background: #343439;
border-color: #3070A9;
border-top: 1px solid #3070A9;
border-left: 1px solid #3070A9;
border-radius: 3px;
padding: 0px;
}
div.post.reply.highlighted {
background: #44444f;
border: 3px dashed #3070a9;
}
div.post.reply div.body a, .mentioned {
color: 60a0dc;
text-decoration: underline;
}
div.post.reply div.body a:hover {
color: #82c2fe !important;
text-decoration: underline !important;
}
div.body a:hover, a.watchThread:hover, a.email span.name:hover {
color: #82c2fe !important;
}
.intro span.subject {
color: #60a0dc !important;
font-weight: 700;
font-size: 1.4em;
line-height: 35px;
height: 35px;
padding: 0 5px;
}
form table tr th {
background: #333;
border: 1px solid #444;
color: #60a0dc;
}
div.ban h2 {
background: #333;
color: inherit;
}
div.ban {
border-color: #800;
}
div.ban p {
color: black;
}
div.pages {
background: #343439;
border: 1px solid #3070a9;
border-radius: 5px;
}
div.pages a.selected {
color: #ccc;
}
div.boardlist {
color: #aaa;
}
div.boardlist a {
color: #ccc;
}
table.modlog tr th {
background: #333;
}
/* options.js */
#options_div, #alert_div {
background: #44444f;
}
.desktop-style div.boardlist:nth-child(1) {
background-color: #bbb;
}
span.name {
color: #93e3ff !important;
}
span.trip {
color: #ffffff;
}
span.quote {
color: green;
}
[id^=thread] {
background: #1C1C1C;
margin-bottom: 1em ;
padding: 1em 1em 0 1em ;
border: 1px solid #333333;
}
[id^=thread] hr {
display: none ;
}
html body hr {
border-color: #9B9B9B;
border-width: 1px;
}
p.intro {
background-color: #44444F;
padding-top: 5px;
padding-left: 5px;
padding-right: 5px;
margin-top: 0px!important;
display: box;
margin: intial;
border-bottom: 1px solid #1c1c1c;
box-shadow: 0 5px 5px -5px #1c1c1c;
}
div.post.op p.intro {
background-color: #252525;
min-height: 30px;
}
span.omitted {
color: #707070;
font-size: .9em;
margin: 5 5px 0px;
}
h1 {
font-size: 3em;
font-weight: 300;
margin: .3em 0;
color: #60a0dc;
}
div.subtitle {
font-style: oblique;
font-size: 1.25em!important;
font-weight: 400!important;
color: #ccc!important;
}
tr td input, #body {
background-color: #AAA;
border-color: #888;
border-width: 1px;
padding-top: 3px;
padding-right: 2px;
padding-bottom: 3px;
padding-left: 2px;
}
#upload_file {
color: #ccc;
background-color: transparent;
}
div.blotter {
color: #ccc!important;
}
div.banner, a.unimportant {
background-color: #60a0dc;
color: #000!important;
border-radius: 3px;
min-height: 25px;
padding-top: 5px;
}
div.thread:hover {
background: #44444f;
border-color: #1c1c1c;
}
span.heading {
color: red;
text-shadow: 0 0 3px #000, 0 0 3px #000;
}
#expand-all-images,
#shrink-all-images,
#treeview{
right: 1em !important;
position: absolute !important;
}
#expand-all-images{
margin-top: 4em !important;
}
#treeview{
margin-top: 5em !important;
}
#shrink-all-images{
margin-top: 6em !important;
}
#expand-all-images + hr,
#shrink-all-images + hr{
opacity: 0 !important;
margin: 0 !important;
}
#treeview + hr{
opacity: 0 !important;
clear: both !important;
}
#options_handler{
margin-top: 3em !important;
}

57
stylesheets/style.css

@ -1016,8 +1016,14 @@ pre {
#options_div {
width: 600px;
height: 360px;
/* width: 620px;
height: 400px;
resize: both;
overflow: auto;*/
}
#alert_div {
width: 500px;
}
@ -1048,7 +1054,7 @@ pre {
#options_tablist {
padding: 0px 5px;
left: 0px;
width: 70px;
width: 90px;
top: 0px;
bottom: 0px;
height: 100%;
@ -1077,8 +1083,8 @@ pre {
padding: 10px;
position: absolute;
top: 0px;
bottom: 0px;
left: 81px;
bottom: 10px;
left: 101px;
right: 0px;
text-align: left;
font-size: 12px;
@ -1104,8 +1110,16 @@ pre {
.poster_id {
cursor: pointer;
white-space:nowrap;
display: inline-block;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
-o-user-select: none;
user-select: none;
}
.poster_id:hover {
color: #800000!important;
}
.poster_id::before {
content: " ID: ";
}
@ -1264,28 +1278,32 @@ div.boardlist a {
/* Threads */
/* Thread Footer */
#thread-interactions {
margin: 8px 0;
clear: both;
margin: 8px 0;
clear: both;
}
#thread-links {
float: left;
float: left;
}
#thread-links > a {
padding-left: none;
padding-right: 10px;
padding-left: none;
padding-right: 10px;
}
#thread-quick-reply {
display: none;
float: right;
margin-right: 10px;
display: none;
position: absolute;
left: 50%;
right: 50%;
text-align: center;
width: 100px;
margin-left: -50px;
}
#thread_stats {
float: right;
float: right;
}
#post-moderation-fields {
float: right;
text-align: right;
float: right;
text-align: right;
}
#delete-fields {
}
@ -1891,4 +1909,13 @@ table.fileboard .intro a {
.dropdown:hover .dropdown-content {
display: block;
}
.own_post {
font-style: italic;
font-weight: normal;
opacity: .666;
}
div.mix {
display: inline-block;
}

10
templates/generic_page.html

@ -8,7 +8,7 @@
</head>
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
<body>
<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %}" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
{{ boardlist.top }}
{% if pm %}<div class="top_notice">You have <a href="?/PM/{{ pm.id }}">an unread PM</a>{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.</div><hr />{% endif %}
{% if config.url_banner %}<img class="banner" src="{{ config.url_banner }}" {% if config.banner_width or config.banner_height %}style="{% if config.banner_width %}width:{{ config.banner_width }}px{% endif %};{% if config.banner_width %}height:{{ config.banner_height }}px{% endif %}" {% endif %}alt="" />{% endif %}
@ -38,10 +38,10 @@
{% endfor %} {{ btn.next }}</div>
{{ boardlist.bottom }}
<footer>
<p class="unimportant" style="margin-top:20px;text-align:center;">- <a href="http://tinyboard.org/">Tinyboard</a> +
<a href='https://int.vichan.net/devel/'>vichan</a> {{ config.version }} -
<br><a href="http://tinyboard.org/">Tinyboard</a> Copyright &copy; 2010-2014 Tinyboard Development Group
<br><a href="https://engine.vichan.net/">vichan</a> Copyright &copy; 2012-2015 vichan-devel</p>
<p class="unimportant" style="margin-top:20px;text-align:center;">- Tinyboard +
<a href="https://engine.vichan.net/">vichan</a> {{ config.version }} -
<br>Tinyboard Copyright &copy; 2010-2014 Tinyboard Development Group
<br><a href="https://engine.vichan.net/">vichan</a> Copyright &copy; 2012-2016 vichan-devel</p>
{% for footer in config.footer %}<p class="unimportant" style="text-align:center;">{{ footer }}</p>{% endfor %}
</footer>
<script type="text/javascript">{% raw %}

21
templates/index.html

@ -5,21 +5,23 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
<script type="text/javascript">
var
{% if not no_post_form %}
var active_page = "index";
active_page = "index"
, board_name = "{{ board.uri }}";
{% else %}
var active_page = "ukko";
active_page = "ukko";
{% endif %}
</script>
{% include 'header.html' %}
<title>{{ board.url }} - {{ board.title|e }}</title>
</head>
<body>
<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %} active-{% if not no_post_form %}index{% else %}ukko{% endif %}" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
<div class="bar top">
{{ boardlist.top }}
</div>
{% if pm %}<div class="top_notice">You have <a href="?/PM/{{ pm.id }}">an unread PM</a>{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.</div><hr />{% endif %}
{% if config.url_banner %}<img class="board_image" src="{{ config.url_banner }}" {% if config.banner_width or config.banner_height %}style="{% if config.banner_width %}width:{{ config.banner_width }}px{% endif %};{% if config.banner_width %}height:{{ config.banner_height }}px{% endif %}" {% endif %}alt="" />{% endif %}
@ -69,6 +71,17 @@
{{ body }}
{% include 'report_delete.html' %}
</form>
<div class="pages">
{{ btn.prev }} {% for page in pages %}
[<a {% if page.selected %}class="selected"{% endif %}{% if not page.selected %}href="{{ page.link }}"{% endif %}>{{ page.num }}</a>]{% if loop.last %} {% endif %}
{% endfor %} {{ btn.next }}
{% if config.catalog_link %}
| <a href="{{ config.root }}{{ board.dir }}{{ config.catalog_link }}">{% trans %}Catalog{% endtrans %}</a>
{% endif %}
</div>
{{ boardlist.bottom }}
{{ config.ad.bottom }}

3
templates/installer/config.html

@ -87,6 +87,9 @@
<legend>Miscellaneous</legend>
<label for="secure_trip_salt">Secure trip (##) salt:</label>
<input type="text" id="secure_trip_salt" name="secure_trip_salt" value="{{ config.secure_trip_salt }}" size="40">
<label for="more">Additional configuration:</label>
<textarea id="more" name="more">{{ more }}</textarea>
</fieldset>
<p style="text-align:center">

53
templates/main.js

@ -70,26 +70,40 @@ var datelocale =
};
function alert(a) {
var handler, div;
var close = function() {
handler.fadeOut(400, function() { handler.remove(); });
return false;
};
function alert(a, do_confirm, confirm_ok_action, confirm_cancel_action) {
var handler, div, bg, closebtn, okbtn;
var close = function() {
handler.fadeOut(400, function() { handler.remove(); });
return false;
};
handler = $("<div id='alert_handler'></div>").hide().appendTo('body');
handler = $("<div id='alert_handler'></div>").hide().appendTo('body');
$("<div id='alert_background'></div>").click(close).appendTo(handler);
bg = $("<div id='alert_background'></div>").appendTo(handler);
div = $("<div id='alert_div'></div>").appendTo(handler);
$("<a id='alert_close' href='javascript:void(0)'><i class='fa fa-times'></i></div>")
.click(close).appendTo(div);
div = $("<div id='alert_div'></div>").appendTo(handler);
closebtn = $("<a id='alert_close' href='javascript:void(0)'><i class='fa fa-times'></i></div>")
.appendTo(div);
$("<div id='alert_message'></div>").html(a).appendTo(div);
$("<div id='alert_message'></div>").html(a).appendTo(div);
$("<button class='button alert_button'>"+_("OK")+"</button>").click(close).appendTo(div);
okbtn = $("<button class='button alert_button'>"+_("OK")+"</button>").appendTo(div);
handler.fadeIn(400);
if (do_confirm) {
confirm_ok_action = (typeof confirm_ok_action !== "function") ? function(){} : confirm_ok_action;
confirm_cancel_action = (typeof confirm_cancel_action !== "function") ? function(){} : confirm_cancel_action;
okbtn.click(confirm_ok_action);
$("<button class='button alert_button'>"+_("Cancel")+"</button>").click(confirm_cancel_action).click(close).appendTo(div);
bg.click(confirm_cancel_action);
okbtn.click(confirm_cancel_action);
closebtn.click(confirm_cancel_action);
}
bg.click(close);
okbtn.click(close);
closebtn.click(close);
handler.fadeIn(400);
}
var saved = {};
@ -106,7 +120,10 @@ var codestyles = {
{% for stylesheet in code_stylesheets %}{% raw %}'{% endraw %}{{ stylesheet.name|addslashes }}{% raw %}' : '{% endraw %}{{ stylesheet.uri|addslashes }}{% raw %}',
{% endraw %}{% endfor %}{% raw %}
};
var board_name = false;
if (typeof board_name === 'undefined') {
var board_name = false;
}
function changeStyle(styleName, link) {
{% endraw %}
@ -163,13 +180,7 @@ function changeStyle(styleName, link) {
{% endraw %}
{% if config.stylesheets_board %}
{# This is such an unacceptable mess. There needs to be an easier way. #}
{# Needs fix for slugify #}
var matches = document.URL.match(/\/(\w+)\/($|{{ config.dir.res|replace({'/': '\\/'}) }}{{ config.file_page|replace({'%d': '\\d+', '.': '\\.'}) }}|{{ config.file_index|replace({'.': '\\.'}) }}|{{ config.file_page|replace({'%d': '\\d+', '.': '\\.'}) }})/);
{% raw %}
if (matches) {
board_name = matches[1];
}
if (!localStorage.board_stylesheets) {
localStorage.board_stylesheets = '{}';

2
templates/mod/ban_form.html

@ -24,7 +24,7 @@
</th>
<td>
{% if not hide_ip %}
<input type="text" name="ip" id="ip" size="30" maxlength="40" value="{{ ip }}">
<input type="text" name="ip" id="ip" size="30" maxlength="40" value="{{ ip|e }}">
{% else %}
<em>{% trans 'hidden' %}</em>
{% endif %}

121
templates/mod/dashboard.html

@ -56,6 +56,127 @@
{% if mod|hasPermission(config.mod.edit_config) %}
<li><a href="?/config">{% trans 'Configuration' %}</a></li>
{% endif %}
=======
<fieldset>
<legend>{% trans 'Boards' %}</legend>
<ul>
{% for board in boards %}
<li>
<a href="?/{{ config.board_path|sprintf(board.uri) }}{{ config.file_index }}">{{ config.board_abbreviation|sprintf(board.uri) }}</a>
-
{{ board.title|e }}
{% if board.subtitle %}
<small>&mdash;
{% if config.allow_subtitle_html %}
{{ board.subtitle }}
{% else %}
{{ board.subtitle|e }}
{% endif %}
</small>
{% endif %}
{% if mod|hasPermission(config.mod.manageboards) %}
<a href="?/edit/{{ board.uri }}"><small>[{% trans 'edit' %}]</small></a>
{% endif %}
{% if mod|hasPermission(config.mod.edit_pages) %}
<a href="?/edit_pages/{{ board.uri }}"><small>[{% trans 'pages' %}]</small></a>
{% endif %}
</li>
{% endfor %}
{% if mod|hasPermission(config.mod.newboard) %}
<li style="margin-top:15px"><a href="?/new-board"><strong>{% trans 'Create new board' %}</strong></a></li>
{% endif %}
</ul>
</fieldset>
<fieldset>
<legend>{% trans 'Messages' %}</legend>
<ul>
{% if mod|hasPermission(config.mod.noticeboard) %}
{% if noticeboard|count > 0 %}
<li>
{% trans 'Noticeboard' %}:
<ul>
{% for post in noticeboard %}
<li>
<a href="?/noticeboard#{{ post.id }}">
{% if post.subject %}
{{ post.subject|e }}
{% else %}
<em>{% trans 'no subject' %}</em>
{% endif %}
</a>
<small class="unimportant">
&mdash; by
{% if post.username %}
{{ post.username|e }}
{% else %}
<em>deleted?</em>
{% endif %}
at
{{ post.time|date(config.post_date) }}
</small>
</li>
{% endfor %}
</ul>
</li>
{% endif %}
<li><a href="?/noticeboard">{% trans 'View all noticeboard entries' %}</a></li>
{% endif %}
<li><a href="?/edit_news">{% trans 'News' %}</a></li>
<li>
<a href="?/inbox">
{% trans 'PM inbox' %}
{% if unread_pms > 0 %}<strong>{%endif %}({{ unread_pms }} unread){% if unread_pms > 0 %}</strong>{%endif %}
</a>
</li>
</ul>
</fieldset>
<fieldset>
<legend>{% trans 'Administration' %}</legend>
<ul>
{% if mod|hasPermission(config.mod.reports) %}
<li>
{% if reports > 0 %}<strong>{% endif %}
<a href="?/reports">{% trans 'Report queue' %} ({{ reports }})</a>
{% if reports > 0 %}</strong>{% endif %}
</li>
{% endif %}
{% if mod|hasPermission(config.mod.view_banlist) %}
<li><a href="?/bans">{% trans 'Ban list' %}</a></li>
{% endif %}
{% if config.ban_appeals and mod|hasPermission(config.mod.view_ban_appeals) %}
<li><a href="?/ban-appeals">{% trans 'Ban appeals' %}</a></li>
{% endif %}
{% if mod|hasPermission(config.mod.manageusers) %}
<li><a href="?/users">{% trans 'Manage users' %}</a></li>
{% elseif mod|hasPermission(config.mod.change_password) %}
<li><a href="?/users/{{ mod.id }}">{% trans 'Change password' %}</a></li>
{% endif %}
{% if mod|hasPermission(config.mod.themes) %}
<li><a href="?/themes">{% trans 'Manage themes' %}</a></li>
{% endif %}
{% if mod|hasPermission(config.mod.modlog) %}
<li><a href="?/log">{% trans 'Moderation log' %}</a></li>
{% endif %}
{% if mod|hasPermission(config.mod.edit_pages) %}
<li><a href="?/edit_pages">{% trans 'Global static pages' %}</a></li>
{% endif %}
{% if mod|hasPermission(config.mod.recent) %}
<li><a href="?/recent/25">{% trans 'Recent posts' %}</a></li>
{% endif %}
{% if mod|hasPermission(config.mod.rebuild) %}
<li><a href="?/rebuild">{% trans 'Rebuild' %}</a></li>
{% endif %}
{% if mod|hasPermission(config.mod.edit_config) %}
<li><a href="?/config">{% trans 'Configuration' %}</a></li>
{% endif %}
</ul>
</fieldset>
{% if mod|hasPermission(config.mod.search) %}

29
templates/mod/edit_page.html

@ -0,0 +1,29 @@
<div style="text-align:center">
<form method="POST">
<input name="token" value="{{ token }}" type="hidden">
<table>
<tr>
<th>{% trans %}Markup method{% endtrans %}
{% set allowed_html = config.allowed_html %}
{% trans %}<p class="unimportant">"markdown" is provided by <a href="http://parsedown.org/">parsedown</a>. Note: images disabled.</p>
<p class="unimportant">"html" allows the following tags:<br/>{{ allowed_html }}</p>
<p class="unimportant">"infinity" is the same as what is used in posts.</p>
<p class="unimportant">This page will not convert between formats,<br/>choose it once or do the conversion yourself!</p>{% endtrans %}
</th>
<td>
<select name="method">
{% for markup in ['markdown', 'html', 'infinity'] %}
<option value="{{ markup }}" {% if page.type == markup %}selected{% endif %}>{{ markup }}</option>
{% endfor %}
</select>
</td></tr>
<tr><th>{% trans %}Page content{% endtrans %}
<br/>
<span class="unimportant">{% trans %}Page will appear at:{% endtrans %}
{% if board %} <a href="/{{ board.uri }}/{{ page.name }}.html">{{ config.domain }}/{{ board.uri }}/{{ page.name }}.html</a>
{% else %} <a href="/{{ page.name }}.html">{{ config.site }}/{{ page.name }}.html</a>
{% endif %}</span></th><td><textarea name="content" style="height:500px;width:500px">{{content}}</textarea></td><tr>
</table>
<input type="submit" value="Save page">
</form>
</div>

16
templates/mod/log.html

@ -10,7 +10,15 @@
<tr>
<td class="minimal">
{% if log.username %}
<a href="?/log:{{ log.username|e }}">{{ log.username|e }}</a>
{% if hide_names %}
<em>hidden</em>
{% else %}
{% if not mod|hasPermission(config.mod.modlog) %}
<a href="?/new_PM/{{ log.username|e }}">{{ log.username|e }}</a>
{% else %}
<a href="?/log:{{ log.username|e }}">{{ log.username|e }}</a>
{% endif %}
{% endif %}
{% elseif log.mod == -1 %}
<em>system</em>
{% else %}
@ -44,7 +52,11 @@
{% if count > logs|count %}
<p class="unimportant" style="text-align:center;word-wrap:break-word">
{% for i in range(0, (count - 1) / config.mod.modlog_page) %}
<a href="?/log{% if username %}:{{ username }}{% endif %}/{{ i + 1 }}">[{{ i + 1 }}]</a>
{% if public %}
<a href="?page={{ i + 1 }}&amp;board={{ board|url_encode }}">[{{ i + 1 }}]</a>
{% else %}
<a href="?/log{% if username %}:{{ username }}{% elseif board %}:b:{{ board }}{% endif %}/{{ i + 1 }}">[{{ i + 1 }}]</a>
{% endif %}
{% endfor %}
</p>
{% endif %}

2
templates/mod/news.html

@ -40,7 +40,7 @@
<div class="ban">
{% if mod|hasPermission(config.mod.news_delete) %}
<span style="float:right;padding:2px">
<a class="unimportant" href="?/news/delete/{{ post.id }}/{{ post.delete_token }}">[{% trans 'delete' %}]</a>
<a class="unimportant" href="?/edit_news/delete/{{ post.id }}/{{ post.delete_token }}">[{% trans 'delete' %}]</a>
</span>
{% endif %}
<h2 id="{{ post.id }}">

34
templates/mod/pages.html

@ -0,0 +1,34 @@
<script type="text/javascript" src="js/jquery.min.js"></script>
<div style="text-align:center">
<p class="unimportant">
{% if board %}
{% set page_max = config.pages_max %}
{% trans %}This page allows you to create static pages for your board. The limit is {{ page_max }} pages per board. You will still have to link to your pages somewhere in your board, for example in a sticky or in the board's announcement. To make links in the board's announcement, use &lt;a&gt; HTML tags.{% endtrans %}
{% else %}
{% trans %}This page allows you to create static pages for your imageboard.{% endtrans %}
{% endif %}
<h2>{% trans %}Existing pages{% endtrans %}</h2>
{% if pages %}
<form>
<table style="margin:auto">
<tr><th>{% trans %}URL{% endtrans %}</th><th>{% trans %}Title{% endtrans %}</th><th>{% trans %}Edit{% endtrans %}</th><th>{% trans %}Delete{% endtrans %}</tr>
{% for page in pages %}
<tr><td>{{ page.name }}</td><td>{{ page.title }}</td><td><a href="?/edit_page/{{ page.id }}">{% trans %}Edit{% endtrans %}</a></td><td><a href="?/edit_pages/delete/{{ page.name }}{% if board %}/{{ board }}{% endif %}/{{ page.delete_token }}">{% trans %}Delete{% endtrans %}</a></td>
{% endfor %}
{% else %}
<em>No pages yet!</em>
{% endif %}
</table>
</form>
<hr/>
<h2>{% trans %}Create a new page{% endtrans %}</h2>
<form method="POST">
<input type="hidden" name="token" value="{{ token }}">
<table>
<tr><th>{% trans %}URL{% endtrans %}</th><th>{% trans %}Title{% endtrans %}</th></tr>
<tr><td><input type="text" name="page"></td><td><input type="text" name="title"></td>
</table>
<input type="submit" value="{% trans %}Create{% endtrans %}">
</form>
</div>

6
templates/mod/rebuilt.html

@ -1,12 +1,12 @@
<div class="ban">
<h2>{% trans 'Rebuilt' %}</h2>
<p>
<a href="?/rebuild">{% trans 'Go back and rebuild again' %}</a>.
</p>
<ul>
{% for log in logs %}
<li>{{ log }}</li>
{% endfor %}
</ul>
<p>
<a href="?/rebuild">{% trans 'Go back and rebuild again' %}</a>.
</p>
</div>

2
templates/mod/view_ip.html

@ -45,7 +45,7 @@
</td>
{% if mod|hasPermission(config.mod.remove_notes) %}
<td class="minimal">
<a href="?/IP/{{ ip }}/remove_note/{{ note.id }}">
<a href="?/IP/{{ ip|url_encode(true) }}/remove_note/{{ note.id }}">
<small>[{% trans 'remove' %}]</small>
</a>
</td>

14
templates/page.html

@ -5,12 +5,14 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1">
<script type="text/javascript">
active_page = "page";
var active_page = "page";
</script>
{% include 'header.html' %}
<title>{{ title }}</title>
</head>
<body>
<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %} active-page" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
{{ boardlist.top }}
{% if pm %}<div class="top_notice">You have <a href="?/PM/{{ pm.id }}">an unread PM</a>{% if pm.waiting > 0 %}, plus {{ pm.waiting }} more waiting{% endif %}.</div><hr>{% endif %}
<header>
<h1>{{ title }}</h1>
@ -24,10 +26,10 @@
{{ body }}
<hr>
<footer>
<p class="unimportant" style="margin-top:20px;text-align:center;">- <a href="http://tinyboard.org/">Tinyboard</a> +
<a href='https://int.vichan.net/devel/'>vichan</a> {{ config.version }} -
<br><a href="http://tinyboard.org/">Tinyboard</a> Copyright &copy; 2010-2014 Tinyboard Development Group
<br><a href="https://engine.vichan.net/">vichan</a> Copyright &copy; 2012-2015 vichan-devel</p>
<p class="unimportant" style="margin-top:20px;text-align:center;">- Tinyboard +
<a href="https://engine.vichan.net/">vichan</a> {{ config.version }} -
<br>Tinyboard Copyright &copy; 2010-2014 Tinyboard Development Group
<br><a href="https://engine.vichan.net/">vichan</a> Copyright &copy; 2012-2016 vichan-devel</p>
</footer>
</body>
</html>

2
templates/post/flag.html

@ -1,5 +1,5 @@
{% if config.display_flags and post.modifiers.flag %}
<img
<img
{% if config.country_flags_condensed %}
class="flag flag-{{ post.modifiers.flag }}" src="{{ config.image_blank }}"
{% else %}

4
templates/post/name.html

@ -1,5 +1,5 @@
{% set capcode = post.capcode|capcode %}
{% if post.email|length > 0 and (config.hide_sage!=true or post.email!='sage') %}
{% if post.email|length > 0 and (config.hide_sage!=true or post.email!='sage') and config.hide_email!=true %}
{# start email #}
<a class="email" href="mailto:{{ post.email }}">
{% endif %}
@ -7,7 +7,7 @@
{% if post.trip|length > 0 %}
<span {% if capcode.trip %}style="{{ capcode.trip }}" {% endif %}class="trip">{{ post.trip }}</span>
{% endif %}
{% if post.email|length > 0 %}
{% if post.email|length > 0 and (config.hide_sage!=true or post.email!='sage') and config.hide_email!=true %}
{# end email #}
</a>
{% endif %}

8
templates/post/post_controls.html

@ -41,7 +41,6 @@
{% endif %}
{% endif %}
{% if mod|hasPermission(config.mod.move, board.uri) %}
{% if not post.thread %}
<a title="{% trans %}Move thread to another board{% endtrans %}" href="?/{{ board.dir }}move/{{ post.id }}">{{ config.mod.link_move }}</a>&nbsp;
@ -49,6 +48,13 @@
<a title="{% trans %}Move reply to another board{% endtrans %}" href="?/{{ board.dir }}move_reply/{{ post.id }}">{{ config.mod.link_move }}</a>&nbsp;
{% endif %}
{% endif %}
{% if mod|hasPermission(config.mod.cycle, board.uri) %}
{% if post.cycle %}
<a title="{% trans %}Make thread not cycle{% endtrans %}" href="?/{{ secure_link(board.dir ~ 'uncycle/' ~ post.id) }}">{{ config.mod.link_uncycle }}</a>&nbsp;
{% else %}
<a title="{% trans %}Make thread cycle{% endtrans %}" href="?/{{ secure_link(board.dir ~ 'cycle/' ~ post.id) }}">{{ config.mod.link_cycle }}</a>&nbsp;
{% endif %}
{% endif %}
{% if mod|hasPermission(config.mod.editpost, board.uri) %}
<a title="{% trans %}Edit post{% endtrans %}" href="?/{{ board.dir }}edit{% if config.mod.raw_html_default %}_raw{% endif %}/{{ post.id }}">{{ config.mod.link_editpost }}</a>&nbsp;
{% endif %}

4
templates/post/poster_id.html

@ -1,7 +1,7 @@
{% if config.poster_ids %}
{% if post.thread %}
ID: <span class="poster_id">{{ post.ip|poster_id(post.thread) }}</span>
<span class="poster_id">{{ post.ip|poster_id(post.thread) }}</span>
{% else %}
ID: <span class="poster_id">{{ post.ip|poster_id(post.id) }}</span>
<span class="poster_id">{{ post.ip|poster_id(post.id) }}</span>
{% endif %}
{% endif %}

15
templates/post_thread.html

@ -1,7 +1,7 @@
{% filter remove_whitespace %}
{# tabs and new lines will be ignored #}
<div id="thread_{{ post.id }}" data-board="{{ board.uri }}">
<div class="thread" id="thread_{{ post.id }}" data-board="{{ board.uri }}">
{% if not index %}<a id="{{ post.id }}" class="post_anchor"></a>{% endif %}
{% include 'post/fileinfo.html' %}
@ -19,25 +19,32 @@
<a class="post_no" onclick="citeReply({{ post.id }})" href="{% if isnoko50 %}{{ post.link('q', '50') }}{% else %}{{ post.link('q') }}{% endif %}">{{ post.id }}</a>
{% if post.sticky %}
{% if config.font_awesome %}
<i class="fa fa-thumb-tack"></i>
<i class="fa fa-thumb-tack" title="Sticky"></i>
{% else %}
<img class="icon" title="Sticky" src="{{ config.image_sticky }}" alt="Sticky" />
{% endif %}
{% endif %}
{% if post.locked %}
{% if config.font_awesome %}
<i class="fa fa-lock"></i>
<i class="fa fa-lock" title="Locked"></i>
{% else %}
<img class="icon" title="Locked" src="{{ config.image_locked }}" alt="Locked" />
{% endif %}
{% endif %}
{% if post.bumplocked and (config.mod.view_bumplock < 0 or (post.mod and post.mod|hasPermission(config.mod.view_bumplock, board.uri))) %}
{% if config.font_awesome %}
<i class="fa fa-anchor"></i>
<i class="fa fa-anchor" title="Bumplocked"></i>
{% else %}
<img class="icon" title="Bumplocked" src="{{ config.image_bumplocked }}" alt="Bumplocked" />
{% endif %}
{% endif %}
{% if post.cycle %}
{% if config.font_awesome %}
<i class="fa fa-refresh" title="Cyclical"></i>
{% else %}
<img class="icon" title="Cyclical" src="{{ config.image_sticky }}" alt="Cyclical" />
{% endif %}
{% endif %}
{% if index %}
<a href="{{ post.root }}{{ board.dir }}{{ config.dir.res }}{{ link_for(post) }}">[{% trans %}Reply{% endtrans %}]</a>
{% endif %}

1
templates/posts.sql

@ -17,6 +17,7 @@ CREATE TABLE IF NOT EXISTS ``posts_{{ board }}`` (
`ip` varchar(39) CHARACTER SET ascii NOT NULL,
`sticky` int(1) NOT NULL,
`locked` int(1) NOT NULL,
`cycle` int(1) NOT NULL,
`sage` int(1) NOT NULL,
`embed` text,
`slug` varchar(256) DEFAULT NULL,

22
templates/report.html

@ -0,0 +1,22 @@
<form action="{{ config.post_url }}" method="post" id="report_form">
{% if error %}
<div class="error">
<strong>{{ error|e }}</strong>
</div>
{% endif %}
<input type="hidden" name="board" value="{{ board.uri }}">
<input type="hidden" name="{{ post|e }}" value="1">
{% if global %}
<input type="hidden" name="global" value="1">
<div><h1>Attention!</h1><p>This form is only for reporting <strong>child pornography</strong>, <strong>bot spam</strong> and <strong>credit card numbers, social security numbers or banking information</strong>. DMCA requests and all other deletion requests <em>MUST</em> be sent via email to [email protected].</p><p>8chan is unmoderated and allows posts without collecting <em>ANY</em> information from the poster less the details of their post. Furthermore, all boards on 8chan are user created and not actively monitored by anyone but the board creator.</p><p>8chan has a small volunteer staff to handle this queue, please do not waste their time by filling it with nonsense! <em>If you made a report with this tool and the post was not deleted, <strong>do not make the report again!</strong> Email [email protected] instead.</em> Abuse of the global report system could lead to address blocks against your IP from 8chan.</p><p>Again, 8chan's global volunteers <em>do not</em> handle board specific issues. You most likely want to click "Report" instead to reach the creator and volunteers he assigned to this board.</p>
{% endif %}
<p>{% trans %}Enter reason below...{% endtrans %}</p>
<input type="text" id="reason" name="reason" value="{{ reason_prefill|e|addslashes }}">
{% if config.report_captcha %}
<p>{% trans %}To submit your report, please fill out the CAPTCHA below.{% endtrans %}</p>
{{ captcha['html'] }}<br/>
<input class="captcha_text" name="captcha_text" size="25" maxlength="6" autocomplete="off" type="text" value="">
<input class="captcha_cookie" name="captcha_cookie" type="hidden" autocomplete="off" value="{{ captcha['cookie']|e }}"><br/>
{% endif %}
<input name="report" value="{% trans %}Submit{% endtrans %}" type="submit">
</form>

21
templates/report_delete.html

@ -1,13 +1,16 @@
{% if config.allow_delete %}
<div class="delete">
{% trans %}Delete Post{% endtrans %} [<input title="Delete file only" type="checkbox" name="file" id="delete_file" />
<label for="delete_file">{% trans %}File{% endtrans %}</label>] <label for="password">{% trans %}Password{% endtrans %}</label>
<div id="post-moderation-fields">
{% if config.allow_delete %}
<div id="delete-fields">
{% trans %}Delete Post{% endtrans %} [<input title="Delete file only" type="checkbox" name="file" id="delete_file" />
<label for="delete_file">{% trans %}File{% endtrans %}</label>] <label for="password">{% trans %}Password{% endtrans %}</label>
<input id="password" type="password" name="password" size="12" maxlength="18" />
<input type="submit" name="delete" value="{% trans %}Delete{% endtrans %}" />
</div>
{% endif %}
<div class="delete" style="clear:both">
<label for="reason">{% trans %}Reason{% endtrans %}</label>
</div>
{% endif %}
<div id="report-fields">
<label for="reason">{% trans %}Reason{% endtrans %}</label>
<input id="reason" type="text" name="reason" size="20" maxlength="30" />
<input type="submit" name="report" value="{% trans %}Report{% endtrans %}" />
</div>
</div>
</div>

10
templates/themes/basic/index.html

@ -12,7 +12,7 @@
<!--{% if config.default_stylesheet.1 != '' %}<link rel="stylesheet" type="text/css" id="stylesheet" href="{{ config.uri_stylesheets }}{{ config.default_stylesheet.1 }}">{% endif %}-->
{% if config.font_awesome %}<link rel="stylesheet" href="{{ config.root }}{{ config.font_awesome_css }}">{% endif %}
</head>
<body>
<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %}" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
<div class="bar top">
{{ boardlist.top }}
</div>
@ -41,10 +41,10 @@
<hr/>
<footer>
<p class="unimportant" style="margin-top:20px;text-align:center;">- <a href="http://tinyboard.org/">Tinyboard</a> +
<a href='https://int.vichan.net/devel/'>vichan</a> {{ config.version }} -
<br><a href="http://tinyboard.org/">Tinyboard</a> Copyright &copy; 2010-2014 Tinyboard Development Group
<br><a href="https://engine.vichan.net/">vichan</a> Copyright &copy; 2012-2015 vichan-devel</p>
<p class="unimportant" style="margin-top:20px;text-align:center;">- Tinyboard +
<a href="https://engine.vichan.net/">vichan</a> {{ config.version }} -
<br>Tinyboard Copyright &copy; 2010-2014 Tinyboard Development Group
<br><a href="https://engine.vichan.net/">vichan</a> Copyright &copy; 2012-2016 vichan-devel</p>
</footer>
</body>
</html>

28
templates/themes/catalog/catalog.html

@ -4,12 +4,12 @@
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
<script type='text/javascript'>
active_page = "catalog";
var active_page = "catalog"
, board_name = "{{ board }}";
</script>
{% include 'header.html' %}
<title>{{ settings.title }} (/{{ board }}/)</title>
</head>
<body class="theme-catalog board-{{ board }}">
<body class="8chan vichan {% if mod %}is-moderator{% else %}is-not-moderator{% endif %} theme-catalog active-catalog" data-stylesheet="{% if config.default_stylesheet.1 != '' %}{{ config.default_stylesheet.1 }}{% else %}default{% endif %}">
<div class="top bar topbar">
{{ boardlist.top }}
</div>
@ -42,14 +42,16 @@
</select>
</div>
<div class="threads">
<ul id="Grid">
{% for post in recent_posts %}
<li class="mix"
<div class="threads">
<div id="Grid">
{% for post in recent_posts %}
<div class="mix"
data-reply="{{ post.reply_count }}"
data-bump="{{ post.bump }}"
data-time="{{ post.time }}"
data-board="{{ post.board }}"
data-bump="{{ post.bump }}"
data-time="{{ post.time }}"
data-id="{{ post.id }}"
data-sticky="{% if post.sticky %}true{% else %}false{% endif %}"
data-locked="{% if post.locked %}true{% else %}false{% endif %}"
>
<div class="thread grid-li grid-size-small">
<a href="{{post.link}}">
@ -61,7 +63,7 @@
id="img-{{ post.id }}" data-subject="{% if post.subject %}{{ post.subject|e }}{% endif %}" data-name="{{ post.name|e }}" data-muhdifference="{{ post.muhdifference }}" class="{{post.board}} thread-image" title="{{post.bump|date('%b %d %H:%M')}}">
</a>
<div class="replies">
<strong>R: {{ post.reply_count }} / I: {{ post.image_count }}</strong>
<strong>R: {{ post.reply_count }} / I: {{ post.image_count }}{% if post.sticky %} (sticky){% endif %}</strong>
{% if post.subject %}
<p class="intro">
<span class="subject">
@ -75,9 +77,9 @@
{{ post.body }}
</div>
</div>
</li>
</div>
{% endfor %}
</ul>
</div>
</div>
<hr/>

19
templates/themes/catalog/index.rss

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
<channel>
<title>/{{ board.uri }}/ - {{ board.title|e }}</title>
<link>{{ config.root }}{{ board.uri }}/</link>
<description>Live feed of new threads on the board /{{ board.uri }}/ - {{ board.title|e }}.</description>
<atom:link href="{{ config.root }}{{ board.uri }}/index.rss" rel="self" type="application/rss+xml"/>
{% for post in recent_posts %}
<item>
<title>{% if post.subject %}{{ post.subject|e }}{% else %}{{ post.body_nomarkup[:256]|remove_modifiers|e }}{% endif %}</title>
<link>{{ config.root }}{{ board.uri }}/res/{{ post.id }}.html</link>
<guid>{{ config.root }}{{ board.uri }}/res/{{ post.id }}.html</guid>
<comments>{{ config.root }}{{ board.uri }}/res/{{ post.id }}.html</comments>
<pubDate>{{ post.pubdate }}</pubDate>
<description><![CDATA[ <a href='{{ config.root }}{{ board.uri }}/res/{{ post.id }}.html' target=_blank><img style='float:left;margin:8px' border=0 src='{% if not config.uri_thumb %}{{ config.root }}{% endif %}{{ post.file }}'></a> {{ post.body }} ]]></description>
</item>
{% endfor %}
</channel>
</rss>

40
templates/themes/catalog/theme.php

@ -15,23 +15,30 @@
// - post-thread (a thread has been made)
if ($action === 'all') {
foreach ($boards as $board) {
if ($config['smart_build']) {
$b = new Catalog();
$action = generation_strategy("sb_catalog", array($board));
if ($action == 'delete') {
file_unlink($config['dir']['home'] . $board . '/catalog.html');
} else {
$b->build($board);
file_unlink($config['dir']['home'] . $board . '/index.rss');
}
elseif ($action == 'rebuild') {
$b->build($settings, $board);
}
}
} elseif (in_array($board, $boards) &&
$action == 'post-thread' ||
($settings['update_on_posts'] && $action == 'post') ||
($settings['update_on_posts'] && $action == 'post-delete'))
{
if ($config['smart_build']) {
} elseif ($action == 'post-thread' || ($settings['update_on_posts'] && $action == 'post') || ($settings['update_on_posts'] && $action == 'post-delete') && in_array($board, $boards)) {
$b = new Catalog();
$action = generation_strategy("sb_catalog", array($board));
if ($action == 'delete') {
file_unlink($config['dir']['home'] . $board . '/catalog.html');
} else {
$b->build($board);
file_unlink($config['dir']['home'] . $board . '/index.rss');
}
}
elseif ($action == 'rebuild') {
$b->build($settings, $board);
}
}
// FIXME: Check that Ukko is actually enabled
if ($settings['enable_ukko'] && (
@ -210,10 +217,13 @@
$post['file'] = $config['uri_thumb'] . $files[0]->thumb;
}
}
} else {
$post['file'] = $config['root'] . $config['image_deleted'];
}
if (empty($post['image_count']))
$post['image_count'] = 0;
$post['pubdate'] = date('r', $post['time']);
$posts[] = $post;
}
@ -247,5 +257,11 @@
'board' => $board_name,
'link' => $board_link
)));
file_write($config['dir']['home'] . $board_name . '/index.rss', Element('themes/catalog/index.rss', Array(
'config' => $config,
'recent_posts' => $recent_posts,
'board' => $board
)));
}
}

8
templates/themes/categories/news.html

@ -32,10 +32,10 @@
</div>
<footer>
<p class="unimportant" style="margin-top:20px;text-align:center;">- <a href="http://tinyboard.org/">Tinyboard</a> +
<a href='https://int.vichan.net/devel/'>vichan</a> {{ config.version }} -
<br><a href="http://tinyboard.org/">Tinyboard</a> Copyright &copy; 2010-2014 Tinyboard Development Group
<br><a href="https://engine.vichan.net/">vichan</a> Copyright &copy; 2012-2015 vichan-devel</p>
<p class="unimportant" style="margin-top:20px;text-align:center;">- Tinyboard +
<a href="https://engine.vichan.net/">vichan</a> {{ config.version }} -
<br>Tinyboard Copyright &copy; 2010-2014 Tinyboard Development Group
<br><a href="https://engine.vichan.net/">vichan</a> Copyright &copy; 2012-2016 vichan-devel</p>
</footer>
</body>
</html>

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save