├── .bowerrc ├── .gitignore ├── assets ├── scss │ ├── base │ │ ├── _variables.scss │ │ ├── _defaults.scss │ │ └── _mixins.scss │ ├── modules │ │ ├── _code-box.scss │ │ ├── _groff-output.scss │ │ ├── _toolbar.scss │ │ └── _switch.scss │ ├── layouts │ │ ├── _live-preview.scss │ │ └── _banner.scss │ └── main.scss ├── js │ ├── main.js │ ├── services │ │ └── text-parser.js │ └── behaviors │ │ ├── set-title.js │ │ └── page-view.js └── img │ ├── github.svg │ └── grapse.svg ├── bower.json ├── package.json ├── manview.3 ├── README.md ├── deploy.sh ├── index.html └── Gruntfile.js /.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "assets/vendor" 3 | } 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | assets/vendor 3 | assets/build 4 | dist 5 | .DS_Store -------------------------------------------------------------------------------- /assets/scss/base/_variables.scss: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Global variables 3 | // ------------------------------------------- 4 | 5 | // Measures 6 | $banner-height: 50px; 7 | 8 | // Colors 9 | 10 | $main-bg-color: #373D49; 11 | 12 | $main-border-color: #E8E8E8; 13 | 14 | $secondary-text-color: #A0AABF; -------------------------------------------------------------------------------- /assets/js/main.js: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Main 3 | // ------------------------------------------- 4 | 5 | ManView = {}; 6 | ManView.Behaviors = {}; 7 | ManView.Services = {}; 8 | 9 | document.addEventListener('DOMContentLoaded', function() { 10 | Essential.loadBehaviors({ 11 | application: ManView.Behaviors, 12 | context: document 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /assets/scss/modules/_code-box.scss: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Code box 3 | // ------------------------------------------- 4 | 5 | .code-box { 6 | $border: 1px solid $main-border-color; 7 | 8 | border: $border; 9 | 10 | .code-box-header { 11 | padding: 5px 30px 5px; 12 | 13 | border-bottom: $border; 14 | 15 | color: $secondary-text-color; 16 | text-transform: uppercase; 17 | font-size: 16px; 18 | } 19 | } -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "manview", 3 | "version": "0.0.1", 4 | "authors": [ 5 | "Roberto Dip ", 6 | "Diomidis Spinellis " 7 | ], 8 | "license": "MIT", 9 | "ignore": [ 10 | "**/.*", 11 | "node_modules", 12 | "bower_components", 13 | "test", 14 | "tests" 15 | ], 16 | "dependencies": { 17 | "normalize-scss": "~3.0.3", 18 | "jroff": "*", 19 | "essential.js": "essential#~0.5.0", 20 | "uri.js": "*" 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /assets/js/services/text-parser.js: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Text parser 3 | // ------------------------------------------- 4 | 5 | ManView.Services.TextParser = Proto.extend({ 6 | constructor: function() { 7 | this.generator = new Jroff.HTMLGenerator(); 8 | this.macroLib = 'an'; 9 | }, 10 | 11 | setMacroLib: function(macroLib) { 12 | this.macroLib = macroLib; 13 | }, 14 | 15 | parseGroff: function(text) { 16 | return this.generator.generate(text, this.macroLib); 17 | } 18 | }); 19 | -------------------------------------------------------------------------------- /assets/scss/modules/_groff-output.scss: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Groff output 3 | // ------------------------------------------- 4 | 5 | .groff-output { 6 | padding: 20px 15px; 7 | 8 | > p:first-of-type { 9 | @include clearfix(); 10 | 11 | margin-bottom: 15px; 12 | 13 | text-align: center; 14 | 15 | span:first-child { 16 | float: left; 17 | } 18 | 19 | span:last-child { 20 | float: right; 21 | } 22 | } 23 | 24 | h2 { 25 | margin: 15px 0; 26 | } 27 | } -------------------------------------------------------------------------------- /assets/scss/layouts/_live-preview.scss: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Live preview 3 | // ------------------------------------------- 4 | 5 | .live-preview-l { 6 | @include clearfix(); 7 | 8 | .code-box { 9 | margin: 0; 10 | width: 100%; 11 | float: left; 12 | 13 | overflow: auto; 14 | 15 | #editor, #result { 16 | height: calc(100vh - #{$banner-height * 3}); 17 | width: 100%; 18 | } 19 | } 20 | 21 | @include breakpoint(small) { 22 | .code-box { 23 | float: none; 24 | width: 100%; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /assets/scss/modules/_toolbar.scss: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Toolbar 3 | // ------------------------------------------- 4 | 5 | .toolbar { 6 | $height: $banner-height - 10px; 7 | 8 | @include clearfix(); 9 | 10 | height: $height; 11 | padding: 0 15px; 12 | line-height: $height; 13 | 14 | .switch, .word-count { 15 | float: right; 16 | margin-left: 15px; 17 | } 18 | 19 | .switch { 20 | padding-top: 10px; 21 | } 22 | 23 | .word-count { 24 | font-size: 14px; 25 | 26 | .word-count-label { 27 | color: $secondary-text-color; 28 | text-transform: uppercase; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /assets/scss/base/_defaults.scss: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Defaults 3 | // -> Base styles 4 | // ------------------------------------------- 5 | 6 | *, *:before, *:after { 7 | box-sizing: inherit; 8 | } 9 | 10 | html, body { 11 | margin: 0; 12 | box-sizing: border-box; 13 | 14 | font-family: 'Open Sans', sans-serif; 15 | } 16 | 17 | h1, h2, h3, h4, p { 18 | margin: 0; 19 | } 20 | 21 | a { 22 | text-decoration: none; 23 | color: inherit; 24 | 25 | &:hover { 26 | text-decoration: underline; 27 | color: inherit; 28 | } 29 | } 30 | 31 | figure { 32 | display: inline-block; 33 | } 34 | 35 | [class*=icon-] { 36 | display: inline-block; 37 | background-size: cover; 38 | } -------------------------------------------------------------------------------- /assets/img/github.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /assets/scss/layouts/_banner.scss: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Banner 3 | // ------------------------------------------- 4 | 5 | [role="banner"] { 6 | @include clearfix(); 7 | 8 | background-color: $main-bg-color; 9 | height: $banner-height; 10 | padding: 0 15px; 11 | 12 | .banner-title { 13 | display: inline-block; 14 | line-height: $banner-height; 15 | 16 | background-size: cover; 17 | font-size: 16px; 18 | color: $secondary-text-color; 19 | } 20 | 21 | .icons-bar { 22 | float: right; 23 | line-height: $banner-height; 24 | 25 | .icon-github { 26 | width: 25px; 27 | height: 25px; 28 | display: inline-block; 29 | 30 | background-size: cover; 31 | vertical-align: middle; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /assets/js/behaviors/set-title.js: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Set title 3 | // ------------------------------------------- 4 | 5 | ManView.Behaviors.SetTitle = Essential.Behavior.extend({ 6 | priority: 1, 7 | 8 | init: function() { 9 | var title = "manview"; 10 | var parsedURI = URI.parse(window.location.href); 11 | if (parsedURI.query) { 12 | var query = URI.parseQuery(parsedURI.query); 13 | if (query.name && query.link) 14 | title += " — " + "" + query.name + ""; 15 | else if (query.name) 16 | title += " — " + query.name; 17 | else if (query.link) 18 | title += " — " + "Manual Page"; 19 | this.el.innerHTML = title; 20 | } 21 | }, 22 | 23 | }); 24 | -------------------------------------------------------------------------------- /assets/scss/main.scss: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Stylesheet manifest 3 | // ------------------------------------------- 4 | 5 | // ------------------------------------- 6 | // Vendor 7 | // ------------------------------------- 8 | 9 | @import "../vendor/normalize-scss/_normalize"; 10 | 11 | // ------------------------------------- 12 | // Base 13 | // ------------------------------------- 14 | 15 | @import "base/variables"; 16 | @import "base/mixins"; 17 | @import "base/defaults"; 18 | 19 | // ------------------------------------- 20 | // Layouts 21 | // ------------------------------------- 22 | 23 | @import "layouts/banner"; 24 | @import "layouts/live-preview"; 25 | 26 | // ------------------------------------- 27 | // Modules 28 | // ------------------------------------- 29 | 30 | @import "modules/toolbar"; 31 | @import "modules/code-box"; 32 | @import "modules/groff-output"; 33 | @import "modules/switch"; 34 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "manview", 3 | "version": "0.0.1", 4 | "description": "Online troff page viewer", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "deploy": "grunt build && ./deploy.sh" 9 | }, 10 | "author": "Roberto Dip and Diomidis Spinellis", 11 | "license": "MIT", 12 | "devDependencies": {}, 13 | "dependencies": { 14 | "bower": "^1.8.2", 15 | "grunt": "^0.4.5", 16 | "grunt-autoprefixer": "^3.0.3", 17 | "grunt-browser-sync": "^2.2.0", 18 | "grunt-contrib-concat": "^0.5.1", 19 | "grunt-contrib-copy": "^0.8.2", 20 | "grunt-contrib-jshint": "^0.11.3", 21 | "grunt-contrib-uglify": "^0.11.0", 22 | "grunt-contrib-watch": "^0.6.1", 23 | "grunt-grunticon": "^2.2.2", 24 | "grunt-modernizr": "^1.0.1", 25 | "grunt-newer": "^1.1.1", 26 | "grunt-sass": "^1.1.0", 27 | "load-grunt-tasks": "^3.4.0", 28 | "time-grunt": "^1.3.0" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /manview.3: -------------------------------------------------------------------------------- 1 | .\" Default manual page 2 | .TH manview 3 2018-03-19 Other 3 | 4 | .SH NAME 5 | manview \- web page that formats a Unix manual page retrieved from the web 6 | 7 | .SH SYNOPSIS 8 | .nf 9 | .B https://dspinellis.github.io/manview/ 10 | ?src=\fIURL\fP[&name=\fItitle\fP][&link=\fIURL\fP] 11 | 12 | .SH DESCRIPTION 13 | The 14 | .B manview 15 | web page displays formatted Unix manual pages 16 | by specifying the URL of their \fItroff\fP source code as an 17 | argument. 18 | Further opetional arguments can specify the displayed title and 19 | the title's hyperlink. 20 | 21 | .SH IMPLEMENTATION NOTES 22 | The source code for this application is available on GitHub 23 | https://github.com/dspinellis/manview. 24 | .B Manview 25 | is heavily based on Jroff, essential.js, and Grapse. 26 | 27 | .SH EXAMPLE 28 | Format the Seventh Edition Unix 29 | .BR pipe (2) 30 | manual page 31 | retrieved from the Unix History Repository. 32 | .br 33 | .nf 34 | https://dspinellis.github.io/manview/?src=https%3A%2F%2Fraw.githubusercontent.com%2Fdspinellis%2Funix-history-repo%2FResearch-V7%2Fusr%2Fman%2Fman2%2Fpipe.2&name=Research%20V7%3A%20pipe(2)&link=https%3A%2F%2Fgithub.com%2Fdspinellis%2Funix-history-repo%2Fblob%2FResearch-V7%2Fusr%2Fman%2Fman2%2Fpipe.2 35 | .fi 36 | 37 | .SH SEE ALSO 38 | The evolution of Unix facilities web page at 39 | https://dspinellis.github.io/unix-history-man/. 40 | 41 | .SH AUTHOR 42 | .B Manview 43 | is written by Diomidis Spinellis 44 | based on Jroff and Grapse written by Roberto Dip. 45 | -------------------------------------------------------------------------------- /assets/js/behaviors/page-view.js: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Live preview 3 | // ------------------------------------------- 4 | 5 | ManView.Behaviors.PageView = Essential.Behavior.extend({ 6 | priority: 1, 7 | 8 | init: function() { 9 | var url = 'https://raw.githubusercontent.com/dspinellis/manview/master/manview.3'; 10 | var parsedURI = URI.parse(window.location.href); 11 | console.log('parsedURI ' + parsedURI); 12 | if (parsedURI.query) { 13 | var query = URI.parseQuery(parsedURI.query); 14 | console.log('Query: ' + query); 15 | if (query.src) 16 | url = query.src; 17 | } 18 | this.parser = ManView.Services.TextParser.new(); 19 | var request = new XMLHttpRequest(); 20 | console.log('Fetching ' + url); 21 | request.open('GET', url); 22 | request.responseType = 'text'; 23 | request.onload = function() { 24 | var customEvent = new CustomEvent("source:retrieved", { 25 | "detail": { 26 | text: request.response 27 | } 28 | }); 29 | document.dispatchEvent(customEvent); 30 | console.log("Received " + request.response.length + " bytes"); 31 | }; 32 | request.send(); 33 | }, 34 | 35 | channels: { 36 | 'source:retrieved': 'refreshChannel', 37 | }, 38 | 39 | refreshChannel: function(e) { 40 | var text = e.detail.text; 41 | console.log("Source text length: " + text.length); 42 | if (text.search(".Dd") != -1) 43 | this.parser.setMacroLib("doc"); 44 | this.el.innerHTML = this.parser.parseGroff(text); 45 | console.log("HTML length: " + this.el.innerHTML.length); 46 | }, 47 | 48 | }); 49 | -------------------------------------------------------------------------------- /assets/scss/base/_mixins.scss: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Breakpoint mixins 3 | // ------------------------------------------- 4 | 5 | @mixin breakpoint($point) { 6 | @if $point == wide { 7 | @media (min-width: 1101px) { @content; } 8 | } 9 | @else if $point == standard { 10 | @media (max-width: 1100px) { @content; } 11 | } 12 | @else if $point == notsmall { 13 | @media (min-width: 767px) { @content; } 14 | } 15 | @else if $point == small { 16 | @media (max-width: 767px) { @content; } 17 | } 18 | } 19 | 20 | // ------------------------------------------- 21 | // Hide text 22 | // ------------------------------------------- 23 | 24 | @mixin hide-text { 25 | vertical-align: text-top; 26 | text-indent: -1000px; 27 | overflow: hidden; 28 | color: transparent; 29 | text-shadow: none; 30 | } 31 | 32 | // ------------------------------------------- 33 | // Reset lists 34 | // ------------------------------------------- 35 | 36 | @mixin reset-list { 37 | margin: 0; 38 | padding: 0; 39 | list-style: none; 40 | } 41 | 42 | // ------------------------------------------- 43 | // Cleafix hack 44 | // ------------------------------------------- 45 | 46 | @mixin clearfix { 47 | &:after { 48 | content: "."; 49 | display: block; 50 | height: 0; 51 | clear: both; 52 | visibility: hidden; 53 | } 54 | } 55 | 56 | // ------------------------------------------- 57 | // Container 58 | // ------------------------------------------- 59 | 60 | // @mixin container { 61 | // @include clearfix(); 62 | 63 | // margin: 0 auto; 64 | // max-width: $container-width; 65 | // } 66 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # manview 2 | 3 | *Unix manual pages online viewer* 4 | 5 | *Manview* is heavily based on 6 | [Jroff](https://github.com/roperzh/jroff), 7 | [essential.js](http://roperzh.github.io/essential.js/), and 8 | [Grapse](https://github.com/roperzh/grapse). 9 | 10 | ## Contributing: 11 | 12 | ### Dependencies: 13 | 14 | - Node.js 15 | - Grunt.js 16 | - Bower 17 | - libsass 18 | 19 | ### Getting started 20 | 21 | Just clone the project and install the dependencies! 22 | 23 | ```bash 24 | $ git clone https://github.com/dspinellis/manview 25 | $ npm install 26 | $ bower install 27 | $ grunt build 28 | $ grunt 29 | ``` 30 | ### Development 31 | 32 | * To update the distributed version of jroff 33 | `cp ../jroff/dist/jroff.js assets/vendor/jroff/dist/jroff.js` 34 | 35 | * To test the current version 36 | 37 | ```bash 38 | grunt build 39 | cd dist 40 | python -m SimpleHTTPServer 3001 41 | ``` 42 | 43 | * Point your browser to a URL such as [http://localhost:3001/?src=https://raw.githubusercontent.com/dspinellis/git-issue/master/git-issue.1](http://localhost:3001/?src=https://raw.githubusercontent.com/dspinellis/git-issue/master/git-issue.1) 44 | 45 | ### Deployment 46 | 47 | In order to deploy, commit and push all your changes on the master 48 | branch, and then run 49 | 50 | ```bash 51 | $ npm run deploy 52 | ``` 53 | 54 | ### Use 55 | Pass the URL where the manual source page is located in an `src` 56 | query argument. 57 | To avoid same-origin problems the response 58 | (and any redirections leading to it) should include 59 | an appropriate CORS `Access-Control-Allow-Origin` header. 60 | (It's a good idea to avoid redirecting URLs.) 61 | You can also pass the page's title in the `name` query 62 | argument and its hyperlink in the `link` query argument. 63 | As an example of this page's use, the 64 | [Evolution of Unix Facilities](https://dspinellis.github.io/unix-history-man/) 65 | web site contains links to 193,781 dynamically-generated historic 66 | and current Unix manual pages. 67 | -------------------------------------------------------------------------------- /assets/scss/modules/_switch.scss: -------------------------------------------------------------------------------- 1 | // ------------------------------------------- 2 | // Switch 3 | // ------------------------------------------- 4 | 5 | .switch { 6 | $switch-height: 20px; 7 | $switch-width: $switch-height * 2; 8 | $padding: 2px; 9 | 10 | @include clearfix(); 11 | 12 | position: relative; 13 | max-height: $switch-height; 14 | line-height: $switch-height; 15 | 16 | .switch-label { 17 | float: left; 18 | padding: 0 5px; 19 | line-height: $switch-height; 20 | 21 | font-size: 14px; 22 | color: black; 23 | text-transform: uppercase; 24 | } 25 | 26 | .cmn-toggle { 27 | position: absolute; 28 | margin-left: -9999px; 29 | visibility: hidden; 30 | } 31 | 32 | .cmn-toggle + label { 33 | display: block; 34 | position: relative; 35 | cursor: pointer; 36 | outline: none; 37 | user-select: none; 38 | float: left; 39 | line-height: $switch-height; 40 | } 41 | 42 | .cmn-toggle-round-flat + label { 43 | padding: $padding; 44 | width: $switch-width; 45 | height: $switch-height; 46 | background-color: $main-border-color; 47 | border-radius: $switch-width; 48 | transition: background 0.4s; 49 | } 50 | 51 | .cmn-toggle-round-flat + label:before, 52 | .cmn-toggle-round-flat + label:after { 53 | display: block; 54 | position: absolute; 55 | content: ""; 56 | } 57 | 58 | .cmn-toggle-round-flat + label:before { 59 | top: $padding; 60 | left: $padding; 61 | bottom: $padding; 62 | right: $padding; 63 | background-color: #fff; 64 | border-radius: $switch-width; 65 | transition: background 0.4s; 66 | } 67 | 68 | .cmn-toggle-round-flat + label:after { 69 | top: $padding * 2; 70 | left: $padding * 2; 71 | bottom: $padding * 2; 72 | width: $switch-width / 2; 73 | background-color: $main-border-color; 74 | border-radius: $switch-width; 75 | transition: transform 0.4s; 76 | } 77 | 78 | .cmn-toggle-round-flat:checked + label:after { 79 | transform: translateX(#{$switch-width - ($padding * 14)}); 80 | } 81 | } -------------------------------------------------------------------------------- /deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -o errexit #abort if any command fails 3 | 4 | deploy_directory=dist 5 | deploy_branch=gh-pages 6 | 7 | #repository to deploy to. must be readable and writable. 8 | repo=origin 9 | 10 | # Parse arg flags 11 | while : ; do 12 | if [[ $1 = "-v" || $1 = "--verbose" ]]; then 13 | verbose=true 14 | shift 15 | elif [[ $1 = "-s" || $1 = "--setup" ]]; then 16 | setup=true 17 | shift 18 | elif [[ $1 = "-e" || $1 = "--allow-empty" ]]; then 19 | allow_empty=true 20 | shift 21 | else 22 | break 23 | fi 24 | done 25 | 26 | #echo expanded commands as they are executed (for debugging) 27 | function enable_expanded_output { 28 | if [ $verbose ]; then 29 | set -o xtrace 30 | set +o verbose 31 | fi 32 | } 33 | 34 | #this is used to avoid outputting the repo URL, which may contain a secret token 35 | function disable_expanded_output { 36 | if [ $verbose ]; then 37 | set +o xtrace 38 | set -o verbose 39 | fi 40 | } 41 | 42 | enable_expanded_output 43 | 44 | function set_user_id { 45 | if [[ -z `git config user.name` ]]; then 46 | git config user.name "$default_username" 47 | fi 48 | if [[ -z `git config user.email` ]]; then 49 | git config user.email "$default_email" 50 | fi 51 | } 52 | 53 | function restore_head { 54 | if [[ $previous_branch = "HEAD" ]]; then 55 | #we weren't on any branch before, so just set HEAD back to the commit it was on 56 | git update-ref --no-deref HEAD $commit_hash $deploy_branch 57 | else 58 | git symbolic-ref HEAD refs/heads/$previous_branch 59 | fi 60 | 61 | git reset --mixed 62 | } 63 | 64 | if ! git diff --exit-code --quiet --cached; then 65 | echo Aborting due to uncommitted changes in the index >&2 66 | exit 1 67 | fi 68 | 69 | commit_title=`git log -n 1 --format="%s" HEAD` 70 | commit_hash=`git log -n 1 --format="%H" HEAD` 71 | previous_branch=`git rev-parse --abbrev-ref HEAD` 72 | 73 | if [ $setup ]; then 74 | mkdir -p "$deploy_directory" 75 | git --work-tree "$deploy_directory" checkout --orphan $deploy_branch 76 | git --work-tree "$deploy_directory" rm -r "*" 77 | git --work-tree "$deploy_directory" add --all 78 | git --work-tree "$deploy_directory" commit -m "initial publish"$'\n\n'"generated from commit $commit_hash" 79 | git push $repo $deploy_branch 80 | restore_head 81 | exit 82 | fi 83 | 84 | if [ ! -d "$deploy_directory" ]; then 85 | echo "Deploy directory '$deploy_directory' does not exist. Aborting." >&2 86 | exit 1 87 | fi 88 | 89 | if [[ -z `ls -A "$deploy_directory" 2> /dev/null` && -z $allow_empty ]]; then 90 | echo "Deploy directory '$deploy_directory' is empty. Aborting. If you're sure you want to deploy an empty tree, use the -e flag." >&2 91 | exit 1 92 | fi 93 | 94 | disable_expanded_output 95 | git fetch --force $repo $deploy_branch:$deploy_branch 96 | enable_expanded_output 97 | 98 | #make deploy_branch the current branch 99 | git symbolic-ref HEAD refs/heads/$deploy_branch 100 | 101 | #put the previously committed contents of deploy_branch branch into the index 102 | git --work-tree "$deploy_directory" reset --mixed --quiet 103 | 104 | git --work-tree "$deploy_directory" add --all 105 | 106 | set +o errexit 107 | diff=$(git --work-tree "$deploy_directory" diff --exit-code --quiet HEAD)$? 108 | set -o errexit 109 | case $diff in 110 | 0) echo No changes to files in $deploy_directory. Skipping commit.;; 111 | 1) 112 | set_user_id 113 | git --work-tree "$deploy_directory" commit -m \ 114 | "publish: $commit_title"$'\n\n'"generated from commit $commit_hash" 115 | 116 | disable_expanded_output 117 | #--quiet is important here to avoid outputting the repo URL, which may contain a secret token 118 | git push --quiet $repo $deploy_branch 119 | enable_expanded_output 120 | ;; 121 | *) 122 | echo git diff exited with code $diff. Aborting. Staying on branch $deploy_branch so you can debug. To switch back to master, use: git symbolic-ref HEAD refs/heads/master && git reset --mixed >&2 123 | exit $diff 124 | ;; 125 | esac 126 | 127 | restore_head 128 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Manview | Dynamic Unix man page viewer 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 24 | 25 | 26 | 29 | 30 | 31 |
32 |

manview

33 | 34 |
35 | 36 |
37 |
38 | 39 |
40 |
41 |
42 |
43 |
44 |
45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | module.exports = function(grunt) { 3 | // Load all tasks 4 | require('load-grunt-tasks')(grunt); 5 | // Show elapsed time 6 | require('time-grunt')(grunt); 7 | 8 | var jsFileList = [ 9 | 'assets/vendor/jroff/dist/jroff.js', 10 | 'assets/vendor/essential.js/essential.js', 11 | 'assets/js/main.js', 12 | 'assets/js/services/*.js', 13 | 'assets/js/behaviors/*.js', 14 | 'assets/vendor/uri.js/src/URI.min.js' 15 | ]; 16 | 17 | grunt.initConfig({ 18 | distPath: 'assets/build', 19 | jshint: { 20 | options: { 21 | jshintrc: '.jshintrc' 22 | }, 23 | all: [ 24 | 'Gruntfile.js', 25 | 'assets/js/*.js', 26 | '!<%= distPath %>/js/scripts.js', 27 | '!assets/**/*.min.*' 28 | ] 29 | }, 30 | sass: { 31 | options: { 32 | sourceMap: false 33 | }, 34 | dev: { 35 | files: { 36 | '<%= distPath %>/css/main.css': 'assets/scss/main.scss' 37 | } 38 | }, 39 | build: { 40 | files: { 41 | '<%= distPath %>/css/main.min.css': 'assets/scss/main.scss' 42 | } 43 | } 44 | }, 45 | concat: { 46 | options: { 47 | separator: ';', 48 | }, 49 | dist: { 50 | src: [jsFileList], 51 | dest: '<%= distPath %>/js/scripts.js', 52 | }, 53 | }, 54 | uglify: { 55 | dist: { 56 | files: { 57 | '<%= distPath %>/js/scripts.min.js': [jsFileList] 58 | } 59 | } 60 | }, 61 | autoprefixer: { 62 | options: { 63 | browsers: ['last 2 versions', 'ie 8', 'ie 9', 'android 2.3', 'android 4', 'opera 12'] 64 | }, 65 | dev: { 66 | options: { 67 | map: { 68 | prev: '<%= distPath %>/css/' 69 | } 70 | }, 71 | src: '<%= distPath %>/css/main.css' 72 | }, 73 | build: { 74 | src: '<%= distPath %>/css/main.min.css' 75 | } 76 | }, 77 | modernizr: { 78 | build: { 79 | devFile: 'assets/vendor/modernizr/modernizr.js', 80 | outputFile: 'assets/js/vendor/modernizr.min.js', 81 | files: { 82 | 'src': [ 83 | ['<%= distPath %>/js/scripts.min.js'], 84 | ['<%= distPath %>/css/main.min.css'] 85 | ] 86 | }, 87 | extra: { 88 | shiv: false 89 | }, 90 | uglify: true, 91 | parseFiles: true 92 | } 93 | }, 94 | grunticon: { 95 | myIcons: { 96 | files: [{ 97 | expand: true, 98 | cwd: 'assets/img', 99 | src: ['*.svg', '*.png'], 100 | dest: '<%= distPath %>/css/icons' 101 | }], 102 | options: { 103 | enhanceSVG: true 104 | } 105 | } 106 | }, 107 | browserSync: { 108 | dev: { 109 | options: { 110 | open: false, 111 | server: { 112 | baseDir: './' 113 | }, 114 | files: ['<%= distPath %>/css/main.css', '<%= distPath %>/js/scripts.js', 'index.html'], 115 | watchTask: true 116 | } 117 | } 118 | }, 119 | watch: { 120 | sass: { 121 | files: [ 122 | 'assets/scss/*.scss', 123 | 'assets/scss/**/*.scss' 124 | ], 125 | tasks: ['sass:dev', 'newer:autoprefixer:dev'], 126 | options: { 127 | spawn: false 128 | } 129 | }, 130 | js: { 131 | files: [ 132 | jsFileList 133 | ], 134 | tasks: ['newer:concat'], 135 | options: { 136 | spawn: false 137 | } 138 | }, 139 | img: { 140 | files: [ 141 | 'assets/img/*.svg' 142 | ], 143 | tasks: ['grunticon'], 144 | options: { 145 | spawn: false 146 | } 147 | } 148 | }, 149 | copy: { 150 | dist: { 151 | files: [ 152 | { expand: true, src: ['<%= distPath %>/**'], dest: 'dist/', filter: 'isFile' }, 153 | { src: ['index.html'], dest: 'dist/index.html' }, 154 | ] 155 | } 156 | } 157 | }); 158 | 159 | // Register tasks 160 | grunt.registerTask('default', [ 161 | 'browserSync', 162 | 'watch' 163 | ]); 164 | 165 | grunt.registerTask('dev', [ 166 | 'grunticon', 167 | 'jshint', 168 | 'sass', 169 | 'autoprefixer:dev', 170 | 'concat' 171 | ]); 172 | 173 | grunt.registerTask('build', [ 174 | // 'jshint', 175 | 'sass', 176 | 'autoprefixer:build', 177 | 'uglify', 178 | 'modernizr', 179 | 'concat', 180 | 'copy' 181 | ]); 182 | }; 183 | -------------------------------------------------------------------------------- /assets/img/grapse.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 25 | 66 | 104 | 129 | 157 | 195 | 227 | 252 | 253 | 254 | --------------------------------------------------------------------------------