From: Michael Wallner Date: Tue, 31 Mar 2015 15:14:07 +0000 (+0200) Subject: inital commit X-Git-Url: https://git.m6w6.name/?p=pharext%2Fpharext.org;a=commitdiff_plain;h=ebc0d017c8a24bd16ca1f4347b39b07ba4349135 inital commit --- ebc0d017c8a24bd16ca1f4347b39b07ba4349135 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b551a21 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +vendor +nbproject +app/credentials.ini diff --git a/app/BaseUrl.php b/app/BaseUrl.php new file mode 100644 index 0000000..1116543 --- /dev/null +++ b/app/BaseUrl.php @@ -0,0 +1,38 @@ + filter_input(INPUT_SERVER, "HTTPS", FILTER_VALIDATE_BOOLEAN) ? "https":"http", + "path" => dirname(filter_input(INPUT_SERVER, "SCRIPT_NAME"))."/" + ); + parent::__construct($self, $url, + self::JOIN_PATH | + self::SANITIZE_PATH | + self::STRIP_QUERY | + self::STRIP_AUTH | + self::FROM_ENV + ); + } + /** + * Extract path info + * + * @param mixed $url full request url + * @return string + */ + function pathinfo($url) { + $url = new Url($this, $url, Url::FROM_ENV | Url::STRIP_QUERY); + $info = substr($url, strlen($this)-1); + return $info; + } + + +} diff --git a/app/Controller.php b/app/Controller.php new file mode 100644 index 0000000..950ae14 --- /dev/null +++ b/app/Controller.php @@ -0,0 +1,8 @@ +app = $app; + $this->github = $github; + $this->app->getView()->addData(["location" => "github"]); + $this->app->getView()->registerFunction("check", [$this, "checkRepoHook"]); + } + + protected function checkToken() { + if ($this->github->hasToken()) { + return true; + } + $this->app->redirect($this->app->getBaseUrl()->mod([ + "path" => "github/signin", + "query" => new QueryString(["returnto" => $this->session->current]) + ])); + return false; + } + + function setSession(Session $session) { + $this->session = $session; + } + + function checkRepoHook($repo) { + if ($repo->hooks) { + foreach ($repo->hooks as $hook) { + if ($hook->name === "web" && $hook->config->url === $this->github->getConfig()->hook->url) { + return true; + } + } + } + return false; + } + + function createLinkGenerator($links) { + return function($which) use($links) { + if (!isset($links[$which])) { + if ($which !== "next" || !isset($links["last"])) { + return null; + } else { + $which = "last"; + } + } + $url = new Url($links[$which], null, 0); + $qry = new QueryString($url->query); + return $qry->getInt("page", 1); + }; + } + +} diff --git a/app/Controller/Github/Callback.php b/app/Controller/Github/Callback.php new file mode 100644 index 0000000..ac2ce7b --- /dev/null +++ b/app/Controller/Github/Callback.php @@ -0,0 +1,34 @@ +app->getRequest()->getQuery("error")) { + $this->app->getView()->addData([ + "error" => $this->app->getRequest()->getQuery("error_description") + ]); + } else { + try { + $this->github->fetchToken( + $this->app->getRequest()->getQuery("code"), + $this->app->getRequest()->getQuery("state"), + function($json) { + $this->github->setToken($json->access_token); + })->send(); + if (isset($this->session->returnto)) { + $this->app->redirect($this->session->returnto); + } else { + $this->app->redirect( + $this->app->getBaseUrl()->mod("./github")); + } + } catch (\app\Github\Exception $exception) { + $this->app->getView()->addData(compact("exception")); + } + } + $this->app->display("github/callback"); + } +} diff --git a/app/Controller/Github/Index.php b/app/Controller/Github/Index.php new file mode 100644 index 0000000..f55eb0c --- /dev/null +++ b/app/Controller/Github/Index.php @@ -0,0 +1,35 @@ +checkToken()) { + try { + $this->github->fetchRepos( + $this->app->getRequest()->getQuery("page"), + [$this, "reposCallback"] + )->send(); + } catch (\app\Github\Exception $exception) { + $this->view->addData(compact("exception")); + } + $this->app->display("github/index"); + } + } + + function reposCallback($repos, $links) { + $this->app->getView()->addData(compact("repos")); + $this->app->getView()->registerFunction("link", $this->createLinkGenerator($links)); + + foreach ($repos as $repo) { + $this->github->fetchHooks($repo->full_name, function($hooks) use($repo) { + $repo->hooks = $hooks; + }); + } + } +} diff --git a/app/Controller/Github/Repo.php b/app/Controller/Github/Repo.php new file mode 100644 index 0000000..0c33a01 --- /dev/null +++ b/app/Controller/Github/Repo.php @@ -0,0 +1,50 @@ +checkToken()) { + try { + $this->github->fetchRepo( + "$owner/$name", + [$this, "repoCallback"] + )->send(); + } catch (\app\Github\Exception $exception) { + $this->app->getView()->addData(compact("exception", "owner", "name")); + } + $this->app->display("github/repo"); + } + } + + function repoCallback($repo, $links) { + $this->app->getView()->addData(compact("repo")); + settype($repo->tags, "object"); + $this->github->fetchTags($repo->full_name, 1, $this->createTagsCallback($repo)); + $this->github->fetchReleases($repo->full_name, 1, $this->createReleasesCallback($repo)); + } + + function createReleasesCallback($repo) { + return function($releases, $links) use($repo) { + foreach ($releases as $release) { + $tag = $release->tag_name; + settype($repo->tags->$tag, "object"); + $repo->tags->$tag->release = $release; + } + }; + } + + function createTagsCallback($repo) { + return function($tags, $links) use ($repo) { + foreach ($tags as $tag) { + $name = $tag->name; + settype($repo->tags->$name, "object"); + $repo->tags->$name->tag = $tag; + } + }; + } +} diff --git a/app/Controller/Github/Signin.php b/app/Controller/Github/Signin.php new file mode 100644 index 0000000..05f3e3c --- /dev/null +++ b/app/Controller/Github/Signin.php @@ -0,0 +1,17 @@ +app->getBaseUrl()->mod("./github/callback"); + $location = $this->github->getAuthUrl($callback); + $this->app->redirect($location); + if ($returnto = $this->app->getRequest()->getQuery("returnto")) { + $this->session->returnto = $returnto; + } + } +} diff --git a/app/Controller/Homepage.php b/app/Controller/Homepage.php new file mode 100644 index 0000000..cf065ff --- /dev/null +++ b/app/Controller/Homepage.php @@ -0,0 +1,20 @@ +app = $app; + } + + function __invoke(array $args = null) { + $this->app->getResponse()->getBody()->append( + $this->app->getView()->render("pages/index") + ); + } +} diff --git a/app/Controller/Wikipage.php b/app/Controller/Wikipage.php new file mode 100644 index 0000000..f350f9a --- /dev/null +++ b/app/Controller/Wikipage.php @@ -0,0 +1,37 @@ +app = $app; + $app->getView()->addData([ + "pages" => $this->wikiPages() + ]); + } + + function __invoke(array $args = null) { + $title = $args["page"]; + $page = $this->wikiPath($args["page"]); + $this->app->display("pages/wiki", compact("title", "page")); + } + + function wikiPages() { + return array_filter(array_map(function($s) { + return strtr(basename($s, ".md"), "-", " "); + }, scandir(self::WIKI_PATH)), function($s) { + return $s{0} !== "."; + }); + } + function wikiPath($page) { + $file = basename(strtr($page, " ", "-"), ".md") . ".md"; + return self::WIKI_PATH . $file; + } +} \ No newline at end of file diff --git a/app/Github/API.php b/app/Github/API.php new file mode 100644 index 0000000..381533d --- /dev/null +++ b/app/Github/API.php @@ -0,0 +1,142 @@ +config = $config; + $this->client = new Client; + $this->tokens = $tokens ?: new Storage\Session; + $this->cache = $cache; + } + + function getConfig() { + return $this->config; + } + + function getClient() { + return $this->client; + } + + function getTokenStorage() { + return $this->tokens; + } + + function getCacheStorage() { + return $this->cache; + } + + function getCacheKey($ident, $page = null) { + return sprintf("%s:%s:%s", $this->getToken(), $ident, $page ?: 1); + } + + function hasToken() { + return $this->tokens->get("access_token"); + } + + function setToken($token) { + $this->tokens->set("access_token", $token, + $this->config->storage->token->ttl); + } + + function getToken() { + if ($this->tokens->get("access_token", $token, $ttl, true)) { + return $token; + } + if (isset($ttl)) { + throw new Exception\TokenExpired($ttl); + } + throw new Exception\TokenNotSet; + } + + function dropToken() { + $this->tokens->del("access_token"); + } + + function getAuthUrl($callback_url) { + $state = base64_encode(openssl_random_pseudo_bytes(24)); + $this->tokens->set("state", $state, 5*60); + $param = [ + "state" => $state, + "client_id" => $this->config->client->id, + "scope" => $this->config->client->scope, + "redirect_uri" => $callback_url, + ]; + return new Url("https://github.com/login/oauth/authorize", [ + "query" => new QueryString($param) + ], 0); + } + + function fetchToken($code, $state, callable $callback) { + if (!$this->tokens->get("state", $orig_state, $ttl, true)) { + if (isset($ttl)) { + throw new Exception\StateExpired($ttl); + } + throw new Exception\StateNotSet; + } + if ($state !== $orig_state) { + throw new Exception\StateMismatch($orig_state, $state); + } + + $fetch = new Fetch\Token($this, compact("code") + $this->config->client->toArray()); + return $fetch($callback); + } + + function fetchRepos($page, callable $callback) { + $fetch = new Fetch\Repos($this); + $fetch->setPage($page); + return $fetch($callback); + } + + function fetchRepo($repo, callable $callback) { + $fetch = new Fetch\Repo($this, compact("repo")); + return $fetch($callback); + } + + function fetchHooks($repo, callable $callback) { + $fetch = new Fetch\Hooks($this, compact("repo")); + return $fetch($callback); + } + + function fetchReleases($repo, $page, callable $callback) { + $fetch = new Fetch\Releases($this, compact("repo")); + $fetch->setPage($page); + return $fetch($callback); + } + + function fetchTags($repo, $page, callable $callback) { + $fetch = new Fetch\Tags($this, compact("repo")); + $fetch->setPage($page); + return $fetch($callback); + } +} diff --git a/app/Github/Exception.php b/app/Github/Exception.php new file mode 100644 index 0000000..3bad002 --- /dev/null +++ b/app/Github/Exception.php @@ -0,0 +1,8 @@ +seconds = abs($seconds); + parent::__construct("State expired $this->seconds seconds ago", 0, $previous); + } + + function getSeconds() { + return $this->seconds; + } +} diff --git a/app/Github/Exception/StateMismatch.php b/app/Github/Exception/StateMismatch.php new file mode 100644 index 0000000..94e16a9 --- /dev/null +++ b/app/Github/Exception/StateMismatch.php @@ -0,0 +1,12 @@ +seconds = abs($seconds); + parent::__construct("Token expired $this->seconds seconds ago", 0, $previous); + } + + function getSeconds() { + return $this->seconds; + } +} diff --git a/app/Github/Exception/TokenFetchFailed.php b/app/Github/Exception/TokenFetchFailed.php new file mode 100644 index 0000000..128330f --- /dev/null +++ b/app/Github/Exception/TokenFetchFailed.php @@ -0,0 +1,13 @@ +api = $api; + $this->config = $api->getConfig(); + $this->args = $args; + $this->url = new Url("https://api.github.com/", null, 0); + if (isset($this->config->fetch->{$this}->per_page)) { + header("X-Fetch-{$this}: ".$this->config->fetch->{$this}->per_page, false); + $this->url->query = "per_page=" . $this->config->fetch->{$this}->per_page; + } + } + + function setPage($page) { + $this->page = $page; + return $this; + } + + function getPage() { + return $this->page; + } + + function __toString() { + $parts = explode("\\", get_class($this)); + return strtolower(end($parts)); + } + + function getStorage(&$ttl = null) { + if (isset($this->config->storage->cache->{$this}->ttl)) { + $ttl = $this->config->storage->cache->{$this}->ttl; + } + return $this->api->getCacheStorage(); + } + + abstract function getRequest(); + abstract function getException($message, $code, $previous = null); + abstract function getCacheKey(); + + function parseLinks(Header $header) { + $params = new Params($header->value, ",", ";", "=", + Params::PARSE_RFC5988 | Params::PARSE_ESCAPED); + $links = []; + foreach ($params->params as $link => $param) { + // strip enclosing brackets + $links[$param["arguments"]["rel"]] = $link; + } + return $links; + } + + function __invoke(callable $callback) { + $ttl = -1; + $key = $this->getCacheKey(); + if (($cache = $this->api->getCacheStorage()) + && $cache->get($key, $cached, $ttl)) { + call_user_func_array($callback, $cached); + } else { + $this->enqueue($callback); + } + return $this->api->getClient(); + } + + protected function wrap(callable $callback) { + return function($response) use($callback) { + $rc = $response->getResponseCode(); + + if ($rc !== 200) { + if ($response->getHeader("Content-Type", Header::class)->match("application/json", Header::MATCH_WORD)) { + $message = json_decode($response->getBody())->message; + } else { + $message = $response->getBody(); + } + throw $this->getException($message, $rc); + } + + $json = json_decode($response->getBody()); + if (($link = $response->getHeader("Link", Header::class))) { + $links = $this->parseLinks($link); + } else { + $links = []; + } + + if (isset($json->error)) { + throw $this->getException($json->error_description, $rc); + } elseif (($cache = $this->getStorage($ttl))) { + $cache->set($this->getCacheKey(), [$json, $links], $ttl); + } + + $callback($json, $links); + + return true; + }; + } + + function enqueue(callable $callback) { + $request = $this->getRequest(); + $wrapper = $this->wrap($callback); + return $this->api->getClient()->enqueue($request, $wrapper); + } +} \ No newline at end of file diff --git a/app/Github/Fetch/Hooks.php b/app/Github/Fetch/Hooks.php new file mode 100644 index 0000000..c2730e4 --- /dev/null +++ b/app/Github/Fetch/Hooks.php @@ -0,0 +1,29 @@ +url->mod([ + "path" => "/repos/" . $this->args["repo"] . "/hooks", + ]); + return new Request("GET", $url, [ + "Accept" => "application/vnd.github.v3+json", + "Authorization" => "token " . $this->api->getToken(), + ]); + } + + function getException($message, $code, $previous = null) { + return new HooksFetchFailed($message, $code, $previous); + } + + function getCacheKey() { + return $this->api->getCacheKey(sprintf("hooks:%s", $this->args["repo"])); + } +} diff --git a/app/Github/Fetch/Releases.php b/app/Github/Fetch/Releases.php new file mode 100644 index 0000000..fa9c012 --- /dev/null +++ b/app/Github/Fetch/Releases.php @@ -0,0 +1,34 @@ +url->mod([ + "path" => "/repos/" . $this->args["repo"] . "/releases", + "query" => new QueryString([ + "page" => $this->getPage(), + ]) + ], 0); + echo $url."
"; + return new Request("GET", $url, [ + "Accept" => "application/vnd.github.v3+json", + "Authorization" => "token " . $this->api->getToken(), + ]); + } + + function getException($message, $code, $previous = null) { + return new ReleasesFetchFailed($message, $code, $previous); + } + + function getCacheKey() { + return $this->api->getCacheKey(sprintf("releases:%s", $this->args["repo"])); + } +} \ No newline at end of file diff --git a/app/Github/Fetch/Repo.php b/app/Github/Fetch/Repo.php new file mode 100644 index 0000000..a0889d6 --- /dev/null +++ b/app/Github/Fetch/Repo.php @@ -0,0 +1,27 @@ +url->mod("/repos/" . $this->args["repo"]); + return new Request("GET", $url, [ + "Accept" => "application/vnd.github.v3+json", + "Authorization" => "token " . $this->api->getToken(), + ]); + } + + function getException($message, $code, $previous = null) { + return new ReposFetchFailed($message, $code, $previous); + } + + function getCacheKey() { + return $this->api->getCacheKey(sprintf("repo:%s", $this->args["repo"])); + } +} diff --git a/app/Github/Fetch/Repos.php b/app/Github/Fetch/Repos.php new file mode 100644 index 0000000..30b3c4c --- /dev/null +++ b/app/Github/Fetch/Repos.php @@ -0,0 +1,49 @@ +url->mod([ + "path" => "/user/repos", + "query" => new QueryString([ + "page" => $this->getPage() + ]) + ]); + return new Request("GET", $url, [ + "Accept" => "application/vnd.github.v3+json", + "Authorization" => "token " . $this->api->getToken(), + ]); + } + + function getException($message, $code, $previous = null) { + return new ReposFetchFailed($message, $code, $previous); + } + + function getCacheKey() { + return $this->api->getCacheKey("repos", $this->page); + } + + protected function wrap(callable $callback) { + return parent::wrap(function($json, $links) use($callback) { + if (($cache = $this->getStorage($ttl))) { + foreach ($json as $repo) { + $key = $this->api->getCacheKey(sprintf("repo:%s", $repo->full_name)); + $cache->set($key, [$repo, $links], $ttl); + } + } + + $callback($json, $links); + + return true; + }); + } + +} diff --git a/app/Github/Fetch/Tags.php b/app/Github/Fetch/Tags.php new file mode 100644 index 0000000..9ddd606 --- /dev/null +++ b/app/Github/Fetch/Tags.php @@ -0,0 +1,33 @@ +url->mod([ + "path" => "/repos/" . $this->args["repo"] . "/tags", + "query" => new QueryString([ + "page" => $this->getPage(), + ]) + ], 0); + return new Request("GET", $url, [ + "Accept" => "application/vnd.github.v3+json", + "Authorization" => "token " . $this->api->getToken(), + ]); + } + + function getException($message, $code, $previous = null) { + return new TagsFetchFailed($message, $code, $previous); + } + + function getCacheKey() { + return $this->api->getCacheKey(sprintf("tags:%s", $this->args["repo"])); + } +} \ No newline at end of file diff --git a/app/Github/Fetch/Token.php b/app/Github/Fetch/Token.php new file mode 100644 index 0000000..16a270a --- /dev/null +++ b/app/Github/Fetch/Token.php @@ -0,0 +1,39 @@ + "application/json", + ]); + $request->getBody()->append( + new QueryString([ + "client_id" => $this->args["id"], + "client_secret" => $this->args["secret"], + "code" => $this->args["code"] + ]) + ); + return $request; + } + + function getException($message, $code, $previous = null) { + return new TokenFetchFailed($message, $code, $previous); + } + + function getCacheKey() { + return "access_token"; + } + + function getStorage(&$ttl = null) { + /* do not cache externally */ + return null; + } +} \ No newline at end of file diff --git a/app/Github/Storage.php b/app/Github/Storage.php new file mode 100644 index 0000000..3855107 --- /dev/null +++ b/app/Github/Storage.php @@ -0,0 +1,10 @@ +ns = $ns; + if (!$mc) { + $mc = new \Memcached("pharext"); + $mc->addServer("localhost", 11211); + } + $this->mc = $mc; + } + + private function key($key) { + return sprintf("%s:%s", $this->ns, $key); + } + + function get($key, &$val = null, &$ltl = null, $update = false) { + if (!$item = $this->mc->get($this->key($key))) { + return false; + } + + $val = $item->value; + $ttl = $item->ttl; + $set = $item->time; + + if (!isset($ttl)) { + return true; + } + $now = time(); + $ltl = $ttl - ($now - $set); + if ($ltl >= 0) { + if ($update) { + $item->time = time(); + $this->mc->set($this->key($key), $item, $ttl + 60*60*24); + } + return true; + } + return false; + } + + function set($key, $val, $ttl = null) { + $item = new Memcache\Item([ + "value" => $val, + "ttl" => $ttl, + "time" => isset($ttl) ? time() : null + ]); + $this->mc->set($this->key($key), $item, isset($ttl) ? $ttl + 60*60*24 : 0); + return $this; + } + + function del($key) { + $this->mc->delete($this->key($key)); + } +} + +namespace app\Github\Storage\Memcache; + +class Item +{ + public $value; + public $time; + public $ttl; + + function __construct(array $data) { + foreach ($data as $key => $val) { + $this->$key = $val; + } + } +} + diff --git a/app/Github/Storage/Session.php b/app/Github/Storage/Session.php new file mode 100644 index 0000000..943e975 --- /dev/null +++ b/app/Github/Storage/Session.php @@ -0,0 +1,43 @@ +ns = $ns; + } + + function set($key, $val, $ttl = null) { + $_SESSION[$this->ns][$key] = [$val, $ttl, isset($ttl) ? time() : null]; + return $this; + } + + function get($key, &$val = null, &$ltl = null, $update = false) { + if (!isset($_SESSION[$this->ns][$key])) { + return false; + } + list($val, $ttl, $set) = $_SESSION[$this->ns][$key]; + if (!isset($ttl)) { + return true; + } + $now = time(); + $ltl = $ttl - ($now - $set); + if ($ltl >= 0) { + if ($update) { + $_SESSION[$this->ns][$key][2] = $now; + } + return true; + } + return false; + } + + function del($key) { + unset($_SESSION[$this->ns][$key]); + return $this; + } +} diff --git a/app/Session.php b/app/Session.php new file mode 100644 index 0000000..87189dc --- /dev/null +++ b/app/Session.php @@ -0,0 +1,57 @@ +session as $key => $val) { + ini_set("session.$key", $val); + } + session_start(); + } + + function regenerateId() { + session_regenerate_id(); + return $this; + } + + function reset() { + $_SESSION = array(); + session_destroy(); + return $this; + } + + function __debugInfo() { + return $_SESSION; + } + + function &__get($p) { + return $_SESSION[$p]; + } + function &offsetGet($o) { + return $_SESSION[$o]; + } + function __set($p, $v) { + $_SESSION[$p] = $v; + } + function offsetSet($o, $v) { + $_SESSION[$o] = $v; + } + function __isset($p) { + return isset($_SESSION[$p]); + } + function offsetExists($o) { + return isset($_SESSION[$o]); + } + function __unset($p) { + unset($_SESSION[$p]); + } + function offsetUnset($o) { + unset($_SESSION[$o]); + } + +} diff --git a/app/Web.php b/app/Web.php new file mode 100644 index 0000000..e9d5d16 --- /dev/null +++ b/app/Web.php @@ -0,0 +1,77 @@ +baseUrl = $baseUrl; + $this->request = $request; + $this->response = $response; + $this->view = $view; + ob_start($response); + } + + function __invoke(Dispatcher $dispatcher) { + $route = $dispatcher->dispatch($this->request->getRequestMethod(), + $this->baseUrl->pathinfo($this->request->getRequestUrl())); + + switch ($route[0]) { + case Dispatcher::NOT_FOUND: + $this->response->setResponseCode(404); + $this->response->getBody()->append($this->view->render("404")); + break; + + case Dispatcher::METHOD_NOT_ALLOWED: + $this->response->setResponseCode(405); + $this->response->setHeader("Allowed", $route[1]); + $this->response->getBody()->append($this->view->render("405")); + break; + + case Dispatcher::FOUND: + list(, $handler, $args) = $route; + $handler(array_map("urldecode", $args)); + break; + } + + $this->response->send(); + } + + function display($view, array $data = []) { + $this->response->getBody()->append( + $this->view->render($view, $data)); + } + + function redirect($url, $code = 302) { + $this->response->setResponseCode($code); + $this->response->setHeader("Location", $url); + } + + function getBaseUrl() { + return $this->baseUrl; + } + + function getView() { + return $this->view; + } + + function getRequest() { + return $this->request; + } + + function getResponse() { + return $this->response; + } +} diff --git a/app/bootstrap.php b/app/bootstrap.php new file mode 100644 index 0000000..0cc7ab8 --- /dev/null +++ b/app/bootstrap.php @@ -0,0 +1,23 @@ +share(Config::class) + ->define(Config::class, [ + ":array" => parse_ini_file(__DIR__."/../config.ini", true), + ":section" => getenv("APP_ENVIRONMENT") + ]) + ->prepare(Config::class, function(Config $config) { + $credentials = parse_ini_file(__DIR__."/../credentials.ini", true); + foreach (new Config($credentials, getenv("APP_ENVIRONMENT")) as $app => $creds) { + /* one level down should suffice, i.e. $config->github->client = {id,secret,scope} */ + if ($creds instanceof Config) { + foreach ($creds as $key => $val) { + $config->$app->$key = $val; + } + } else { + $config->$app = $creds; + } + } + }); diff --git a/app/bootstrap/github.php b/app/bootstrap/github.php new file mode 100644 index 0000000..55a49a7 --- /dev/null +++ b/app/bootstrap/github.php @@ -0,0 +1,17 @@ +share(Github\API::class) + ->delegate(Github\API::class, function() use($injector) { + return new Github\API( + $injector->make(Config::class)->github + ,new Github\Storage\Session("gh-tokens") + #,new Github\Storage\Memcache("gh-cache") + ); + }); + diff --git a/app/bootstrap/plates.php b/app/bootstrap/plates.php new file mode 100644 index 0000000..0b14222 --- /dev/null +++ b/app/bootstrap/plates.php @@ -0,0 +1,46 @@ +share(Plates\Engine::class) + ->define(Plates\Engine::class, [ + ":directory" => __DIR__."/../views", + ":fileExtension" => "phtml" + ]) + ->prepare(Plates\Engine::class, function(Plates\Engine $view) use($injector) { + $view->addData([ + "config" => $injector->make(Config::class), + "baseUrl" => $injector->make(BaseUrl::class), + "request" => $injector->make(Request::class), + "response" => $injector->make(Response::class), + ]); + $view->registerFunction("shorten", function($text) { + if (strlen($text) < 78) { + return $text; + } + return current(explode("\n", wordwrap($text)))."…"; + }); + $view->registerFunction("utc", function($d) { + return date_create($d)->setTimeZone(new \DateTimeZone("UTC")); + }); + $view->registerFunction("md", function($string, $file = false) { + if ($file) { + $md = \MarkdownDocument::createFromStream($string); + } else { + $md = \MarkdownDocument::createFromString($string); + } + $md->compile(\MarkdownDocument::AUTOLINK); + return $md->getHtml(); + }); + }); + diff --git a/app/bootstrap/web.php b/app/bootstrap/web.php new file mode 100644 index 0000000..2ad975f --- /dev/null +++ b/app/bootstrap/web.php @@ -0,0 +1,75 @@ +share(Request::class); +$injector->share(Response::class); + +$injector->share(RouteCollector::class) + ->prepare(RouteCollector::class, function($routes) use($injector) { + $routes->addRoute("GET", "/reset", function(array $args = null) use($injector) { + $injector->make(Session::class)->reset()->regenerateId(); + $injector->make(Web::class)->redirect($injector->make(BaseUrl::class)); + }); + $routes->addRoute("GET", "/session", function(array $args = null) use($injector) { + $session = $injector->make(Session::class); + $response = $injector->make(Response::class); + $response->setContentType("text/plain"); + ob_start($response); + var_dump($_SESSION, $session); + }); + $routes->addRoute("GET", "/info", function(array $args = null) { + phpinfo(); + }); + + foreach (parse_ini_file(__DIR__."/../routes.ini", true) as $controller => $definition) { + $factory = function(array $args = null) use($injector, $controller) { + $handler = $injector->make("app\\Controller\\$controller"); + $handler($args); + }; + foreach ($definition as $method => $locations) { + foreach ($locations as $location) { + $routes->addRoute($method, $location, $factory); + } + } + } + }) + ->alias(RouteParser::class, RouteParser\Std::class) + ->alias(DataGenerator::class, DataGenerator\GroupCountBased::class); + +$injector->share(Dispatcher::class) + ->alias(Dispatcher::class, Dispatcher\GroupCountBased::class) + ->delegate(Dispatcher\GroupCountBased::class, function($class, Injector $injector) { + return new $class($injector->make(RouteCollector::class)->getData()); + }); + +$injector->prepare(Controller::class, function(Controller $controller, Injector $injector) { + if (method_exists($controller, "setSession")) { + $controller->setSession($injector->make(Session::class)); + } +}); + +$injector->share(Session::class) + ->prepare(Session::class, function(Session $session, Injector $injector) { + if (isset($session->current) && (!isset($session->previous) || strcmp($session->current, $session->previous))) { + $session->previous = $session->current; + $session->current = $injector->make(Request::class)->getRequestUrl(); + } + $session->current = $injector->make(Request::class)->getRequestUrl(); + }); + +$injector->share(BaseUrl::class); +$injector->share(Web::class); diff --git a/app/config.ini b/app/config.ini new file mode 100644 index 0000000..22dd591 --- /dev/null +++ b/app/config.ini @@ -0,0 +1,24 @@ +[production] + +github.hook.url = https://pharext.org/github/hook + +github.fetch.repos.per_page = 10 +github.fetch.hooks.per_page = 100 + +github.storage.token.ttl = 3600 +github.storage.cache.repo.ttl = 3600 +github.storage.cache.repos.ttl = 3600 +github.storage.cache.hooks.ttl = 3600 +github.storage.cache.tags.ttl = 3600 +github.storage.cache.releases.ttl = 3600 + +session.use_cookies = 1 +session.use_only_cookies = 1 +session.use_strict_mode = 1 +session.cookie_httponly = 1 +session.cookie_secure = 1 +session.cache_limiter = private_no_expire +; minutes +session.cache_expire = 0 + +[localhost : production] diff --git a/app/routes.ini b/app/routes.ini new file mode 100644 index 0000000..f9954d8 --- /dev/null +++ b/app/routes.ini @@ -0,0 +1,19 @@ +[Homepage] +GET[] = / + +[Wikipage] +GET[] = /pages/{page} + +[Github\Callback] +GET[] = /github/callback + +[Github\Index] +GET[] = /github + +[Github\Repo] +GET[] = /github/repo/{owner}/{name} +GET[] = /github/repo/{owner}/{name}/{page} + +[Github\Signin] +GET[] = /github/signin + diff --git a/app/views/404.phtml b/app/views/404.phtml new file mode 100644 index 0000000..491df9c --- /dev/null +++ b/app/views/404.phtml @@ -0,0 +1,5 @@ +layout("layout") ?> + + diff --git a/app/views/405.phtml b/app/views/405.phtml new file mode 100644 index 0000000..ec25164 --- /dev/null +++ b/app/views/405.phtml @@ -0,0 +1,5 @@ +layout("layout") ?> + + diff --git a/app/views/alert.phtml b/app/views/alert.phtml new file mode 100644 index 0000000..bd8c0b7 --- /dev/null +++ b/app/views/alert.phtml @@ -0,0 +1,10 @@ + + diff --git a/app/views/github/callback.phtml b/app/views/github/callback.phtml new file mode 100644 index 0000000..f3fdf46 --- /dev/null +++ b/app/views/github/callback.phtml @@ -0,0 +1,5 @@ +layout("layout") ?> + + + fetch("alert") ?> + diff --git a/app/views/github/index.phtml b/app/views/github/index.phtml new file mode 100644 index 0000000..987ea8e --- /dev/null +++ b/app/views/github/index.phtml @@ -0,0 +1,65 @@ +layout("layout") ?> + + + + + fetch("alert") ?> + + + + + + + + + + + + + + "> + + + + + + + +
RepoForkLast PushedHook
+ full_name) ?>"> + e($repo->name) ?> + +
+ e($this->shorten($repo->description)) ?> +
fork) : ?> + + + + " + href="mod("./github/repo/".$repo->full_name) ?>"> + + " + href="mod(["path" => "github/repo", "query" => "hook=del&repo=". urlencode($repo->full_name)]) ?>"> + +
+ + + diff --git a/app/views/github/repo.phtml b/app/views/github/repo.phtml new file mode 100644 index 0000000..4477e8a --- /dev/null +++ b/app/views/github/repo.phtml @@ -0,0 +1,156 @@ +layout("layout") ?> + + + + + fetch("alert") ?> + + +
+
+
+

e($repo->description) ?>

+
+
+
+
+
+ +
+ + watchers_count ?> Watchers + +
+
+ +
+ + stargazers_count ?> Stars + +
+
+ +
+ + forks_count ?> Forks + +
+
+
+
+ +
+
+
+
+

Has a config*.m4 file?

+
+
+

YES

+
+
+ +
+
+

Has a package*.xml file?

+
+
+

NO

+
+
+ +
+
+

Has a pharext_package.php file?

+
+
+

YES

+
+
+ +
+
+

Is the pharext hook enabled?

+
+
+

NO

+
+
+
+ +
+
+
+ +
+
+ +
+
+
+
+ +

Release History

+ +tags as $name => $v) : ?> +
"> +
+

+ release)) : ?> + e($v->tag->name) ?> + Tag + + e($v->release->name) ?> + Release + +

+
+
+ release)) : ?> +

md($v->release->body) ?>

+ +
    +
  • + + e($v->tag->name) ?> +
  • + + release)) : ?> +
  • + + e($v->release->target_commitish) ?> +
  • +
  • + + + + + +
+
+
+ + +
+ + diff --git a/app/views/layout.phtml b/app/views/layout.phtml new file mode 100644 index 0000000..a321f80 --- /dev/null +++ b/app/views/layout.phtml @@ -0,0 +1,38 @@ + + + + + + <?php if (!empty($title)) : ?> + <?= $this->e($title) ?> + <?php endif; ?> + PHARext + + + + "> + "> + + + +
"> + + section("content") ?> + +
+ + + + + diff --git a/app/views/navbar.phtml b/app/views/navbar.phtml new file mode 100644 index 0000000..f2c9571 --- /dev/null +++ b/app/views/navbar.phtml @@ -0,0 +1,82 @@ + + \ No newline at end of file diff --git a/app/views/pages/index.phtml b/app/views/pages/index.phtml new file mode 100644 index 0000000..af4bed4 --- /dev/null +++ b/app/views/pages/index.phtml @@ -0,0 +1,60 @@ +layout("layout", ["container" => false, "location" => null]) ?> + +
+
+

+ PHARext + PHP extension packaging +

+
+
+

+ What? +

+

+ Distribute your PHP extensions as self-installing PHAR archives.1) +

+

" class="btn btn-primary btn-lg btn-block"> + Learn more about pharext! + +

+
+
+

+ Services +

+

+ Manage automatic pharext builds of your PHP extensions. +

+ " class="btn btn-primary btn-lg btn-block"> + Start right with your Github repos! + +
+
+
+
+ +
+ +
+ +
+
+
+

+ 1) + On Unix-like operating systems only. + +

+
+
+
+ diff --git a/app/views/pages/wiki.phtml b/app/views/pages/wiki.phtml new file mode 100644 index 0000000..e0c7056 --- /dev/null +++ b/app/views/pages/wiki.phtml @@ -0,0 +1,7 @@ +layout("layout", ["location" => "pages"]) ?> + + + +md($page, true) ?> diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..573d1d9 --- /dev/null +++ b/composer.json @@ -0,0 +1,27 @@ +{ + "repositories": [{ + "type": "package", + "package": { + "name": "m6w6/pharext.wiki", + "version": "dev-master", + "source": { + "url": "https://github.com/m6w6/pharext.wiki.git", + "type": "git", + "reference": "master" + } + } + }], + "autoload": { + "psr-0": { + "app": "" + } + }, + "require": { + "m6w6/pharext": "~3.0", + "m6w6/merry": "~1.1", + "nikic/fast-route": "0.4.*", + "league/plates": "~3.1", + "rdlowrey/auryn": "dev-master", + "m6w6/pharext.wiki": "dev-master" + } +} diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..9ed2b4d --- /dev/null +++ b/composer.lock @@ -0,0 +1,255 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", + "This file is @generated automatically" + ], + "hash": "c13d01568dea02e2afe4b9060478178f", + "packages": [ + { + "name": "league/plates", + "version": "3.1.0", + "source": { + "type": "git", + "url": "https://github.com/thephpleague/plates.git", + "reference": "c39d8fbcbeb07a0948d5e1cf8d78c6e1ef3e81a8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/thephpleague/plates/zipball/c39d8fbcbeb07a0948d5e1cf8d78c6e1ef3e81a8", + "reference": "c39d8fbcbeb07a0948d5e1cf8d78c6e1ef3e81a8", + "shasum": "" + }, + "require-dev": { + "mikey179/vfsstream": "~1.4.0", + "phpunit/phpunit": "~4.0", + "squizlabs/php_codesniffer": "~1.5" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0-dev" + } + }, + "autoload": { + "psr-4": { + "League\\Plates\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Reinink", + "email": "jonathan@reinink.ca", + "role": "Developer" + } + ], + "description": "Plates, the native PHP template system that's fast, easy to use and easy to extend.", + "homepage": "http://platesphp.com", + "keywords": [ + "league", + "package", + "templates", + "templating", + "views" + ], + "time": "2014-10-21 12:06:12" + }, + { + "name": "m6w6/merry", + "version": "v1.1.0", + "source": { + "type": "git", + "url": "https://github.com/m6w6/merry.git", + "reference": "079ca547531915b0ba0cb6829a89d9c7d4b93408" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/m6w6/merry/zipball/079ca547531915b0ba0cb6829a89d9c7d4b93408", + "reference": "079ca547531915b0ba0cb6829a89d9c7d4b93408", + "shasum": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "merry\\": "lib/merry" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "authors": [ + { + "name": "Michael Wallner", + "email": "mike@php.net" + } + ], + "description": "Merry container and configuration", + "homepage": "http://github.com/m6w6/merry", + "keywords": [ + "config", + "configuration", + "container", + "merry" + ], + "time": "2015-03-12 13:49:46" + }, + { + "name": "m6w6/pharext", + "version": "v3.0.1", + "source": { + "type": "git", + "url": "https://github.com/m6w6/pharext.git", + "reference": "3dcc326aa922ce4b70d07049572c6f15a744869c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/m6w6/pharext/zipball/3dcc326aa922ce4b70d07049572c6f15a744869c", + "reference": "3dcc326aa922ce4b70d07049572c6f15a744869c", + "shasum": "" + }, + "bin": [ + "bin/pharext", + "bin/pharext.pubkey" + ], + "type": "project", + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-2-Clause" + ], + "description": "Package PHP extensions as self-installing PHARs", + "keywords": [ + "ext", + "extension", + "install", + "package", + "phar" + ], + "time": "2015-04-08 10:17:40" + }, + { + "name": "m6w6/pharext.wiki", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/m6w6/pharext.wiki.git", + "reference": "master" + }, + "type": "library", + "time": "2015-04-30 17:39:40" + }, + { + "name": "nikic/fast-route", + "version": "v0.4.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/FastRoute.git", + "reference": "f26a8f7788f25c0e3e9b1579d38d7ccab2755320" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/FastRoute/zipball/f26a8f7788f25c0e3e9b1579d38d7ccab2755320", + "reference": "f26a8f7788f25c0e3e9b1579d38d7ccab2755320", + "shasum": "" + }, + "require": { + "php": ">=5.4.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "FastRoute\\": "src/" + }, + "files": [ + "src/functions.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov", + "email": "nikic@php.net" + } + ], + "description": "Fast request router for PHP", + "keywords": [ + "router", + "routing" + ], + "time": "2015-02-26 15:33:07" + }, + { + "name": "rdlowrey/auryn", + "version": "dev-master", + "source": { + "type": "git", + "url": "https://github.com/rdlowrey/Auryn.git", + "reference": "500b08ec9942eb8b647b128fa13942d45754e3d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rdlowrey/Auryn/zipball/500b08ec9942eb8b647b128fa13942d45754e3d4", + "reference": "500b08ec9942eb8b647b128fa13942d45754e3d4", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Auryn\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Dan Ackroyd", + "email": "Danack@basereality.com", + "homepage": "http://www.basereality.com", + "role": "Developer" + }, + { + "name": "Daniel Lowrey", + "email": "rdlowrey@gmail.com", + "role": "Creator / Main Developer" + }, + { + "name": "Levi Morrison", + "email": "levim@php.net", + "homepage": "http://morrisonlevi.github.com/", + "role": "Developer" + } + ], + "description": "Auryn is a dependency injector for bootstrapping object-oriented PHP applications.", + "homepage": "https://github.com/rdlowrey/Auryn", + "keywords": [ + "dependency injection", + "dic", + "ioc" + ], + "time": "2015-03-12 19:52:27" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "stable", + "stability-flags": { + "rdlowrey/auryn": 20, + "m6w6/pharext.wiki": 20 + }, + "prefer-stable": false, + "prefer-lowest": false, + "platform": [], + "platform-dev": [] +} diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..affb2a0 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,6 @@ +RewriteEngine On +RewriteCond %{REQUEST_FILENAME} -f [OR] +RewriteCond %{REQUEST_FILENAME} -d [OR] +RewriteCond %{REQUEST_FILENAME} -l +RewriteRule ^ - [L] +RewriteRule ^ index.php [L] diff --git a/public/index.css b/public/index.css new file mode 100644 index 0000000..64b1821 --- /dev/null +++ b/public/index.css @@ -0,0 +1,24 @@ +.footer { + position: fixed; + bottom: 0px; + width: 100%; + height: 60px; + background-color: #F5F5F5; +} +body { + margin-bottom: 60px; +} +.footer > .container { + padding: 15px; + text-align: center; +} + +pre { + display: inline-block; + background-color: #333; + color: #ccc; +} + +h4 { + margin-top: 20px; +} \ No newline at end of file diff --git a/public/index.php b/public/index.php new file mode 100644 index 0000000..8d0bf63 --- /dev/null +++ b/public/index.php @@ -0,0 +1,7 @@ +execute(Web::class); diff --git a/public/octicons/LICENSE.txt b/public/octicons/LICENSE.txt new file mode 100644 index 0000000..69aa0d5 --- /dev/null +++ b/public/octicons/LICENSE.txt @@ -0,0 +1,9 @@ +(c) 2012-2015 GitHub + +When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos) + +Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL) +Applies to all font files + +Code License: MIT (http://choosealicense.com/licenses/mit/) +Applies to all other files diff --git a/public/octicons/README.md b/public/octicons/README.md new file mode 100644 index 0000000..1007073 --- /dev/null +++ b/public/octicons/README.md @@ -0,0 +1 @@ +If you intend to install Octicons locally, install `octicons-local.ttf`. It should appear as “github-octicons” in your font list. It is specially designed not to conflict with GitHub's web fonts. diff --git a/public/octicons/octicons-local.ttf b/public/octicons/octicons-local.ttf new file mode 100644 index 0000000..9ed9c1a Binary files /dev/null and b/public/octicons/octicons-local.ttf differ diff --git a/public/octicons/octicons.css b/public/octicons/octicons.css new file mode 100644 index 0000000..9b86765 --- /dev/null +++ b/public/octicons/octicons.css @@ -0,0 +1,236 @@ +@font-face { + font-family: 'octicons'; + src: url('octicons.eot?#iefix') format('embedded-opentype'), + url('octicons.woff') format('woff'), + url('octicons.ttf') format('truetype'), + url('octicons.svg#octicons') format('svg'); + font-weight: normal; + font-style: normal; +} + +/* + +.octicon is optimized for 16px. +.mega-octicon is optimized for 32px but can be used larger. + +*/ +.octicon, .mega-octicon { + font: normal normal normal 16px/1 octicons; + display: inline-block; + text-decoration: none; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.mega-octicon { font-size: 32px; } + +.octicon-alert:before { content: '\f02d'} /*  */ +.octicon-alignment-align:before { content: '\f08a'} /*  */ +.octicon-alignment-aligned-to:before { content: '\f08e'} /*  */ +.octicon-alignment-unalign:before { content: '\f08b'} /*  */ +.octicon-arrow-down:before { content: '\f03f'} /*  */ +.octicon-arrow-left:before { content: '\f040'} /*  */ +.octicon-arrow-right:before { content: '\f03e'} /*  */ +.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ +.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ +.octicon-arrow-small-right:before { content: '\f071'} /*  */ +.octicon-arrow-small-up:before { content: '\f09f'} /*  */ +.octicon-arrow-up:before { content: '\f03d'} /*  */ +.octicon-beer:before { content: '\f069'} /*  */ +.octicon-book:before { content: '\f007'} /*  */ +.octicon-bookmark:before { content: '\f07b'} /*  */ +.octicon-briefcase:before { content: '\f0d3'} /*  */ +.octicon-broadcast:before { content: '\f048'} /*  */ +.octicon-browser:before { content: '\f0c5'} /*  */ +.octicon-bug:before { content: '\f091'} /*  */ +.octicon-calendar:before { content: '\f068'} /*  */ +.octicon-check:before { content: '\f03a'} /*  */ +.octicon-checklist:before { content: '\f076'} /*  */ +.octicon-chevron-down:before { content: '\f0a3'} /*  */ +.octicon-chevron-left:before { content: '\f0a4'} /*  */ +.octicon-chevron-right:before { content: '\f078'} /*  */ +.octicon-chevron-up:before { content: '\f0a2'} /*  */ +.octicon-circle-slash:before { content: '\f084'} /*  */ +.octicon-circuit-board:before { content: '\f0d6'} /*  */ +.octicon-clippy:before { content: '\f035'} /*  */ +.octicon-clock:before { content: '\f046'} /*  */ +.octicon-cloud-download:before { content: '\f00b'} /*  */ +.octicon-cloud-upload:before { content: '\f00c'} /*  */ +.octicon-code:before { content: '\f05f'} /*  */ +.octicon-color-mode:before { content: '\f065'} /*  */ +.octicon-comment-add:before, +.octicon-comment:before { content: '\f02b'} /*  */ +.octicon-comment-discussion:before { content: '\f04f'} /*  */ +.octicon-credit-card:before { content: '\f045'} /*  */ +.octicon-dash:before { content: '\f0ca'} /*  */ +.octicon-dashboard:before { content: '\f07d'} /*  */ +.octicon-database:before { content: '\f096'} /*  */ +.octicon-device-camera:before { content: '\f056'} /*  */ +.octicon-device-camera-video:before { content: '\f057'} /*  */ +.octicon-device-desktop:before { content: '\f27c'} /*  */ +.octicon-device-mobile:before { content: '\f038'} /*  */ +.octicon-diff:before { content: '\f04d'} /*  */ +.octicon-diff-added:before { content: '\f06b'} /*  */ +.octicon-diff-ignored:before { content: '\f099'} /*  */ +.octicon-diff-modified:before { content: '\f06d'} /*  */ +.octicon-diff-removed:before { content: '\f06c'} /*  */ +.octicon-diff-renamed:before { content: '\f06e'} /*  */ +.octicon-ellipsis:before { content: '\f09a'} /*  */ +.octicon-eye-unwatch:before, +.octicon-eye-watch:before, +.octicon-eye:before { content: '\f04e'} /*  */ +.octicon-file-binary:before { content: '\f094'} /*  */ +.octicon-file-code:before { content: '\f010'} /*  */ +.octicon-file-directory:before { content: '\f016'} /*  */ +.octicon-file-media:before { content: '\f012'} /*  */ +.octicon-file-pdf:before { content: '\f014'} /*  */ +.octicon-file-submodule:before { content: '\f017'} /*  */ +.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ +.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ +.octicon-file-text:before { content: '\f011'} /*  */ +.octicon-file-zip:before { content: '\f013'} /*  */ +.octicon-flame:before { content: '\f0d2'} /*  */ +.octicon-fold:before { content: '\f0cc'} /*  */ +.octicon-gear:before { content: '\f02f'} /*  */ +.octicon-gift:before { content: '\f042'} /*  */ +.octicon-gist:before { content: '\f00e'} /*  */ +.octicon-gist-secret:before { content: '\f08c'} /*  */ +.octicon-git-branch-create:before, +.octicon-git-branch-delete:before, +.octicon-git-branch:before { content: '\f020'} /*  */ +.octicon-git-commit:before { content: '\f01f'} /*  */ +.octicon-git-compare:before { content: '\f0ac'} /*  */ +.octicon-git-merge:before { content: '\f023'} /*  */ +.octicon-git-pull-request-abandoned:before, +.octicon-git-pull-request:before { content: '\f009'} /*  */ +.octicon-globe:before { content: '\f0b6'} /*  */ +.octicon-graph:before { content: '\f043'} /*  */ +.octicon-heart:before { content: '\2665'} /* ♥ */ +.octicon-history:before { content: '\f07e'} /*  */ +.octicon-home:before { content: '\f08d'} /*  */ +.octicon-horizontal-rule:before { content: '\f070'} /*  */ +.octicon-hourglass:before { content: '\f09e'} /*  */ +.octicon-hubot:before { content: '\f09d'} /*  */ +.octicon-inbox:before { content: '\f0cf'} /*  */ +.octicon-info:before { content: '\f059'} /*  */ +.octicon-issue-closed:before { content: '\f028'} /*  */ +.octicon-issue-opened:before { content: '\f026'} /*  */ +.octicon-issue-reopened:before { content: '\f027'} /*  */ +.octicon-jersey:before { content: '\f019'} /*  */ +.octicon-jump-down:before { content: '\f072'} /*  */ +.octicon-jump-left:before { content: '\f0a5'} /*  */ +.octicon-jump-right:before { content: '\f0a6'} /*  */ +.octicon-jump-up:before { content: '\f073'} /*  */ +.octicon-key:before { content: '\f049'} /*  */ +.octicon-keyboard:before { content: '\f00d'} /*  */ +.octicon-law:before { content: '\f0d8'} /*  */ +.octicon-light-bulb:before { content: '\f000'} /*  */ +.octicon-link:before { content: '\f05c'} /*  */ +.octicon-link-external:before { content: '\f07f'} /*  */ +.octicon-list-ordered:before { content: '\f062'} /*  */ +.octicon-list-unordered:before { content: '\f061'} /*  */ +.octicon-location:before { content: '\f060'} /*  */ +.octicon-gist-private:before, +.octicon-mirror-private:before, +.octicon-git-fork-private:before, +.octicon-lock:before { content: '\f06a'} /*  */ +.octicon-logo-github:before { content: '\f092'} /*  */ +.octicon-mail:before { content: '\f03b'} /*  */ +.octicon-mail-read:before { content: '\f03c'} /*  */ +.octicon-mail-reply:before { content: '\f051'} /*  */ +.octicon-mark-github:before { content: '\f00a'} /*  */ +.octicon-markdown:before { content: '\f0c9'} /*  */ +.octicon-megaphone:before { content: '\f077'} /*  */ +.octicon-mention:before { content: '\f0be'} /*  */ +.octicon-microscope:before { content: '\f089'} /*  */ +.octicon-milestone:before { content: '\f075'} /*  */ +.octicon-mirror-public:before, +.octicon-mirror:before { content: '\f024'} /*  */ +.octicon-mortar-board:before { content: '\f0d7'} /*  */ +.octicon-move-down:before { content: '\f0a8'} /*  */ +.octicon-move-left:before { content: '\f074'} /*  */ +.octicon-move-right:before { content: '\f0a9'} /*  */ +.octicon-move-up:before { content: '\f0a7'} /*  */ +.octicon-mute:before { content: '\f080'} /*  */ +.octicon-no-newline:before { content: '\f09c'} /*  */ +.octicon-octoface:before { content: '\f008'} /*  */ +.octicon-organization:before { content: '\f037'} /*  */ +.octicon-package:before { content: '\f0c4'} /*  */ +.octicon-paintcan:before { content: '\f0d1'} /*  */ +.octicon-pencil:before { content: '\f058'} /*  */ +.octicon-person-add:before, +.octicon-person-follow:before, +.octicon-person:before { content: '\f018'} /*  */ +.octicon-pin:before { content: '\f041'} /*  */ +.octicon-playback-fast-forward:before { content: '\f0bd'} /*  */ +.octicon-playback-pause:before { content: '\f0bb'} /*  */ +.octicon-playback-play:before { content: '\f0bf'} /*  */ +.octicon-playback-rewind:before { content: '\f0bc'} /*  */ +.octicon-plug:before { content: '\f0d4'} /*  */ +.octicon-repo-create:before, +.octicon-gist-new:before, +.octicon-file-directory-create:before, +.octicon-file-add:before, +.octicon-plus:before { content: '\f05d'} /*  */ +.octicon-podium:before { content: '\f0af'} /*  */ +.octicon-primitive-dot:before { content: '\f052'} /*  */ +.octicon-primitive-square:before { content: '\f053'} /*  */ +.octicon-pulse:before { content: '\f085'} /*  */ +.octicon-puzzle:before { content: '\f0c0'} /*  */ +.octicon-question:before { content: '\f02c'} /*  */ +.octicon-quote:before { content: '\f063'} /*  */ +.octicon-radio-tower:before { content: '\f030'} /*  */ +.octicon-repo-delete:before, +.octicon-repo:before { content: '\f001'} /*  */ +.octicon-repo-clone:before { content: '\f04c'} /*  */ +.octicon-repo-force-push:before { content: '\f04a'} /*  */ +.octicon-gist-fork:before, +.octicon-repo-forked:before { content: '\f002'} /*  */ +.octicon-repo-pull:before { content: '\f006'} /*  */ +.octicon-repo-push:before { content: '\f005'} /*  */ +.octicon-rocket:before { content: '\f033'} /*  */ +.octicon-rss:before { content: '\f034'} /*  */ +.octicon-ruby:before { content: '\f047'} /*  */ +.octicon-screen-full:before { content: '\f066'} /*  */ +.octicon-screen-normal:before { content: '\f067'} /*  */ +.octicon-search-save:before, +.octicon-search:before { content: '\f02e'} /*  */ +.octicon-server:before { content: '\f097'} /*  */ +.octicon-settings:before { content: '\f07c'} /*  */ +.octicon-log-in:before, +.octicon-sign-in:before { content: '\f036'} /*  */ +.octicon-log-out:before, +.octicon-sign-out:before { content: '\f032'} /*  */ +.octicon-split:before { content: '\f0c6'} /*  */ +.octicon-squirrel:before { content: '\f0b2'} /*  */ +.octicon-star-add:before, +.octicon-star-delete:before, +.octicon-star:before { content: '\f02a'} /*  */ +.octicon-steps:before { content: '\f0c7'} /*  */ +.octicon-stop:before { content: '\f08f'} /*  */ +.octicon-repo-sync:before, +.octicon-sync:before { content: '\f087'} /*  */ +.octicon-tag-remove:before, +.octicon-tag-add:before, +.octicon-tag:before { content: '\f015'} /*  */ +.octicon-telescope:before { content: '\f088'} /*  */ +.octicon-terminal:before { content: '\f0c8'} /*  */ +.octicon-three-bars:before { content: '\f05e'} /*  */ +.octicon-thumbsdown:before { content: '\f0db'} /*  */ +.octicon-thumbsup:before { content: '\f0da'} /*  */ +.octicon-tools:before { content: '\f031'} /*  */ +.octicon-trashcan:before { content: '\f0d0'} /*  */ +.octicon-triangle-down:before { content: '\f05b'} /*  */ +.octicon-triangle-left:before { content: '\f044'} /*  */ +.octicon-triangle-right:before { content: '\f05a'} /*  */ +.octicon-triangle-up:before { content: '\f0aa'} /*  */ +.octicon-unfold:before { content: '\f039'} /*  */ +.octicon-unmute:before { content: '\f0ba'} /*  */ +.octicon-versions:before { content: '\f064'} /*  */ +.octicon-remove-close:before, +.octicon-x:before { content: '\f081'} /*  */ +.octicon-zap:before { content: '\26A1'} /* ⚡ */ diff --git a/public/octicons/octicons.eot b/public/octicons/octicons.eot new file mode 100644 index 0000000..842f631 Binary files /dev/null and b/public/octicons/octicons.eot differ diff --git a/public/octicons/octicons.less b/public/octicons/octicons.less new file mode 100644 index 0000000..f48dca8 --- /dev/null +++ b/public/octicons/octicons.less @@ -0,0 +1,235 @@ +@octicons-font-path: "."; +@octicons-version: "675c3211eac589bbda193fdb306ce567a2c4569f"; + +@font-face { + font-family: 'octicons'; + src: ~"url('@{octicons-font-path}/octicons.eot?#iefix&v=@{octicons-version}') format('embedded-opentype')", + ~"url('@{octicons-font-path}/octicons.woff?v=@{octicons-version}') format('woff')", + ~"url('@{octicons-font-path}/octicons.ttf?v=@{octicons-version}') format('truetype')", + ~"url('@{octicons-font-path}/octicons.svg?v=@{octicons-version}#octicons') format('svg')"; + font-weight: normal; + font-style: normal; +} + +// .octicon is optimized for 16px. +// .mega-octicon is optimized for 32px but can be used larger. +.octicon, .mega-octicon { + font: normal normal normal 16px/1 octicons; + display: inline-block; + text-decoration: none; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.mega-octicon { font-size: 32px; } + +.octicon-alert:before { content: '\f02d'} /*  */ +.octicon-alignment-align:before { content: '\f08a'} /*  */ +.octicon-alignment-aligned-to:before { content: '\f08e'} /*  */ +.octicon-alignment-unalign:before { content: '\f08b'} /*  */ +.octicon-arrow-down:before { content: '\f03f'} /*  */ +.octicon-arrow-left:before { content: '\f040'} /*  */ +.octicon-arrow-right:before { content: '\f03e'} /*  */ +.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ +.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ +.octicon-arrow-small-right:before { content: '\f071'} /*  */ +.octicon-arrow-small-up:before { content: '\f09f'} /*  */ +.octicon-arrow-up:before { content: '\f03d'} /*  */ +.octicon-beer:before { content: '\f069'} /*  */ +.octicon-book:before { content: '\f007'} /*  */ +.octicon-bookmark:before { content: '\f07b'} /*  */ +.octicon-briefcase:before { content: '\f0d3'} /*  */ +.octicon-broadcast:before { content: '\f048'} /*  */ +.octicon-browser:before { content: '\f0c5'} /*  */ +.octicon-bug:before { content: '\f091'} /*  */ +.octicon-calendar:before { content: '\f068'} /*  */ +.octicon-check:before { content: '\f03a'} /*  */ +.octicon-checklist:before { content: '\f076'} /*  */ +.octicon-chevron-down:before { content: '\f0a3'} /*  */ +.octicon-chevron-left:before { content: '\f0a4'} /*  */ +.octicon-chevron-right:before { content: '\f078'} /*  */ +.octicon-chevron-up:before { content: '\f0a2'} /*  */ +.octicon-circle-slash:before { content: '\f084'} /*  */ +.octicon-circuit-board:before { content: '\f0d6'} /*  */ +.octicon-clippy:before { content: '\f035'} /*  */ +.octicon-clock:before { content: '\f046'} /*  */ +.octicon-cloud-download:before { content: '\f00b'} /*  */ +.octicon-cloud-upload:before { content: '\f00c'} /*  */ +.octicon-code:before { content: '\f05f'} /*  */ +.octicon-color-mode:before { content: '\f065'} /*  */ +.octicon-comment-add:before, +.octicon-comment:before { content: '\f02b'} /*  */ +.octicon-comment-discussion:before { content: '\f04f'} /*  */ +.octicon-credit-card:before { content: '\f045'} /*  */ +.octicon-dash:before { content: '\f0ca'} /*  */ +.octicon-dashboard:before { content: '\f07d'} /*  */ +.octicon-database:before { content: '\f096'} /*  */ +.octicon-device-camera:before { content: '\f056'} /*  */ +.octicon-device-camera-video:before { content: '\f057'} /*  */ +.octicon-device-desktop:before { content: '\f27c'} /*  */ +.octicon-device-mobile:before { content: '\f038'} /*  */ +.octicon-diff:before { content: '\f04d'} /*  */ +.octicon-diff-added:before { content: '\f06b'} /*  */ +.octicon-diff-ignored:before { content: '\f099'} /*  */ +.octicon-diff-modified:before { content: '\f06d'} /*  */ +.octicon-diff-removed:before { content: '\f06c'} /*  */ +.octicon-diff-renamed:before { content: '\f06e'} /*  */ +.octicon-ellipsis:before { content: '\f09a'} /*  */ +.octicon-eye-unwatch:before, +.octicon-eye-watch:before, +.octicon-eye:before { content: '\f04e'} /*  */ +.octicon-file-binary:before { content: '\f094'} /*  */ +.octicon-file-code:before { content: '\f010'} /*  */ +.octicon-file-directory:before { content: '\f016'} /*  */ +.octicon-file-media:before { content: '\f012'} /*  */ +.octicon-file-pdf:before { content: '\f014'} /*  */ +.octicon-file-submodule:before { content: '\f017'} /*  */ +.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ +.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ +.octicon-file-text:before { content: '\f011'} /*  */ +.octicon-file-zip:before { content: '\f013'} /*  */ +.octicon-flame:before { content: '\f0d2'} /*  */ +.octicon-fold:before { content: '\f0cc'} /*  */ +.octicon-gear:before { content: '\f02f'} /*  */ +.octicon-gift:before { content: '\f042'} /*  */ +.octicon-gist:before { content: '\f00e'} /*  */ +.octicon-gist-secret:before { content: '\f08c'} /*  */ +.octicon-git-branch-create:before, +.octicon-git-branch-delete:before, +.octicon-git-branch:before { content: '\f020'} /*  */ +.octicon-git-commit:before { content: '\f01f'} /*  */ +.octicon-git-compare:before { content: '\f0ac'} /*  */ +.octicon-git-merge:before { content: '\f023'} /*  */ +.octicon-git-pull-request-abandoned:before, +.octicon-git-pull-request:before { content: '\f009'} /*  */ +.octicon-globe:before { content: '\f0b6'} /*  */ +.octicon-graph:before { content: '\f043'} /*  */ +.octicon-heart:before { content: '\2665'} /* ♥ */ +.octicon-history:before { content: '\f07e'} /*  */ +.octicon-home:before { content: '\f08d'} /*  */ +.octicon-horizontal-rule:before { content: '\f070'} /*  */ +.octicon-hourglass:before { content: '\f09e'} /*  */ +.octicon-hubot:before { content: '\f09d'} /*  */ +.octicon-inbox:before { content: '\f0cf'} /*  */ +.octicon-info:before { content: '\f059'} /*  */ +.octicon-issue-closed:before { content: '\f028'} /*  */ +.octicon-issue-opened:before { content: '\f026'} /*  */ +.octicon-issue-reopened:before { content: '\f027'} /*  */ +.octicon-jersey:before { content: '\f019'} /*  */ +.octicon-jump-down:before { content: '\f072'} /*  */ +.octicon-jump-left:before { content: '\f0a5'} /*  */ +.octicon-jump-right:before { content: '\f0a6'} /*  */ +.octicon-jump-up:before { content: '\f073'} /*  */ +.octicon-key:before { content: '\f049'} /*  */ +.octicon-keyboard:before { content: '\f00d'} /*  */ +.octicon-law:before { content: '\f0d8'} /*  */ +.octicon-light-bulb:before { content: '\f000'} /*  */ +.octicon-link:before { content: '\f05c'} /*  */ +.octicon-link-external:before { content: '\f07f'} /*  */ +.octicon-list-ordered:before { content: '\f062'} /*  */ +.octicon-list-unordered:before { content: '\f061'} /*  */ +.octicon-location:before { content: '\f060'} /*  */ +.octicon-gist-private:before, +.octicon-mirror-private:before, +.octicon-git-fork-private:before, +.octicon-lock:before { content: '\f06a'} /*  */ +.octicon-logo-github:before { content: '\f092'} /*  */ +.octicon-mail:before { content: '\f03b'} /*  */ +.octicon-mail-read:before { content: '\f03c'} /*  */ +.octicon-mail-reply:before { content: '\f051'} /*  */ +.octicon-mark-github:before { content: '\f00a'} /*  */ +.octicon-markdown:before { content: '\f0c9'} /*  */ +.octicon-megaphone:before { content: '\f077'} /*  */ +.octicon-mention:before { content: '\f0be'} /*  */ +.octicon-microscope:before { content: '\f089'} /*  */ +.octicon-milestone:before { content: '\f075'} /*  */ +.octicon-mirror-public:before, +.octicon-mirror:before { content: '\f024'} /*  */ +.octicon-mortar-board:before { content: '\f0d7'} /*  */ +.octicon-move-down:before { content: '\f0a8'} /*  */ +.octicon-move-left:before { content: '\f074'} /*  */ +.octicon-move-right:before { content: '\f0a9'} /*  */ +.octicon-move-up:before { content: '\f0a7'} /*  */ +.octicon-mute:before { content: '\f080'} /*  */ +.octicon-no-newline:before { content: '\f09c'} /*  */ +.octicon-octoface:before { content: '\f008'} /*  */ +.octicon-organization:before { content: '\f037'} /*  */ +.octicon-package:before { content: '\f0c4'} /*  */ +.octicon-paintcan:before { content: '\f0d1'} /*  */ +.octicon-pencil:before { content: '\f058'} /*  */ +.octicon-person-add:before, +.octicon-person-follow:before, +.octicon-person:before { content: '\f018'} /*  */ +.octicon-pin:before { content: '\f041'} /*  */ +.octicon-playback-fast-forward:before { content: '\f0bd'} /*  */ +.octicon-playback-pause:before { content: '\f0bb'} /*  */ +.octicon-playback-play:before { content: '\f0bf'} /*  */ +.octicon-playback-rewind:before { content: '\f0bc'} /*  */ +.octicon-plug:before { content: '\f0d4'} /*  */ +.octicon-repo-create:before, +.octicon-gist-new:before, +.octicon-file-directory-create:before, +.octicon-file-add:before, +.octicon-plus:before { content: '\f05d'} /*  */ +.octicon-podium:before { content: '\f0af'} /*  */ +.octicon-primitive-dot:before { content: '\f052'} /*  */ +.octicon-primitive-square:before { content: '\f053'} /*  */ +.octicon-pulse:before { content: '\f085'} /*  */ +.octicon-puzzle:before { content: '\f0c0'} /*  */ +.octicon-question:before { content: '\f02c'} /*  */ +.octicon-quote:before { content: '\f063'} /*  */ +.octicon-radio-tower:before { content: '\f030'} /*  */ +.octicon-repo-delete:before, +.octicon-repo:before { content: '\f001'} /*  */ +.octicon-repo-clone:before { content: '\f04c'} /*  */ +.octicon-repo-force-push:before { content: '\f04a'} /*  */ +.octicon-gist-fork:before, +.octicon-repo-forked:before { content: '\f002'} /*  */ +.octicon-repo-pull:before { content: '\f006'} /*  */ +.octicon-repo-push:before { content: '\f005'} /*  */ +.octicon-rocket:before { content: '\f033'} /*  */ +.octicon-rss:before { content: '\f034'} /*  */ +.octicon-ruby:before { content: '\f047'} /*  */ +.octicon-screen-full:before { content: '\f066'} /*  */ +.octicon-screen-normal:before { content: '\f067'} /*  */ +.octicon-search-save:before, +.octicon-search:before { content: '\f02e'} /*  */ +.octicon-server:before { content: '\f097'} /*  */ +.octicon-settings:before { content: '\f07c'} /*  */ +.octicon-log-in:before, +.octicon-sign-in:before { content: '\f036'} /*  */ +.octicon-log-out:before, +.octicon-sign-out:before { content: '\f032'} /*  */ +.octicon-split:before { content: '\f0c6'} /*  */ +.octicon-squirrel:before { content: '\f0b2'} /*  */ +.octicon-star-add:before, +.octicon-star-delete:before, +.octicon-star:before { content: '\f02a'} /*  */ +.octicon-steps:before { content: '\f0c7'} /*  */ +.octicon-stop:before { content: '\f08f'} /*  */ +.octicon-repo-sync:before, +.octicon-sync:before { content: '\f087'} /*  */ +.octicon-tag-remove:before, +.octicon-tag-add:before, +.octicon-tag:before { content: '\f015'} /*  */ +.octicon-telescope:before { content: '\f088'} /*  */ +.octicon-terminal:before { content: '\f0c8'} /*  */ +.octicon-three-bars:before { content: '\f05e'} /*  */ +.octicon-thumbsdown:before { content: '\f0db'} /*  */ +.octicon-thumbsup:before { content: '\f0da'} /*  */ +.octicon-tools:before { content: '\f031'} /*  */ +.octicon-trashcan:before { content: '\f0d0'} /*  */ +.octicon-triangle-down:before { content: '\f05b'} /*  */ +.octicon-triangle-left:before { content: '\f044'} /*  */ +.octicon-triangle-right:before { content: '\f05a'} /*  */ +.octicon-triangle-up:before { content: '\f0aa'} /*  */ +.octicon-unfold:before { content: '\f039'} /*  */ +.octicon-unmute:before { content: '\f0ba'} /*  */ +.octicon-versions:before { content: '\f064'} /*  */ +.octicon-remove-close:before, +.octicon-x:before { content: '\f081'} /*  */ +.octicon-zap:before { content: '\26A1'} /* ⚡ */ diff --git a/public/octicons/octicons.svg b/public/octicons/octicons.svg new file mode 100644 index 0000000..d3116a6 --- /dev/null +++ b/public/octicons/octicons.svg @@ -0,0 +1,200 @@ + + + + +(c) 2012-2015 GitHub + +When using the GitHub logos, be sure to follow the GitHub logo guidelines (https://github.com/logos) + +Font License: SIL OFL 1.1 (http://scripts.sil.org/OFL) +Applies to all font files + +Code License: MIT (http://choosealicense.com/licenses/mit/) +Applies to all other files + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/public/octicons/octicons.ttf b/public/octicons/octicons.ttf new file mode 100644 index 0000000..d9b4c89 Binary files /dev/null and b/public/octicons/octicons.ttf differ diff --git a/public/octicons/octicons.woff b/public/octicons/octicons.woff new file mode 100644 index 0000000..5f6408f Binary files /dev/null and b/public/octicons/octicons.woff differ diff --git a/public/octicons/sprockets-octicons.scss b/public/octicons/sprockets-octicons.scss new file mode 100644 index 0000000..1e8d1ca --- /dev/null +++ b/public/octicons/sprockets-octicons.scss @@ -0,0 +1,232 @@ +@font-face { + font-family: 'octicons'; + src: font-url('octicons.eot?#iefix') format('embedded-opentype'), + font-url('octicons.woff') format('woff'), + font-url('octicons.ttf') format('truetype'), + font-url('octicons.svg#octicons') format('svg'); + font-weight: normal; + font-style: normal; +} + +// .octicon is optimized for 16px. +// .mega-octicon is optimized for 32px but can be used larger. +.octicon, .mega-octicon { + font: normal normal normal 16px/1 octicons; + display: inline-block; + text-decoration: none; + text-rendering: auto; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; +} +.mega-octicon { font-size: 32px; } + +.octicon-alert:before { content: '\f02d'} /*  */ +.octicon-alignment-align:before { content: '\f08a'} /*  */ +.octicon-alignment-aligned-to:before { content: '\f08e'} /*  */ +.octicon-alignment-unalign:before { content: '\f08b'} /*  */ +.octicon-arrow-down:before { content: '\f03f'} /*  */ +.octicon-arrow-left:before { content: '\f040'} /*  */ +.octicon-arrow-right:before { content: '\f03e'} /*  */ +.octicon-arrow-small-down:before { content: '\f0a0'} /*  */ +.octicon-arrow-small-left:before { content: '\f0a1'} /*  */ +.octicon-arrow-small-right:before { content: '\f071'} /*  */ +.octicon-arrow-small-up:before { content: '\f09f'} /*  */ +.octicon-arrow-up:before { content: '\f03d'} /*  */ +.octicon-beer:before { content: '\f069'} /*  */ +.octicon-book:before { content: '\f007'} /*  */ +.octicon-bookmark:before { content: '\f07b'} /*  */ +.octicon-briefcase:before { content: '\f0d3'} /*  */ +.octicon-broadcast:before { content: '\f048'} /*  */ +.octicon-browser:before { content: '\f0c5'} /*  */ +.octicon-bug:before { content: '\f091'} /*  */ +.octicon-calendar:before { content: '\f068'} /*  */ +.octicon-check:before { content: '\f03a'} /*  */ +.octicon-checklist:before { content: '\f076'} /*  */ +.octicon-chevron-down:before { content: '\f0a3'} /*  */ +.octicon-chevron-left:before { content: '\f0a4'} /*  */ +.octicon-chevron-right:before { content: '\f078'} /*  */ +.octicon-chevron-up:before { content: '\f0a2'} /*  */ +.octicon-circle-slash:before { content: '\f084'} /*  */ +.octicon-circuit-board:before { content: '\f0d6'} /*  */ +.octicon-clippy:before { content: '\f035'} /*  */ +.octicon-clock:before { content: '\f046'} /*  */ +.octicon-cloud-download:before { content: '\f00b'} /*  */ +.octicon-cloud-upload:before { content: '\f00c'} /*  */ +.octicon-code:before { content: '\f05f'} /*  */ +.octicon-color-mode:before { content: '\f065'} /*  */ +.octicon-comment-add:before, +.octicon-comment:before { content: '\f02b'} /*  */ +.octicon-comment-discussion:before { content: '\f04f'} /*  */ +.octicon-credit-card:before { content: '\f045'} /*  */ +.octicon-dash:before { content: '\f0ca'} /*  */ +.octicon-dashboard:before { content: '\f07d'} /*  */ +.octicon-database:before { content: '\f096'} /*  */ +.octicon-device-camera:before { content: '\f056'} /*  */ +.octicon-device-camera-video:before { content: '\f057'} /*  */ +.octicon-device-desktop:before { content: '\f27c'} /*  */ +.octicon-device-mobile:before { content: '\f038'} /*  */ +.octicon-diff:before { content: '\f04d'} /*  */ +.octicon-diff-added:before { content: '\f06b'} /*  */ +.octicon-diff-ignored:before { content: '\f099'} /*  */ +.octicon-diff-modified:before { content: '\f06d'} /*  */ +.octicon-diff-removed:before { content: '\f06c'} /*  */ +.octicon-diff-renamed:before { content: '\f06e'} /*  */ +.octicon-ellipsis:before { content: '\f09a'} /*  */ +.octicon-eye-unwatch:before, +.octicon-eye-watch:before, +.octicon-eye:before { content: '\f04e'} /*  */ +.octicon-file-binary:before { content: '\f094'} /*  */ +.octicon-file-code:before { content: '\f010'} /*  */ +.octicon-file-directory:before { content: '\f016'} /*  */ +.octicon-file-media:before { content: '\f012'} /*  */ +.octicon-file-pdf:before { content: '\f014'} /*  */ +.octicon-file-submodule:before { content: '\f017'} /*  */ +.octicon-file-symlink-directory:before { content: '\f0b1'} /*  */ +.octicon-file-symlink-file:before { content: '\f0b0'} /*  */ +.octicon-file-text:before { content: '\f011'} /*  */ +.octicon-file-zip:before { content: '\f013'} /*  */ +.octicon-flame:before { content: '\f0d2'} /*  */ +.octicon-fold:before { content: '\f0cc'} /*  */ +.octicon-gear:before { content: '\f02f'} /*  */ +.octicon-gift:before { content: '\f042'} /*  */ +.octicon-gist:before { content: '\f00e'} /*  */ +.octicon-gist-secret:before { content: '\f08c'} /*  */ +.octicon-git-branch-create:before, +.octicon-git-branch-delete:before, +.octicon-git-branch:before { content: '\f020'} /*  */ +.octicon-git-commit:before { content: '\f01f'} /*  */ +.octicon-git-compare:before { content: '\f0ac'} /*  */ +.octicon-git-merge:before { content: '\f023'} /*  */ +.octicon-git-pull-request-abandoned:before, +.octicon-git-pull-request:before { content: '\f009'} /*  */ +.octicon-globe:before { content: '\f0b6'} /*  */ +.octicon-graph:before { content: '\f043'} /*  */ +.octicon-heart:before { content: '\2665'} /* ♥ */ +.octicon-history:before { content: '\f07e'} /*  */ +.octicon-home:before { content: '\f08d'} /*  */ +.octicon-horizontal-rule:before { content: '\f070'} /*  */ +.octicon-hourglass:before { content: '\f09e'} /*  */ +.octicon-hubot:before { content: '\f09d'} /*  */ +.octicon-inbox:before { content: '\f0cf'} /*  */ +.octicon-info:before { content: '\f059'} /*  */ +.octicon-issue-closed:before { content: '\f028'} /*  */ +.octicon-issue-opened:before { content: '\f026'} /*  */ +.octicon-issue-reopened:before { content: '\f027'} /*  */ +.octicon-jersey:before { content: '\f019'} /*  */ +.octicon-jump-down:before { content: '\f072'} /*  */ +.octicon-jump-left:before { content: '\f0a5'} /*  */ +.octicon-jump-right:before { content: '\f0a6'} /*  */ +.octicon-jump-up:before { content: '\f073'} /*  */ +.octicon-key:before { content: '\f049'} /*  */ +.octicon-keyboard:before { content: '\f00d'} /*  */ +.octicon-law:before { content: '\f0d8'} /*  */ +.octicon-light-bulb:before { content: '\f000'} /*  */ +.octicon-link:before { content: '\f05c'} /*  */ +.octicon-link-external:before { content: '\f07f'} /*  */ +.octicon-list-ordered:before { content: '\f062'} /*  */ +.octicon-list-unordered:before { content: '\f061'} /*  */ +.octicon-location:before { content: '\f060'} /*  */ +.octicon-gist-private:before, +.octicon-mirror-private:before, +.octicon-git-fork-private:before, +.octicon-lock:before { content: '\f06a'} /*  */ +.octicon-logo-github:before { content: '\f092'} /*  */ +.octicon-mail:before { content: '\f03b'} /*  */ +.octicon-mail-read:before { content: '\f03c'} /*  */ +.octicon-mail-reply:before { content: '\f051'} /*  */ +.octicon-mark-github:before { content: '\f00a'} /*  */ +.octicon-markdown:before { content: '\f0c9'} /*  */ +.octicon-megaphone:before { content: '\f077'} /*  */ +.octicon-mention:before { content: '\f0be'} /*  */ +.octicon-microscope:before { content: '\f089'} /*  */ +.octicon-milestone:before { content: '\f075'} /*  */ +.octicon-mirror-public:before, +.octicon-mirror:before { content: '\f024'} /*  */ +.octicon-mortar-board:before { content: '\f0d7'} /*  */ +.octicon-move-down:before { content: '\f0a8'} /*  */ +.octicon-move-left:before { content: '\f074'} /*  */ +.octicon-move-right:before { content: '\f0a9'} /*  */ +.octicon-move-up:before { content: '\f0a7'} /*  */ +.octicon-mute:before { content: '\f080'} /*  */ +.octicon-no-newline:before { content: '\f09c'} /*  */ +.octicon-octoface:before { content: '\f008'} /*  */ +.octicon-organization:before { content: '\f037'} /*  */ +.octicon-package:before { content: '\f0c4'} /*  */ +.octicon-paintcan:before { content: '\f0d1'} /*  */ +.octicon-pencil:before { content: '\f058'} /*  */ +.octicon-person-add:before, +.octicon-person-follow:before, +.octicon-person:before { content: '\f018'} /*  */ +.octicon-pin:before { content: '\f041'} /*  */ +.octicon-playback-fast-forward:before { content: '\f0bd'} /*  */ +.octicon-playback-pause:before { content: '\f0bb'} /*  */ +.octicon-playback-play:before { content: '\f0bf'} /*  */ +.octicon-playback-rewind:before { content: '\f0bc'} /*  */ +.octicon-plug:before { content: '\f0d4'} /*  */ +.octicon-repo-create:before, +.octicon-gist-new:before, +.octicon-file-directory-create:before, +.octicon-file-add:before, +.octicon-plus:before { content: '\f05d'} /*  */ +.octicon-podium:before { content: '\f0af'} /*  */ +.octicon-primitive-dot:before { content: '\f052'} /*  */ +.octicon-primitive-square:before { content: '\f053'} /*  */ +.octicon-pulse:before { content: '\f085'} /*  */ +.octicon-puzzle:before { content: '\f0c0'} /*  */ +.octicon-question:before { content: '\f02c'} /*  */ +.octicon-quote:before { content: '\f063'} /*  */ +.octicon-radio-tower:before { content: '\f030'} /*  */ +.octicon-repo-delete:before, +.octicon-repo:before { content: '\f001'} /*  */ +.octicon-repo-clone:before { content: '\f04c'} /*  */ +.octicon-repo-force-push:before { content: '\f04a'} /*  */ +.octicon-gist-fork:before, +.octicon-repo-forked:before { content: '\f002'} /*  */ +.octicon-repo-pull:before { content: '\f006'} /*  */ +.octicon-repo-push:before { content: '\f005'} /*  */ +.octicon-rocket:before { content: '\f033'} /*  */ +.octicon-rss:before { content: '\f034'} /*  */ +.octicon-ruby:before { content: '\f047'} /*  */ +.octicon-screen-full:before { content: '\f066'} /*  */ +.octicon-screen-normal:before { content: '\f067'} /*  */ +.octicon-search-save:before, +.octicon-search:before { content: '\f02e'} /*  */ +.octicon-server:before { content: '\f097'} /*  */ +.octicon-settings:before { content: '\f07c'} /*  */ +.octicon-log-in:before, +.octicon-sign-in:before { content: '\f036'} /*  */ +.octicon-log-out:before, +.octicon-sign-out:before { content: '\f032'} /*  */ +.octicon-split:before { content: '\f0c6'} /*  */ +.octicon-squirrel:before { content: '\f0b2'} /*  */ +.octicon-star-add:before, +.octicon-star-delete:before, +.octicon-star:before { content: '\f02a'} /*  */ +.octicon-steps:before { content: '\f0c7'} /*  */ +.octicon-stop:before { content: '\f08f'} /*  */ +.octicon-repo-sync:before, +.octicon-sync:before { content: '\f087'} /*  */ +.octicon-tag-remove:before, +.octicon-tag-add:before, +.octicon-tag:before { content: '\f015'} /*  */ +.octicon-telescope:before { content: '\f088'} /*  */ +.octicon-terminal:before { content: '\f0c8'} /*  */ +.octicon-three-bars:before { content: '\f05e'} /*  */ +.octicon-thumbsdown:before { content: '\f0db'} /*  */ +.octicon-thumbsup:before { content: '\f0da'} /*  */ +.octicon-tools:before { content: '\f031'} /*  */ +.octicon-trashcan:before { content: '\f0d0'} /*  */ +.octicon-triangle-down:before { content: '\f05b'} /*  */ +.octicon-triangle-left:before { content: '\f044'} /*  */ +.octicon-triangle-right:before { content: '\f05a'} /*  */ +.octicon-triangle-up:before { content: '\f0aa'} /*  */ +.octicon-unfold:before { content: '\f039'} /*  */ +.octicon-unmute:before { content: '\f0ba'} /*  */ +.octicon-versions:before { content: '\f064'} /*  */ +.octicon-remove-close:before, +.octicon-x:before { content: '\f081'} /*  */ +.octicon-zap:before { content: '\26A1'} /* ⚡ */