├── .gitignore ├── LICENSE ├── README.md ├── make.js ├── monitor.sh ├── package.json ├── repos ├── conquest.json ├── dombuilder.json ├── domlog.json ├── gen-run.json ├── grab.js ├── mine.json ├── msgpack-js.json ├── push-to-pull.json ├── rec.json └── uvrun.json ├── res ├── icon-128.png ├── icon-16.png ├── icon-60.png ├── index.html ├── prism.css └── sprites.png └── src ├── app ├── backend.js ├── phone-ui.js └── ui.js ├── appinfo.json ├── background.js ├── chrome.js ├── chrome.less ├── git-browser.appcache ├── icons.less ├── lib ├── chrome-tcp.js ├── defer.js ├── git-chrome-localdb.js ├── progress-parser.js └── trace.js ├── manifest.json ├── manifest.webapp ├── moz-tcp-trace.js ├── moz-tcp.js ├── moz.js ├── moz.less ├── old ├── chrome.js ├── git-idb.js ├── git-webfs.js ├── moz.js └── webos.js ├── prism ├── prism-bash.js ├── prism-c.js ├── prism-clike.js ├── prism-coffeescript.js ├── prism-core.js ├── prism-cpp.js ├── prism-css-extras.js ├── prism-css.js ├── prism-javascript.js └── prism-markup.js ├── server.js ├── style.less ├── transitions.less ├── web.js ├── web.less └── webos.less /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | build 3 | .zedstate 4 | tags 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Tim Caswell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | git-browser 2 | =========== 3 | 4 | ![js-git browser](http://creationix.com/git-browser-tile-1400x560-half.png) 5 | 6 | Browse Git Repos offline. 7 | 8 | ## Firefox OS Build Instructions 9 | 10 | ```sh 11 | git clone git@github.com:creationix/git-browser.git 12 | cd git-browser 13 | npm install 14 | node make.js moz 15 | ``` 16 | 17 | Then add `build/moz/manifest.webapp` to the simulator and push it to a device. 18 | 19 | ## Chrome Packaged App Build Instructions 20 | 21 | ```sh 22 | git clone git@github.com:creationix/git-browser.git 23 | cd git-browser 24 | npm install 25 | node make.js chrome 26 | ``` 27 | 28 | Then go to and "Load unpacked extension..." and browse to `build/chrome`. 29 | 30 | ## Web App Build Instructions 31 | 32 | This builds a web app that can run in a normal hosted webpage 33 | 34 | ```sh 35 | git clone git@github.com:creationix/git-browser.git 36 | cd git-browser 37 | npm install 38 | node make.js web 39 | ``` 40 | 41 | Then point your browser to `build/web/index.html`. 42 | 43 | ## Developing Instructions 44 | 45 | Included is a `monitor.sh` script that watches for file changes and updates the build directories. 46 | 47 | It accepts make targets as it's arguments, so simply replace `./make` in the above instructions with `./monitor.sh`. 48 | 49 | * Note that this requires `inotifywait` which is found in the `inotify-tools` package in Ubuntu Linux. 50 | -------------------------------------------------------------------------------- /make.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var T = require('tim-task'); 3 | var path = require('path'); 4 | 5 | // Override paths using environment variables 6 | var WEBDIR = process.env.WEBDIR || "build/web"; 7 | var MOZDIR = process.env.MOZDIR || "build/moz"; 8 | var CHROMEDIR = process.env.CHROMEDIR || "build/chrome"; 9 | var WEBOSDIR = process.env.WEBOSDIR || "build/webos"; 10 | var MOZZIP = MOZDIR + "-app.zip"; 11 | var CHROMEZIP = CHROMEDIR + "-app.zip"; 12 | var IPK = "build/com.creationix.git-browser_0.0.1_all.ipk"; 13 | 14 | T("code", ["web", "moz", "chrome"]); 15 | 16 | T("all", ["code", "moz-zip", "chrome-zip"]); 17 | 18 | function base(bootstrap, targetDir) { 19 | return T.parallel( 20 | T.copy("res", targetDir), 21 | T.newer("src", /\.less$/, targetDir + "/style.css", 22 | T.lessc("src/" + bootstrap + ".less", targetDir + "/style.css") 23 | ), 24 | T.build("src/" + bootstrap + ".js", targetDir + "/app.js") 25 | ); 26 | } 27 | function zipFile(zip, dir) { 28 | return T.serial( 29 | T.parallel( 30 | T.rmrf(zip), 31 | T.mkdirp(path.dirname(zip)) 32 | ), 33 | T.execFile("zip", ["-o", "-r", path.relative(dir, zip), '.'], {cwd: dir}) 34 | ); 35 | } 36 | 37 | T("web", T.serial( 38 | T.parallel( 39 | T.copy("src/server.js", WEBDIR + "/server.js"), 40 | base("web", WEBDIR) 41 | ), 42 | T.manifest(WEBDIR, [ 43 | "index.html", 44 | "style.css", 45 | "app.js", 46 | "prism.css" 47 | ], "git-browser.appcache") 48 | )); 49 | 50 | T("webos", T.parallel( 51 | base("webos", WEBOSDIR), 52 | T.copy("src/appinfo.json", WEBOSDIR + "/appinfo.json") 53 | )); 54 | 55 | T("webos", T.parallel( 56 | base("webos", WEBOSDIR), 57 | T.copy("src/appinfo.json", WEBOSDIR + "/appinfo.json") 58 | )); 59 | 60 | T("webos-ipk", ["webos"], T.newer(WEBOSDIR, /.*/, IPK, 61 | T.execFile("palm-package", [path.relative("build", WEBOSDIR)], {cwd:"build"}) 62 | )); 63 | 64 | T("webos-install", ["webos-ipk"], 65 | T.execFile("palm-install", [IPK], {}) 66 | ); 67 | 68 | T("webos-run", ["webos-install"], 69 | T.execFile("palm-launch", ["com.creationix.git-browser"], {}) 70 | ); 71 | 72 | T("moz", T.parallel( 73 | base("moz", MOZDIR), 74 | T.copy("src/manifest.webapp", MOZDIR + "/manifest.webapp") 75 | )); 76 | 77 | T("moz-zip", ["moz"], zipFile(MOZZIP, MOZDIR)); 78 | 79 | T("chrome", T.parallel( 80 | base("chrome", CHROMEDIR), 81 | T.copy("src/manifest.json", CHROMEDIR + "/manifest.json"), 82 | T.copy("src/background.js", CHROMEDIR + "/background.js") 83 | )); 84 | 85 | T("chrome-zip", ["chrome"], zipFile(CHROMEZIP, CHROMEDIR)); 86 | 87 | T("clean", T.rmrf("build")); 88 | 89 | T.execute(T.run, process.argv.slice(2), function (err) { 90 | if (err) { 91 | console.error(err.stack || err); 92 | process.exit(-1); 93 | } 94 | // console.log("Done"); 95 | }); 96 | -------------------------------------------------------------------------------- /monitor.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | ./make.js $@ 3 | while true 4 | do inotifywait -e create -e delete -e modify -q -r src res node_modules 5 | ./make.js $@ 6 | done 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "git-browser", 3 | "private": true, 4 | "version": "0.0.0", 5 | "description": "A git browser app for chrome and firefox", 6 | "dependencies": { 7 | "base64-js": "~0.0.2", 8 | "js-git": "~0.6.1", 9 | "push-to-pull": "~0.1.0", 10 | "to-utf8": "~0.0.1", 11 | "varint": "~0.0.3", 12 | "domlog": "0.0.9", 13 | "dombuilder": "~0.1.2", 14 | "git-fs-db": "~0.1.1", 15 | "git-net": "~0.0.4", 16 | "git-localdb": "~0.1.0", 17 | "git-memdb": "~0.3.0", 18 | "git-sha1": "~0.1.0", 19 | "git-zlib": "~0.1.0", 20 | "websocket-tcp-client": "~0.1.0", 21 | "git-http": "~0.1.1", 22 | "git-indexeddb": "~0.1.0", 23 | "bops": "~0.1.0" 24 | }, 25 | "devDependencies": { 26 | "tim-task": "~0.1.0", 27 | "send": "~0.1.4", 28 | "ws": "~0.4.31" 29 | }, 30 | "repository": { 31 | "type": "git", 32 | "url": "git://github.com/creationix/git-browser.git" 33 | }, 34 | "author": "Tim Caswell ", 35 | "license": "MIT", 36 | "bugs": { 37 | "url": "https://github.com/creationix/git-browser/issues" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /repos/domlog.json: -------------------------------------------------------------------------------- 1 | {"refs":{"refs/heads/master":"309b084b71bb267ed896f03de3ee74ebe848df8f","refs/tags/0.0.1":"ca48322725a9593df2cd9d2201841e21efde4e87","refs/tags/0.0.2":"f6f0eb679ce6ffc1dbca3accfb6566b6faba3f92","refs/tags/0.0.3":"0e659ab3d1a79be2490159dfc00688224b505df2","refs/tags/0.0.4":"fea7956949b6ba8cf64e39a76c9a291c07f80a47","refs/tags/0.0.5":"bdc79df551bdc6db108508d8443c446a340779a7","refs/tags/0.0.6":"0b777c5259d912c61f3b4961e062ab907ba45a8e","refs/tags/0.0.7":"db919a6b5ac7ad73a391a316d697749de030aa9a","refs/tags/0.0.8":"37ee9bbc881d0173fbafafe879b0f38673818ec8","refs/tags/0.0.9":"309b084b71bb267ed896f03de3ee74ebe848df8f"},"objects":{"309b084b71bb267ed896f03de3ee74ebe848df8f":{"type":"commit","body":{"tree":"4deff2c198e5665e639c50863f3c6ffe1f0da413","parents":["37ee9bbc881d0173fbafafe879b0f38673818ec8"],"author":"Tim Caswell 1377791993 -0500","committer":"Tim Caswell 1377791993 -0500","message":"Show custom object types\n"}},"37ee9bbc881d0173fbafafe879b0f38673818ec8":{"type":"commit","body":{"tree":"3a2b4086795c13d73b0bb3332d220f87f45c9f6c","parents":["db919a6b5ac7ad73a391a316d697749de030aa9a"],"author":"Tim Caswell 1377760694 -0500","committer":"Tim Caswell 1377760694 -0500","message":"Show inherited properties\n"}},"db919a6b5ac7ad73a391a316d697749de030aa9a":{"type":"commit","body":{"tree":"bf4d140d4ddf152d73bd678d5751178d94eb2e82","parents":["0b777c5259d912c61f3b4961e062ab907ba45a8e"],"author":"Tim Caswell 1371168236 -0500","committer":"Tim Caswell 1371168236 -0500","message":"Add repo link and bump version to 0.0.7\n"}},"0b777c5259d912c61f3b4961e062ab907ba45a8e":{"type":"commit","body":{"tree":"72e69e245d628da21a9841c03303b52b20a0a99d","parents":["bdc79df551bdc6db108508d8443c446a340779a7"],"author":"Tim Caswell 1369373544 -0500","committer":"Tim Caswell 1369373544 -0500","message":"Minor cleanup\n"}},"bdc79df551bdc6db108508d8443c446a340779a7":{"type":"commit","body":{"tree":"d9f22fee067085eeb166ceb0245a2e5cc1b3aec6","parents":["fea7956949b6ba8cf64e39a76c9a291c07f80a47"],"author":"Tim Caswell 1369193665 -0500","committer":"Tim Caswell 1369193665 -0500","message":"Improve string rendering\n"}},"fea7956949b6ba8cf64e39a76c9a291c07f80a47":{"type":"commit","body":{"tree":"a6c51164e4e31cbf81c2c04c7ebdf3db632df916","parents":["0e659ab3d1a79be2490159dfc00688224b505df2"],"author":"Tim Caswell 1369188509 -0500","committer":"Tim Caswell 1369188509 -0500","message":"Implement binary logger and tag as 0.0.4\n"}},"0e659ab3d1a79be2490159dfc00688224b505df2":{"type":"commit","body":{"tree":"92e6e81d2b0d6732babd4e3007313b3c07552ca0","parents":["f6f0eb679ce6ffc1dbca3accfb6566b6faba3f92"],"author":"Tim Caswell 1369178420 -0500","committer":"Tim Caswell 1369178420 -0500","message":"Add error logging\n"}},"f6f0eb679ce6ffc1dbca3accfb6566b6faba3f92":{"type":"commit","body":{"tree":"a112a0a1eadb65f49b86aeb30ba7138c5bced5a0","parents":["ca48322725a9593df2cd9d2201841e21efde4e87"],"author":"Tim Caswell 1369173288 -0500","committer":"Tim Caswell 1369173288 -0500","message":"Update style and bump to 0.0.2\n"}},"ca48322725a9593df2cd9d2201841e21efde4e87":{"type":"commit","body":{"tree":"c8b4d7c7a8b0df019d8aa8479bbf6ba7a9b72af6","parents":["9ab00ef1ff6eb35e41d1ba6d456ec633025b5b14"],"author":"Tim Caswell 1369172002 -0500","committer":"Tim Caswell 1369172002 -0500","message":"Initial Commit\n"}},"9ab00ef1ff6eb35e41d1ba6d456ec633025b5b14":{"type":"commit","body":{"tree":"fd23232d66e18ea045f46aa3713a4a06ff4888d8","author":"Tim Caswell 1369171903 -0700","committer":"Tim Caswell 1369171903 -0700","message":"Initial commit\n"}},"4deff2c198e5665e639c50863f3c6ffe1f0da413":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"80c1a291b917f3dbd573f136b50c8ea891544c2e"},{"mode":33188,"name":"log.js","hash":"b4fe4cc2c730661e86f37d8c19eb832bef93f7cc"},{"mode":33188,"name":"package.json","hash":"f24722e9c83bbca55058fdca1a2cf00f097556a6"}]},"3a2b4086795c13d73b0bb3332d220f87f45c9f6c":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"80c1a291b917f3dbd573f136b50c8ea891544c2e"},{"mode":33188,"name":"log.js","hash":"69d7828f98fd82de43f4919529a7c91b353d5d2d"},{"mode":33188,"name":"package.json","hash":"4768fa0a1fa50975412e4f0e3aa622b354eab596"}]},"72e69e245d628da21a9841c03303b52b20a0a99d":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"80c1a291b917f3dbd573f136b50c8ea891544c2e"},{"mode":33188,"name":"log.js","hash":"d8945fd20b86f4ac4717c1f476b007f55fd228c9"},{"mode":33188,"name":"package.json","hash":"09ba8f7d00ba06c336e3a87a4b848dc64d33804b"}]},"d9f22fee067085eeb166ceb0245a2e5cc1b3aec6":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"80c1a291b917f3dbd573f136b50c8ea891544c2e"},{"mode":33188,"name":"log.js","hash":"e70ae812af9c5c93be2911c4112b52dc2df2b8b0"},{"mode":33188,"name":"package.json","hash":"8a3b55c8067590ade2667b6d95e6af87dd30db9d"}]},"a6c51164e4e31cbf81c2c04c7ebdf3db632df916":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"80c1a291b917f3dbd573f136b50c8ea891544c2e"},{"mode":33188,"name":"log.js","hash":"c47c6fa58c77191c5886649531a8db1f77c4534d"},{"mode":33188,"name":"package.json","hash":"b09d1d795997a5e7808d5c1de76cc4ac599e7cba"}]},"92e6e81d2b0d6732babd4e3007313b3c07552ca0":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"80c1a291b917f3dbd573f136b50c8ea891544c2e"},{"mode":33188,"name":"log.js","hash":"34dc5c5a5f387ea6a77d5021ccdee17d7fe32916"},{"mode":33188,"name":"package.json","hash":"ffc0322e20b2302f2b67c214f29d74f21e378f71"}]},"a112a0a1eadb65f49b86aeb30ba7138c5bced5a0":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"80c1a291b917f3dbd573f136b50c8ea891544c2e"},{"mode":33188,"name":"log.js","hash":"65600abd74adf57ec345eb41e6aefb9930e1ba1f"},{"mode":33188,"name":"package.json","hash":"9fefbd85fbffc874846f4d5c6fc875b46e718d3d"}]},"c8b4d7c7a8b0df019d8aa8479bbf6ba7a9b72af6":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"80c1a291b917f3dbd573f136b50c8ea891544c2e"},{"mode":33188,"name":"log.js","hash":"5a782d5c837350f30355fda7e219d07a03d90087"},{"mode":33188,"name":"package.json","hash":"398a10d9fadea096f761fbe930920d2c32fa2af0"}]},"fd23232d66e18ea045f46aa3713a4a06ff4888d8":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"80c1a291b917f3dbd573f136b50c8ea891544c2e"}]},"80c1a291b917f3dbd573f136b50c8ea891544c2e":{"type":"blob","body":"domlog\n======\n\nA simple on-screen logger using dombuilder to create elements.\n"},"69d7828f98fd82de43f4919529a7c91b353d5d2d":{"type":"blob","body":"\"use strict\";\n\nvar domBuilder = require('dombuilder');\n\nmodule.exports = log;\nlog.toDom = toDom;\nlog.container = undefined;\nlog.setup = setup;\nlog.css =\n \".log:hover{background:rgba(0,0,0,1);height:100%;-webkit-user-select:text;}\\n\" +\n \".log{background:rgba(0,0,0,0.7);color:#ddd;font-family:monospace;padding:0;position:absolute;left:0;right:0;bottom:0;margin:0;height:200px;overflow:auto;transition:all 1s ease-in-out}\\n\" +\n \".log .array:after{content:']'}\\n\" +\n \".log .array:before{content:'['}\\n\" +\n \".log .binary{color:#88e}\\n\" +\n \".log .binary:after{content:'>'}\\n\" +\n \".log .binary:before{content:'<'}\\n\" +\n \".log .binary:before,.log .binary:after{font-weight:bold;color:#bcf}\\n\" +\n \".log .boolean{color:#f4a}\\n\" +\n \".log .error{color:#f33;white-space:pre-wrap}\\n\" +\n \".log .function{color:#fb0}\\n\" +\n \".log .null{color:#aaa;font-weight:bold}\\n\" +\n \".log .number{color:#5cf}\\n\" +\n \".log .hidden{opacity:0.5}\\n\" +\n \".log .object .key:after{content:':';font-weight:bold}\\n\" +\n \".log .object:after{content:'}'}\\n\" +\n \".log .object:before{content:'{'}\\n\" +\n \".log .string{color:#4e2}\\n\" +\n \".log .string:after{content:'\\\\201D'}\\n\" +\n \".log .string:before{content:'\\\\201C'}\\n\" +\n \".log .string:before,.log .string:after{color:#5f3;font-weight:bold}\\n\" +\n \".log .text{color:#fff}\\n\" +\n \".log .undefined{color:#aaa}\\n\" +\n \".log > li{padding:5px}\\n\" +\n \".log > li *{display:inline-block;margin:0 3px;padding:0}\\n\";\n\nfunction keys(obj) {\n var data = [];\n for (var key in obj) {\n data.push(key);\n }\n return data;\n}\n\nfunction toDom(val) {\n if (val === null) {\n return [\"span.null\", \"null\"];\n }\n if (Array.isArray(val)) {\n return [\"ul.array\"].concat(\n val.map(function (item) {\n return [\"li\", toDom(item)];\n })\n );\n }\n if (val instanceof Error) {\n return [\".error\", val.stack];\n }\n if (val instanceof Uint8Array) {\n var str = val.length.toString(16) + \":\";\n for (var i = 0, l = Math.min(val.length, 25); i < l; i++) {\n var c = val[i];\n if (c < 0x10) str += \"0\" + c.toString(16);\n else str += c.toString(16);\n }\n if (i < val.length) str += \"...\";\n return [\".binary\", str];\n }\n var type = typeof val;\n if (type === \"object\") {\n return [\"dl.object\"].concat(\n keys(val).map(function (key) {\n return [\n [\"dt\", {class: val.hasOwnProperty(key) ? \"key\" : \"key hidden\" }, key],\n [\"dd\", toDom(val[key])]\n ];\n })\n );\n }\n if (type === \"string\") {\n val = JSON.stringify(val);\n val = val.substr(1, val.length - 2);\n return [\"span.string\", val];\n }\n var title = \"\" + val;\n val = title.split(\"\\n\");\n if (val.length > 1) {\n val = val[0] + \" ... \" + val[val.length - 1];\n }\n else {\n val = val[0];\n }\n return [\"span\", {title: title, class: type}, document.createTextNode(val)];\n}\n\nfunction setup(extra) {\n var style = document.createElement(\"style\");\n style.textContent = log.css;\n document.head.appendChild(style);\n log.container = domBuilder([\"ul.log\"]);\n if (extra) {\n for (var key in extra) {\n log.container.style[key] = extra[key];\n }\n }\n document.body.appendChild(log.container);\n}\n\nfunction item(val, i) {\n if (!i && typeof val === \"string\") return [\"span.text\", val];\n return toDom(val);\n}\n\nfunction log() {\n if (!log.container) setup();\n var child = domBuilder([\"li\"].concat(Array.prototype.map.call(arguments, item)));\n log.container.appendChild(child);\n log.container.scrollTop = child.offsetTop;\n}\n"},"4768fa0a1fa50975412e4f0e3aa622b354eab596":{"type":"blob","body":"{\n \"name\": \"domlog\",\n \"description\": \"A simple on-screen logger using dombuilder to create elements.\",\n \"version\": \"0.0.8\",\n \"author\": \"Tim Caswell \",\n \"main\": \"log.js\",\n \"dependencies\": {\n \"dombuilder\": \"~0.1.2\"\n },\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git://github.com/creationix/domlog.git\"\n }\n}\n"},"bf4d140d4ddf152d73bd678d5751178d94eb2e82":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"80c1a291b917f3dbd573f136b50c8ea891544c2e"},{"mode":33188,"name":"log.js","hash":"d8945fd20b86f4ac4717c1f476b007f55fd228c9"},{"mode":33188,"name":"package.json","hash":"392369770e5435b42d4c355ae971e321b645827c"}]},"398a10d9fadea096f761fbe930920d2c32fa2af0":{"type":"blob","body":"{\n \"name\": \"domlog\",\n \"description\": \"A simple on-screen logger using dombuilder to create elements.\",\n \"version\": \"0.0.1\",\n \"author\": \"Tim Caswell \",\n \"main\": \"log.js\",\n \"license\": \"MIT\"\n}\n"},"9fefbd85fbffc874846f4d5c6fc875b46e718d3d":{"type":"blob","body":"{\n \"name\": \"domlog\",\n \"description\": \"A simple on-screen logger using dombuilder to create elements.\",\n \"version\": \"0.0.2\",\n \"author\": \"Tim Caswell \",\n \"main\": \"log.js\",\n \"dependencies\": {\n \"dombuilder\": \"~0.1.2\"\n },\n \"license\": \"MIT\"\n}\n"},"ffc0322e20b2302f2b67c214f29d74f21e378f71":{"type":"blob","body":"{\n \"name\": \"domlog\",\n \"description\": \"A simple on-screen logger using dombuilder to create elements.\",\n \"version\": \"0.0.3\",\n \"author\": \"Tim Caswell \",\n \"main\": \"log.js\",\n \"dependencies\": {\n \"dombuilder\": \"~0.1.2\"\n },\n \"license\": \"MIT\"\n}\n"},"b09d1d795997a5e7808d5c1de76cc4ac599e7cba":{"type":"blob","body":"{\n \"name\": \"domlog\",\n \"description\": \"A simple on-screen logger using dombuilder to create elements.\",\n \"version\": \"0.0.4\",\n \"author\": \"Tim Caswell \",\n \"main\": \"log.js\",\n \"dependencies\": {\n \"dombuilder\": \"~0.1.2\"\n },\n \"license\": \"MIT\"\n}\n"},"8a3b55c8067590ade2667b6d95e6af87dd30db9d":{"type":"blob","body":"{\n \"name\": \"domlog\",\n \"description\": \"A simple on-screen logger using dombuilder to create elements.\",\n \"version\": \"0.0.5\",\n \"author\": \"Tim Caswell \",\n \"main\": \"log.js\",\n \"dependencies\": {\n \"dombuilder\": \"~0.1.2\"\n },\n \"license\": \"MIT\"\n}\n"},"09ba8f7d00ba06c336e3a87a4b848dc64d33804b":{"type":"blob","body":"{\n \"name\": \"domlog\",\n \"description\": \"A simple on-screen logger using dombuilder to create elements.\",\n \"version\": \"0.0.6\",\n \"author\": \"Tim Caswell \",\n \"main\": \"log.js\",\n \"dependencies\": {\n \"dombuilder\": \"~0.1.2\"\n },\n \"license\": \"MIT\"\n}\n"},"392369770e5435b42d4c355ae971e321b645827c":{"type":"blob","body":"{\n \"name\": \"domlog\",\n \"description\": \"A simple on-screen logger using dombuilder to create elements.\",\n \"version\": \"0.0.7\",\n \"author\": \"Tim Caswell \",\n \"main\": \"log.js\",\n \"dependencies\": {\n \"dombuilder\": \"~0.1.2\"\n },\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git://github.com/creationix/domlog.git\"\n }\n}\n"},"f24722e9c83bbca55058fdca1a2cf00f097556a6":{"type":"blob","body":"{\n \"name\": \"domlog\",\n \"description\": \"A simple on-screen logger using dombuilder to create elements.\",\n \"version\": \"0.0.9\",\n \"author\": \"Tim Caswell \",\n \"main\": \"log.js\",\n \"dependencies\": {\n \"dombuilder\": \"~0.1.2\"\n },\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git://github.com/creationix/domlog.git\"\n }\n}\n"},"d8945fd20b86f4ac4717c1f476b007f55fd228c9":{"type":"blob","body":"\"use strict\";\n\nvar domBuilder = require('dombuilder');\n\nmodule.exports = log;\nlog.toDom = toDom;\nlog.container = undefined;\nlog.setup = setup;\nlog.css =\n \".log:hover{background:rgba(0,0,0,1);height:100%;-webkit-user-select:text;}\\n\" +\n \".log{background:rgba(0,0,0,0.7);color:#ddd;font-family:monospace;padding:0;position:absolute;left:0;right:0;bottom:0;margin:0;height:200px;overflow:auto;transition:all 1s ease-in-out}\\n\" +\n \".log .array:after{content:']'}\\n\" +\n \".log .array:before{content:'['}\\n\" +\n \".log .binary{color:#88e}\\n\" +\n \".log .binary:after{content:'>'}\\n\" +\n \".log .binary:before{content:'<'}\\n\" +\n \".log .binary:before,.log .binary:after{font-weight:bold;color:#bcf}\\n\" +\n \".log .boolean{color:#f4a}\\n\" +\n \".log .error{color:#f33;white-space:pre-wrap}\\n\" +\n \".log .function{color:#fb0}\\n\" +\n \".log .null{color:#aaa;font-weight:bold}\\n\" +\n \".log .number{color:#5cf}\\n\" +\n \".log .object .key:after{content:':';font-weight:bold}\\n\" +\n \".log .object:after{content:'}'}\\n\" +\n \".log .object:before{content:'{'}\\n\" +\n \".log .string{color:#4e2}\\n\" +\n \".log .string:after{content:'\\\\201D'}\\n\" +\n \".log .string:before{content:'\\\\201C'}\\n\" +\n \".log .string:before,.log .string:after{color:#5f3;font-weight:bold}\\n\" +\n \".log .text{color:#fff}\\n\" +\n \".log .undefined{color:#aaa}\\n\" +\n \".log > li{padding:5px}\\n\" +\n \".log > li *{display:inline-block;margin:0 3px;padding:0}\\n\";\n\nfunction toDom(val) {\n if (val === null) {\n return [\"span.null\", \"null\"];\n }\n if (Array.isArray(val)) {\n return [\"ul.array\"].concat(\n val.map(function (item) {\n return [\"li\", toDom(item)];\n })\n );\n }\n if (val instanceof Error) {\n return [\".error\", val.stack];\n }\n if (val instanceof Uint8Array) {\n var str = val.length.toString(16) + \":\";\n for (var i = 0, l = Math.min(val.length, 25); i < l; i++) {\n var c = val[i];\n if (c < 0x10) str += \"0\" + c.toString(16);\n else str += c.toString(16);\n }\n if (i < val.length) str += \"...\";\n return [\".binary\", str];\n }\n var type = typeof val;\n if (type === \"object\") {\n return [\"dl.object\"].concat(\n Object.keys(val).map(function (key) {\n return [\n [\"dt.key\", key],\n [\"dd\", toDom(val[key])]\n ];\n })\n );\n }\n if (type === \"string\") {\n val = JSON.stringify(val);\n val = val.substr(1, val.length - 2);\n return [\"span.string\", val];\n }\n var title = \"\" + val;\n val = title.split(\"\\n\");\n if (val.length > 1) {\n val = val[0] + \" ... \" + val[val.length - 1];\n }\n else {\n val = val[0];\n }\n return [\"span\", {title: title, class: type}, document.createTextNode(val)];\n}\n\nfunction setup(extra) {\n var style = document.createElement(\"style\");\n style.textContent = log.css;\n document.head.appendChild(style);\n log.container = domBuilder([\"ul.log\"]);\n if (extra) {\n for (var key in extra) {\n log.container.style[key] = extra[key];\n }\n }\n document.body.appendChild(log.container);\n}\n\nfunction item(val, i) {\n if (!i && typeof val === \"string\") return [\"span.text\", val];\n return toDom(val);\n}\n\nfunction log() {\n if (!log.container) setup();\n var child = domBuilder([\"li\"].concat(Array.prototype.map.call(arguments, item)));\n log.container.appendChild(child);\n log.container.scrollTop = child.offsetTop;\n}\n"},"b4fe4cc2c730661e86f37d8c19eb832bef93f7cc":{"type":"blob","body":"\"use strict\";\n\nvar domBuilder = require('dombuilder');\n\nmodule.exports = log;\nlog.toDom = toDom;\nlog.container = undefined;\nlog.setup = setup;\nlog.css =\n \".log:hover{background:rgba(0,0,0,1);height:100%;-webkit-user-select:text;}\\n\" +\n \".log{background:rgba(0,0,0,0.7);color:#ddd;font-family:monospace;padding:0;position:absolute;left:0;right:0;bottom:0;margin:0;height:200px;overflow:auto;transition:all 1s ease-in-out}\\n\" +\n \".log .array:after{content:']'}\\n\" +\n \".log .array:before{content:'['}\\n\" +\n \".log .binary{color:#88e}\\n\" +\n \".log .binary:after{content:'>'}\\n\" +\n \".log .binary:before{content:'<'}\\n\" +\n \".log .binary:before,.log .binary:after{font-weight:bold;color:#bcf}\\n\" +\n \".log .boolean{color:#f4a}\\n\" +\n \".log .error{color:#f33;white-space:pre-wrap}\\n\" +\n \".log .function{color:#fb0}\\n\" +\n \".log .null{color:#aaa;font-weight:bold}\\n\" +\n \".log .number{color:#5cf}\\n\" +\n \".log .hidden{opacity:0.5}\\n\" +\n \".log .obj-name{font-style:italic}\\n\" +\n \".log .object .key:after{content:':';font-weight:bold}\\n\" +\n \".log .object:after{content:'}'}\\n\" +\n \".log .object:before{content:'{'}\\n\" +\n \".log .string{color:#4e2}\\n\" +\n \".log .string:after{content:'\\\\201D'}\\n\" +\n \".log .string:before{content:'\\\\201C'}\\n\" +\n \".log .string:before,.log .string:after{color:#5f3;font-weight:bold}\\n\" +\n \".log .text{color:#fff}\\n\" +\n \".log .undefined{color:#aaa}\\n\" +\n \".log > li{padding:5px}\\n\" +\n \".log > li *{display:inline-block;margin:0 3px;padding:0}\\n\";\n\nfunction keys(obj) {\n var data = [];\n for (var key in obj) {\n data.push(key);\n }\n return data;\n}\n\nfunction toDom(val) {\n if (val === null) {\n return [\"span.null\", \"null\"];\n }\n if (Array.isArray(val)) {\n return [\"ul.array\"].concat(\n val.map(function (item) {\n return [\"li\", toDom(item)];\n })\n );\n }\n if (val instanceof Error) {\n return [\".error\", val.stack];\n }\n if (val instanceof Uint8Array) {\n var str = val.length.toString(16) + \":\";\n for (var i = 0, l = Math.min(val.length, 25); i < l; i++) {\n var c = val[i];\n if (c < 0x10) str += \"0\" + c.toString(16);\n else str += c.toString(16);\n }\n if (i < val.length) str += \"...\";\n return [\".binary\", str];\n }\n var type = typeof val;\n if (type === \"object\") {\n var name = Object.prototype.toString.call(val);\n name = name.substr(8, name.length - 9);\n var obj = [\"dl.object\"].concat(\n keys(val).map(function (key) {\n return [\n [\"dt\", {class: val.hasOwnProperty(key) ? \"key\" : \"key hidden\" }, key],\n [\"dd\", toDom(val[key])]\n ];\n })\n );\n if (name === \"Object\") return obj;\n return [[\"span.obj-name\", name], obj];\n }\n if (type === \"string\") {\n val = JSON.stringify(val);\n val = val.substr(1, val.length - 2);\n return [\"span.string\", val];\n }\n var title = \"\" + val;\n val = title.split(\"\\n\");\n if (val.length > 1) {\n val = val[0] + \" ... \" + val[val.length - 1];\n }\n else {\n val = val[0];\n }\n return [\"span\", {title: title, class: type}, document.createTextNode(val)];\n}\n\nfunction setup(extra) {\n var style = document.createElement(\"style\");\n style.textContent = log.css;\n document.head.appendChild(style);\n log.container = domBuilder([\"ul.log\"]);\n if (extra) {\n for (var key in extra) {\n log.container.style[key] = extra[key];\n }\n }\n document.body.appendChild(log.container);\n}\n\nfunction item(val, i) {\n if (!i && typeof val === \"string\") return [\"span.text\", val];\n return toDom(val);\n}\n\nfunction log() {\n if (!log.container) setup();\n var child = domBuilder([\"li\"].concat(Array.prototype.map.call(arguments, item)));\n log.container.appendChild(child);\n log.container.scrollTop = child.offsetTop;\n}\n"},"e70ae812af9c5c93be2911c4112b52dc2df2b8b0":{"type":"blob","body":"\"use strict\";\n\nvar domBuilder = require('dombuilder');\n\nmodule.exports = log;\nlog.toDom = toDom;\nlog.container = undefined;\nlog.setup = setup;\nlog.css =\n \".log:hover{background:rgba(0,0,0,1);height:100%}\\n\" +\n \".log{background:rgba(0,0,0,0.7);color:#ddd;font-family:monospace;padding:0;position:absolute;left:0;right:0;bottom:0;margin:0;height:200px;overflow:auto;transition:all 1s ease-in-out}\\n\" +\n \".log .array:after{content:']'}\\n\" +\n \".log .array:before{content:'['}\\n\" +\n \".log .binary{color:#88e}\\n\" +\n \".log .binary:after{content:'>'}\\n\" +\n \".log .binary:before{content:'<'}\\n\" +\n \".log .binary:before,.log .binary:after{font-weight:bold;color:#bcf}\\n\" +\n \".log .boolean{color:#f4a}\\n\" +\n \".log .error{color:#f33;white-space:pre-wrap}\\n\" +\n \".log .function{color:#fb0}\\n\" +\n \".log .null{color:#aaa;font-weight:bold}\\n\" +\n \".log .number{color:#5cf}\\n\" +\n \".log .object .key:after{content:':';font-weight:bold}\\n\" +\n \".log .object:after{content:'}'}\\n\" +\n \".log .object:before{content:'{'}\\n\" +\n \".log .string{color:#4e2}\\n\" +\n \".log .string:after{content:'\\\\201D'}\\n\" +\n \".log .string:before{content:'\\\\201C'}\\n\" +\n \".log .string:before,.log .string:after{color:#5f3;font-weight:bold}\\n\" +\n \".log .text{color:#fff}\\n\" +\n \".log .undefined{color:#aaa}\\n\" +\n \".log > li{padding:5px}\\n\" +\n \".log > li *{display:inline-block;margin:0 3px;padding:0}\\n\";\n\nfunction toDom(val) {\n if (val === null) {\n return [\"span.null\", \"null\"];\n }\n if (Array.isArray(val)) {\n return [\"ul.array\"].concat(\n val.map(function (item) {\n return [\"li\", toDom(item)];\n })\n );\n }\n if (val instanceof Error) {\n return [\".error\", val.stack];\n }\n if (val instanceof Uint8Array) {\n var str = val.length.toString(16) + \":\";\n for (var i = 0, l = Math.min(val.length, 25); i < l; i++) {\n var c = val[i];\n if (c < 0x10) str += \"0\" + c.toString(16);\n else str += c.toString(16);\n }\n if (i < val.length) str += \"...\";\n return [\".binary\", str];\n }\n var type = typeof val;\n if (type === \"object\") {\n return [\"dl.object\"].concat(\n Object.keys(val).map(function (key) {\n return [\n [\"dt.key\", key],\n [\"dd\", toDom(val[key])]\n ];\n })\n );\n }\n if (type === \"string\") {\n val = JSON.stringify(val);\n val = val.substr(1, val.length - 2);\n return [\"span.string\", val];\n }\n var title = \"\" + val;\n val = title.split(\"\\n\");\n if (val.length > 1) {\n val = val[0] + \" ... \" + val[val.length - 1];\n }\n else {\n val = val[0];\n }\n return [\"span\", {title: title, class: type}, document.createTextNode(val)];\n}\n\nfunction setup(extra) {\n var style = document.createElement(\"style\");\n style.textContent = log.css;\n document.head.appendChild(style);\n log.container = domBuilder([\"ul.log\"]);\n if (extra) {\n for (var key in extra) {\n log.container.style[key] = extra[key];\n }\n }\n document.body.appendChild(log.container);\n}\n\nfunction item(val) {\n if (typeof val === \"string\") return [\"span.text\", val];\n return toDom(val);\n}\n\nfunction log() {\n if (!log.container) setup();\n var child = domBuilder([\"li\"].concat(Array.prototype.map.call(arguments, item)));\n log.container.appendChild(child);\n log.container.scrollTop = child.offsetTop;\n}\n"},"5a782d5c837350f30355fda7e219d07a03d90087":{"type":"blob","body":"var domBuilder = require('dombuilder');\n\nexports.toDom = toDom;\nexports.container = undefined;\nexports.setup = setup;\nexports.log = log;\nexports.css =\n \".log{background:#222;color:#ddd;font-family:monospace;padding:0}\\n\" +\n \".log .array:after{content:']'}\\n\" +\n \".log .array:before{content:'['}\\n\" +\n \".log .boolean{color:#f4a}\\n\" +\n \".log .function{color:#fb0}\\n\" +\n \".log .null{color:#aaa;font-weight:bold}\\n\" +\n \".log .number{color:#5cf}\\n\" +\n \".log .object .key:after{content:':';font-weight:bold}\\n\" +\n \".log .object:after{content:'}'}\\n\" +\n \".log .object:before{content:'{'}\\n\" +\n \".log .string{color:#4e2}\\n\" +\n \".log .string:after{content:'\\\\201D'}\\n\" +\n \".log .string:before{content:'\\\\201C'}\\n\" +\n \".log .string:before,.log .string:after{color:#5f3;font-weight:bold}\\n\" +\n \".log .text{color:#fff}\\n\" +\n \".log .undefined{color:#aaa}\\n\" +\n \".log > li{padding:5px}\\n\" +\n \".log > li *{display:inline-block;margin:0 3px;padding:0}\\n\";\n\nfunction toDom(val) {\n if (val === null) {\n return [\"span.null\", \"null\"];\n }\n if (Array.isArray(val)) {\n return [\"ul.array\"].concat(\n val.map(function (item) {\n return [\"li\", toDom(item)];\n })\n );\n }\n var type = typeof val;\n if (type === \"object\") {\n return [\"dl.object\"].concat(\n Object.keys(val).map(function (key) {\n return [\n [\"dt.key\", key],\n [\"dd\", toDom(val[key])]\n ];\n })\n );\n }\n var title = \"\" + val;\n val = title.split(\"\\n\");\n if (val.length > 1) {\n val = val[0] + \" ... \" + val[val.length - 1];\n }\n else {\n val = val[0];\n }\n return [\"span\", {title: title, class: type}, document.createTextNode(val)];\n}\n\nfunction setup() {\n var style = document.createElement(\"style\");\n style.textContent = exports.css;\n document.head.appendChild(style);\n exports.container = domBuilder([\"ul.log\"]);\n document.body.appendChild(exports.container);\n}\n\nfunction item(val) {\n if (typeof val === \"string\") return [\"span.text\", val];\n return toDom(val);\n}\n\nfunction log() {\n if (!exports.container) setup();\n exports.container.appendChild(domBuilder([\"li\"].concat(Array.prototype.map.call(arguments, item))));\n}\n"},"65600abd74adf57ec345eb41e6aefb9930e1ba1f":{"type":"blob","body":"\"use strict\";\n\nvar domBuilder = require('dombuilder');\n\nmodule.exports = log;\nlog.toDom = toDom;\nlog.container = undefined;\nlog.setup = setup;\nlog.css =\n \".log:hover{background:rgba(0,0,0,1);height:100%}\\n\" +\n \".log{background:rgba(0,0,0,0.7);color:#ddd;font-family:monospace;padding:0;position:absolute;left:0;right:0;bottom:0;margin:0;height:200px;overflow:auto;transition:all 1s ease-in-out}\\n\" +\n \".log .array:after{content:']'}\\n\" +\n \".log .array:before{content:'['}\\n\" +\n \".log .boolean{color:#f4a}\\n\" +\n \".log .function{color:#fb0}\\n\" +\n \".log .null{color:#aaa;font-weight:bold}\\n\" +\n \".log .number{color:#5cf}\\n\" +\n \".log .object .key:after{content:':';font-weight:bold}\\n\" +\n \".log .object:after{content:'}'}\\n\" +\n \".log .object:before{content:'{'}\\n\" +\n \".log .string{color:#4e2}\\n\" +\n \".log .string:after{content:'\\\\201D'}\\n\" +\n \".log .string:before{content:'\\\\201C'}\\n\" +\n \".log .string:before,.log .string:after{color:#5f3;font-weight:bold}\\n\" +\n \".log .text{color:#fff}\\n\" +\n \".log .undefined{color:#aaa}\\n\" +\n \".log > li{padding:5px}\\n\" +\n \".log > li *{display:inline-block;margin:0 3px;padding:0}\\n\";\n\nfunction toDom(val) {\n if (val === null) {\n return [\"span.null\", \"null\"];\n }\n if (Array.isArray(val)) {\n return [\"ul.array\"].concat(\n val.map(function (item) {\n return [\"li\", toDom(item)];\n })\n );\n }\n var type = typeof val;\n if (type === \"object\") {\n return [\"dl.object\"].concat(\n Object.keys(val).map(function (key) {\n return [\n [\"dt.key\", key],\n [\"dd\", toDom(val[key])]\n ];\n })\n );\n }\n var title = \"\" + val;\n val = title.split(\"\\n\");\n if (val.length > 1) {\n val = val[0] + \" ... \" + val[val.length - 1];\n }\n else {\n val = val[0];\n }\n return [\"span\", {title: title, class: type}, document.createTextNode(val)];\n}\n\nfunction setup() {\n var style = document.createElement(\"style\");\n style.textContent = log.css;\n document.head.appendChild(style);\n log.container = domBuilder([\"ul.log\"]);\n document.body.appendChild(log.container);\n}\n\nfunction item(val) {\n if (typeof val === \"string\") return [\"span.text\", val];\n return toDom(val);\n}\n\nfunction log() {\n if (!log.container) setup();\n var child = domBuilder([\"li\"].concat(Array.prototype.map.call(arguments, item)));\n log.container.appendChild(child);\n log.container.scrollTop = child.offsetTop;\n}\n"},"34dc5c5a5f387ea6a77d5021ccdee17d7fe32916":{"type":"blob","body":"\"use strict\";\n\nvar domBuilder = require('dombuilder');\n\nmodule.exports = log;\nlog.toDom = toDom;\nlog.container = undefined;\nlog.setup = setup;\nlog.css =\n \".log:hover{background:rgba(0,0,0,1);height:100%}\\n\" +\n \".log{background:rgba(0,0,0,0.7);color:#ddd;font-family:monospace;padding:0;position:absolute;left:0;right:0;bottom:0;margin:0;height:200px;overflow:auto;transition:all 1s ease-in-out}\\n\" +\n \".log .array:after{content:']'}\\n\" +\n \".log .array:before{content:'['}\\n\" +\n \".log .boolean{color:#f4a}\\n\" +\n \".log .error{color:#f33;white-space:pre-wrap}\\n\" +\n \".log .function{color:#fb0}\\n\" +\n \".log .null{color:#aaa;font-weight:bold}\\n\" +\n \".log .number{color:#5cf}\\n\" +\n \".log .object .key:after{content:':';font-weight:bold}\\n\" +\n \".log .object:after{content:'}'}\\n\" +\n \".log .object:before{content:'{'}\\n\" +\n \".log .string{color:#4e2}\\n\" +\n \".log .string:after{content:'\\\\201D'}\\n\" +\n \".log .string:before{content:'\\\\201C'}\\n\" +\n \".log .string:before,.log .string:after{color:#5f3;font-weight:bold}\\n\" +\n \".log .text{color:#fff}\\n\" +\n \".log .undefined{color:#aaa}\\n\" +\n \".log > li{padding:5px}\\n\" +\n \".log > li *{display:inline-block;margin:0 3px;padding:0}\\n\";\n\nfunction toDom(val) {\n if (val === null) {\n return [\"span.null\", \"null\"];\n }\n if (Array.isArray(val)) {\n return [\"ul.array\"].concat(\n val.map(function (item) {\n return [\"li\", toDom(item)];\n })\n );\n }\n if (val instanceof Error) {\n return [\".error\", val.stack];\n }\n var type = typeof val;\n if (type === \"object\") {\n return [\"dl.object\"].concat(\n Object.keys(val).map(function (key) {\n return [\n [\"dt.key\", key],\n [\"dd\", toDom(val[key])]\n ];\n })\n );\n }\n var title = \"\" + val;\n val = title.split(\"\\n\");\n if (val.length > 1) {\n val = val[0] + \" ... \" + val[val.length - 1];\n }\n else {\n val = val[0];\n }\n return [\"span\", {title: title, class: type}, document.createTextNode(val)];\n}\n\nfunction setup(extra) {\n var style = document.createElement(\"style\");\n style.textContent = log.css;\n document.head.appendChild(style);\n log.container = domBuilder([\"ul.log\"]);\n if (extra) {\n for (var key in extra) {\n log.container.style[key] = extra[key];\n }\n }\n document.body.appendChild(log.container);\n}\n\nfunction item(val) {\n if (typeof val === \"string\") return [\"span.text\", val];\n return toDom(val);\n}\n\nfunction log() {\n if (!log.container) setup();\n var child = domBuilder([\"li\"].concat(Array.prototype.map.call(arguments, item)));\n log.container.appendChild(child);\n log.container.scrollTop = child.offsetTop;\n}\n"},"c47c6fa58c77191c5886649531a8db1f77c4534d":{"type":"blob","body":"\"use strict\";\n\nvar domBuilder = require('dombuilder');\n\nmodule.exports = log;\nlog.toDom = toDom;\nlog.container = undefined;\nlog.setup = setup;\nlog.css =\n \".log:hover{background:rgba(0,0,0,1);height:100%}\\n\" +\n \".log{background:rgba(0,0,0,0.7);color:#ddd;font-family:monospace;padding:0;position:absolute;left:0;right:0;bottom:0;margin:0;height:200px;overflow:auto;transition:all 1s ease-in-out}\\n\" +\n \".log .array:after{content:']'}\\n\" +\n \".log .array:before{content:'['}\\n\" +\n \".log .binary{color:#88e}\\n\" +\n \".log .binary:after{content:'>'}\\n\" +\n \".log .binary:before{content:'<'}\\n\" +\n \".log .binary:before,.log .binary:after{font-weight:bold;color:#bcf}\\n\" +\n \".log .boolean{color:#f4a}\\n\" +\n \".log .error{color:#f33;white-space:pre-wrap}\\n\" +\n \".log .function{color:#fb0}\\n\" +\n \".log .null{color:#aaa;font-weight:bold}\\n\" +\n \".log .number{color:#5cf}\\n\" +\n \".log .object .key:after{content:':';font-weight:bold}\\n\" +\n \".log .object:after{content:'}'}\\n\" +\n \".log .object:before{content:'{'}\\n\" +\n \".log .string{color:#4e2}\\n\" +\n \".log .string:after{content:'\\\\201D'}\\n\" +\n \".log .string:before{content:'\\\\201C'}\\n\" +\n \".log .string:before,.log .string:after{color:#5f3;font-weight:bold}\\n\" +\n \".log .text{color:#fff}\\n\" +\n \".log .undefined{color:#aaa}\\n\" +\n \".log > li{padding:5px}\\n\" +\n \".log > li *{display:inline-block;margin:0 3px;padding:0}\\n\";\n\nfunction toDom(val) {\n if (val === null) {\n return [\"span.null\", \"null\"];\n }\n if (Array.isArray(val)) {\n return [\"ul.array\"].concat(\n val.map(function (item) {\n return [\"li\", toDom(item)];\n })\n );\n }\n if (val instanceof Error) {\n return [\".error\", val.stack];\n }\n if (val instanceof Uint8Array) {\n var str = val.length.toString(16) + \":\";\n for (var i = 0, l = Math.min(val.length, 25); i < l; i++) {\n var c = val[i];\n if (c < 0x10) str += \"0\" + c.toString(16);\n else str += c.toString(16);\n }\n if (i < val.length) str += \"...\";\n return [\".binary\", str];\n }\n var type = typeof val;\n if (type === \"object\") {\n return [\"dl.object\"].concat(\n Object.keys(val).map(function (key) {\n return [\n [\"dt.key\", key],\n [\"dd\", toDom(val[key])]\n ];\n })\n );\n }\n var title = \"\" + val;\n val = title.split(\"\\n\");\n if (val.length > 1) {\n val = val[0] + \" ... \" + val[val.length - 1];\n }\n else {\n val = val[0];\n }\n return [\"span\", {title: title, class: type}, document.createTextNode(val)];\n}\n\nfunction setup(extra) {\n var style = document.createElement(\"style\");\n style.textContent = log.css;\n document.head.appendChild(style);\n log.container = domBuilder([\"ul.log\"]);\n if (extra) {\n for (var key in extra) {\n log.container.style[key] = extra[key];\n }\n }\n document.body.appendChild(log.container);\n}\n\nfunction item(val) {\n if (typeof val === \"string\") return [\"span.text\", val];\n return toDom(val);\n}\n\nfunction log() {\n if (!log.container) setup();\n var child = domBuilder([\"li\"].concat(Array.prototype.map.call(arguments, item)));\n log.container.appendChild(child);\n log.container.scrollTop = child.offsetTop;\n}\n"}}} -------------------------------------------------------------------------------- /repos/grab.js: -------------------------------------------------------------------------------- 1 | require('js-git/lib/platform.js')(require('js-git-node-platform')); 2 | var db = require('../app/git-memdb.js')(); 3 | 4 | // Load the libraries 5 | var wrap = require('js-git/lib/repo.js'); 6 | var autoProto = require('js-git/protocols/auto.js'); 7 | var urlParse = require('url').parse; 8 | var serial = require('js-git/helpers/serial.js'); 9 | var parallel = require('js-git/helpers/parallel.js'); 10 | var parallelData = require('js-git/helpers/parallel-data.js'); 11 | var decoders = require('js-git/lib/decode.js'); 12 | 13 | var url = process.argv[2] || "git://github.com/creationix/conquest.git"; 14 | var opts = urlParse(url); 15 | if (!opts.protocol) { 16 | opts = urlParse("ssh://" + url); 17 | } 18 | var path = opts.pathname.match(/[^\/]*$/)[0].replace(/git$/, "json"); 19 | 20 | var connection = autoProto(opts); 21 | var repo = wrap(db, true); 22 | 23 | var config = { 24 | includeTag: true, 25 | onProgress: function (data) { 26 | process.stdout.write(data); 27 | }, 28 | onError: function (data) { 29 | process.stderr.write(data); 30 | } 31 | }; 32 | 33 | parallelData({ 34 | init: repo.init(), 35 | pack: connection.fetch(config), 36 | }, function (err, result) { 37 | if (err) throw err; 38 | serial( 39 | parallel( 40 | repo.importRefs(result.pack.refs), 41 | repo.unpack(result.pack, config) 42 | ), 43 | connection.close() 44 | )(function (err) { 45 | if (err) throw err; 46 | var refs = {}; 47 | var objects = {}; 48 | var isHash = /^[0-9a-f]{40}$/; 49 | Object.keys(db.db).forEach(function (key) { 50 | var value = db.db[key]; 51 | if (!isHash.test(key)) { 52 | return refs[key] = value; 53 | } 54 | var body = decoders[value.type](value.body); 55 | if (Buffer.isBuffer(body)) body = body.toString('binary'); 56 | objects[key] = { 57 | type: value.type, 58 | body: body 59 | }; 60 | }); 61 | console.error("Writing " + path); 62 | require('fs').writeFile(path, JSON.stringify({ 63 | refs: refs, 64 | objects: objects 65 | })); 66 | }); 67 | }); 68 | -------------------------------------------------------------------------------- /repos/mine.json: -------------------------------------------------------------------------------- 1 | {"refs":{"refs/heads/master":"e37c5b9a28c8a59386d58253235aaa4b383297e9"},"objects":{"e37c5b9a28c8a59386d58253235aaa4b383297e9":{"type":"commit","body":{"tree":"89f1e01e4c0f9b3b1d8b734e2c0187e1860bb092","author":"Tim Caswell 1367448117 -0500","committer":"Tim Caswell 1367448117 -0500","message":"Initial Commit\n"}},"89f1e01e4c0f9b3b1d8b734e2c0187e1860bb092":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391"},{"mode":33188,"name":"mine.js","hash":"54c75f921162128901ea556e10d06130e21f045c"},{"mode":33188,"name":"package.json","hash":"3d7367dee9a0785db04c38051bb38242051623f7"}]},"e69de29bb2d1d6434b8b29ae775ad8c2e48c5391":{"type":"blob","body":""},"54c75f921162128901ea556e10d06130e21f045c":{"type":"blob","body":"// Mine a string for require calls and export the module names\n// Extract all require calls using a proper state-machine parser.\nmodule.exports = function mine(js) {\n var names = [];\n var state = 0;\n var ident;\n var quote;\n var name;\n var states = [\n // 0 - START\n function (char) {\n if (char === \"/\") state = 6;\n else if (char === \"'\" || char === '\"') {\n quote = char;\n state = 4;\n }\n else if (char === \"r\") {\n ident = char;\n state = 1;\n }\n },\n // 1 - IDENT\n function (char) {\n if (char === \"require\"[ident.length]) {\n ident += char;\n }\n else if (char === \"(\" && ident === \"require\") {\n ident = undefined;\n state = 2;\n }\n else {\n state = 0;\n }\n },\n // 2 - CALL\n function (char) {\n if (char === \"'\" || char === '\"') {\n quote = char;\n name = \"\";\n state = 3;\n }\n else {\n state = 0;\n }\n },\n // 3 - NAME\n function (char) {\n if (char === quote) {\n names.push(name);\n name = undefined;\n state = 0;\n }\n else {\n name += char;\n }\n },\n // 4 - STRING\n function (char) {\n if (char === \"\\\\\") {\n state = 5;\n }\n else if (char === quote) {\n state = 0;\n }\n },\n // 5 - ESCAPE\n function (char) {\n state = 4;\n },\n // 6 - SLASH\n function (char) {\n if (char === \"/\") state = 7;\n else if (char === \"*\") state = 8;\n else state = 0;\n },\n // 7 - LINE_COMMENT\n function (char) {\n if (char === \"\\r\" || char === \"\\n\") state = 0;\n },\n // 8 - MULTILINE_COMMENT\n function (char) {\n if (char === \"*\") state = 9;\n },\n // 9 - MULTILINE_ENDING\n function (char) {\n if (char === \"/\") state = 0;\n else if (char !== \"*\") state = 8;\n }\n ];\n for (var i = 0, l = js.length; i < l; i++) {\n states[state](js[i]);\n }\n return names;\n}\n"},"3d7367dee9a0785db04c38051bb38242051623f7":{"type":"blob","body":"{\n \"name\": \"mine\",\n \"version\": \"0.0.0\",\n \"description\": \"Digs into a javascript file looking for require calls. Used to statically extract common js dependencies.\",\n \"main\": \"mine.js\",\n \"scripts\": {\n \"test\": \"echo \\\"Error: no test specified\\\" && exit 1\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git://github.com/creationix/miner.git\"\n },\n \"author\": \"Tim Caswell \",\n \"license\": \"MIT\",\n \"readmeFilename\": \"README.md\"\n}\n"}}} -------------------------------------------------------------------------------- /repos/push-to-pull.json: -------------------------------------------------------------------------------- 1 | {"refs":{"refs/heads/master":"f1c77934ba9f24ffaad0e22f7a58c9cfcb70fdef","refs/tags/0.0.1":"77e6a69ef39c7490de0f59e866f61cc316fa4923","refs/tags/0.0.2":"3857a9a2d4fe5d622c5d17d921a8729b248bb4aa","refs/tags/0.0.3":"495a64731cd05725c4a94990e454e79876537b4d","refs/tags/0.1.0":"f1c77934ba9f24ffaad0e22f7a58c9cfcb70fdef"},"objects":{"f1c77934ba9f24ffaad0e22f7a58c9cfcb70fdef":{"type":"commit","body":{"tree":"ed0b0419a734372c7a08dd0ec8f42b15e504fcbd","parents":["495a64731cd05725c4a94990e454e79876537b4d"],"author":"Tim Caswell 1373154309 -0500","committer":"Tim Caswell 1373154309 -0500","message":"Simplify interface error handling. No error inputs and throw on output\n"}},"495a64731cd05725c4a94990e454e79876537b4d":{"type":"commit","body":{"tree":"4fbb831ac5e49cc8f3a7f7ecf93c3b734b6b6246","parents":["5c25a9c1cd82a2d0f00ec470885eb12cd21356ac"],"author":"Tim Caswell 1373047549 -0500","committer":"Tim Caswell 1373047549 -0500","message":"Forward extra arguments through\n"}},"3857a9a2d4fe5d622c5d17d921a8729b248bb4aa":{"type":"commit","body":{"tree":"f1e395c42a71f2d4a46cb3ff0f518a63f681a0c7","parents":["77e6a69ef39c7490de0f59e866f61cc316fa4923"],"author":"Tim Caswell 1372881793 -0500","committer":"Tim Caswell 1372881793 -0500","message":"Optimize transform to not use readQueue\n"}},"77e6a69ef39c7490de0f59e866f61cc316fa4923":{"type":"commit","body":{"tree":"6b488e71525bf198b7f1494ca07f2646ae8f80c9","parents":["a31b6bc8d1233fc30a0aef6ad985b4444856962c"],"author":"Tim Caswell 1372818857 -0500","committer":"Tim Caswell 1372818857 -0500","message":"Package for npm\n"}},"5c25a9c1cd82a2d0f00ec470885eb12cd21356ac":{"type":"commit","body":{"tree":"4bafa0a389460c29db41e254e4f5107be80355ad","parents":["3857a9a2d4fe5d622c5d17d921a8729b248bb4aa"],"author":"Tim Caswell 1372882043 -0500","committer":"Tim Caswell 1372882043 -0500","message":"Add docs\n"}},"a31b6bc8d1233fc30a0aef6ad985b4444856962c":{"type":"commit","body":{"tree":"25813ca237736876e980afc3bc4cd84a26927c3c","parents":["84ba12b8c4696008a5d47b0d551fa9a1ead4eee3"],"author":"Tim Caswell 1372795963 -0500","committer":"Tim Caswell 1372795963 -0500","message":"Add initial implementation\n"}},"84ba12b8c4696008a5d47b0d551fa9a1ead4eee3":{"type":"commit","body":{"tree":"4444e64be433eb4bbf182787d69ea71edcdb0ff4","author":"Tim Caswell 1372553247 -0700","committer":"Tim Caswell 1372553247 -0700","message":"Initial commit\n"}},"ed0b0419a734372c7a08dd0ec8f42b15e504fcbd":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"d6ce7cc3df54eb0a42f3ea264c0a449c1d013fba"},{"mode":33188,"name":"package.json","hash":"cca62655d422b7e98dc570f752e84936bedf41df"},{"mode":33188,"name":"test.js","hash":"9c905cd71f4f0bec271a77dafce3441bf20463c6"},{"mode":33188,"name":"transform.js","hash":"b3000e07893a03763f87f05c424b496c07d815c4"}]},"4bafa0a389460c29db41e254e4f5107be80355ad":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"6d476138fcc2ded7d05a4ca027d9d2cf50c41e67"},{"mode":33188,"name":"package.json","hash":"b13507592ded2b5ee6285d189b1e5c792cbaf69c"},{"mode":33188,"name":"test.js","hash":"59d5eb3f5c35d86ec6b397ef1b4bcc119763832b"},{"mode":33188,"name":"transform.js","hash":"fc3b27f0f702f67c068d2ee85d15e8fb8eeb2081"}]},"6b488e71525bf198b7f1494ca07f2646ae8f80c9":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"60fc73d687d0935a8d739f3991ad91d20dcb9ed6"},{"mode":33188,"name":"package.json","hash":"3f521cc0326658c3e4ac8a78a91008a5487a0f5e"},{"mode":33188,"name":"test.js","hash":"59d5eb3f5c35d86ec6b397ef1b4bcc119763832b"},{"mode":33188,"name":"transform.js","hash":"a8b92759734e3ce720c7372e23ca76d77a412a66"}]},"4444e64be433eb4bbf182787d69ea71edcdb0ff4":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"60fc73d687d0935a8d739f3991ad91d20dcb9ed6"}]},"6d476138fcc2ded7d05a4ca027d9d2cf50c41e67":{"type":"blob","body":"push-to-pull\n============\n\nConvert a push-filter to a pull-filter (for simple streams)\n\nUsage is simple. Define your filter as an `(emit) -> emit` transform where `emit` is `(err, item)`. Then when you need an `(stream) -> stream` filter, use this module to convert it.\n\n```js\nvar pushToPull = require('push-to-pull');\n\n// Dumb filter that inputs numbers and outputs that many monkeys\n// Encoded as an (emit) -> emit transform filter.\nfunction pushFilter(emit) {\n return function (err, item) {\n if (item === undefined) return emit(err);\n for (var i = 1; i <= item; i++) {\n emit(null, \"MONKEY \" + i);\n } \n }\n}\n\n// Same filter, but as a (stream) -> stream transform filter\nvar pullFilter = pushToPull(pushFilter);\n```\n"},"e29ae3f19b5db780c90bf1a0cc2a58f02383d0af":{"type":"blob","body":"{\n \"name\": \"push-to-pull\",\n \"version\": \"0.0.3\",\n \"description\": \"Convert a push-filter to a pull-filter (for simple streams)\",\n \"main\": \"transform.js\",\n \"scripts\": {\n \"test\": \"node test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git://github.com/creationix/push-to-pull.git\"\n },\n \"keywords\": [\n \"simple-stream\",\n \"push-filter\",\n \"stream\"\n ],\n \"author\": \"Tim Caswell \",\n \"license\": \"MIT\",\n \"readmeFilename\": \"README.md\",\n \"gitHead\": \"a31b6bc8d1233fc30a0aef6ad985b4444856962c\",\n \"bugs\": {\n \"url\": \"https://github.com/creationix/push-to-pull/issues\"\n }\n}\n"},"59d5eb3f5c35d86ec6b397ef1b4bcc119763832b":{"type":"blob","body":"var pushToPull = require('./.');\n\n// Create a stream that counts from 1 to infinity and emits the numbers after a\n// short delay specefied by `ms`.\n\n// source(ms) -> stream\nfunction source(ms) {\n var i = 0;\n var done = false;\n return { read: read, abort: abort };\n \n function read(callback) {\n if (done) return callback();\n var n = ++i;\n setTimeout(function () {\n callback(null, n);\n }, ms);\n }\n \n function abort(callback) {\n done = true;\n callback();\n }\n}\n\n// Consume a stream for at least num items and then tell upstream we're done\n// with it. Wait for the end event and report the events in a continuable.\n// sink(stream, maxNum) -> continuable\nfunction sink(stream, num) {\n var items = [];\n\n var finish;\n return function (callback) {\n var done = false;\n // Hide the real callback so that it can only be called once.\n finish = function (err, items) {\n if (done) return;\n done = true;\n callback(err, items);\n };\n stream.read(onRead);\n };\n \n function onRead(err, item) {\n // When the end is reached, resolve the continuable\n if (item === undefined) return finish(err, items);\n // Check to see if we've got enough\n if (items.length >= num) {\n stream.abort(onAbort);\n return finish(null, items);\n }\n else {\n items.push(item);\n stream.read(onRead);\n }\n }\n \n function onAbort(err) {\n if (err) return finish(err);\n }\n}\n\nvar filter = pushToPull(function (emit) {\n return function (err, item) {\n if (item === undefined) return emit(err);\n emit(null, item);\n emit(null, \"extra\");\n }\n});\n\nvar stream = source(16);\nstream = filter(stream);\nsink(stream, 10)(function (err, items) {\n if (err) throw err;\n console.log(items);\n});"},"b3000e07893a03763f87f05c424b496c07d815c4":{"type":"blob","body":"// input push-filter: (emit) -> emit\n// output is simple-stream pull-filter: (stream) -> stream\nmodule.exports = pushToPull;\nfunction pushToPull(parser) {\n return function (stream) {\n \n var write = parser(onData);\n var cb = null;\n var queue = [];\n \n return { read: read, abort: stream.abort };\n \n function read(callback) {\n if (queue.length) return callback(null, queue.shift());\n if (cb) return callback(new Error(\"Only one read at a time.\"));\n cb = callback;\n stream.read(onRead);\n \n }\n\n function onRead(err, item) {\n var callback = cb;\n cb = null;\n if (err) return callback(err);\n try {\n write(item);\n }\n catch (err) {\n return callback(err);\n }\n return read(callback);\n }\n\n function onData(item) {\n queue.push(item);\n }\n\n };\n}\n"},"fc3b27f0f702f67c068d2ee85d15e8fb8eeb2081":{"type":"blob","body":"// input push-filter: (emit) -> emit\n// output is simple-stream pull-filter: (stream) -> stream\nmodule.exports = pushToPull;\nfunction pushToPull(pushFilter) {\n return function (stream) {\n var queue = [];\n var output = null;\n var done = false;\n\n var emit = pushFilter(onEmit);\n\n return { read: read, abort: stream.abort };\n\n function read(callback) {\n if (done) return callback();\n if (queue.length) {\n return callback.apply(null, queue.shift());\n }\n if (output) return callback(new Error(\"Only one read allowed at a time\"));\n output = callback;\n stream.read(emit);\n }\n\n function onEmit(err, item) {\n if (output) {\n var callback = output;\n output = null;\n callback(err, item);\n }\n else {\n queue.push(arguments);\n }\n }\n\n };\n}\n"},"a8b92759734e3ce720c7372e23ca76d77a412a66":{"type":"blob","body":"// input push-filter: (emit) -> emit\n// output is simple-stream pull-filter: (stream) -> stream\nmodule.exports = pushToPull;\nfunction pushToPull(pushFilter) {\n return function (stream) {\n var dataQueue = [];\n var readQueue = [];\n var reading = false;\n var done = false;\n\n var emit = pushFilter(onEmit);\n\n return { read: read, abort: stream.abort };\n\n function read(callback) {\n if (done) return callback();\n readQueue.push(callback);\n check();\n }\n\n function check() {\n while (readQueue.length && dataQueue.length) {\n var data = dataQueue.shift();\n readQueue.shift().apply(null, data);\n if (data[1] === undefined) done = true;\n }\n\n while (done && readQueue.length) {\n readQueue.shift()();\n }\n\n if (reading || !readQueue.length) return;\n reading = true;\n stream.read(onRead);\n }\n\n function onRead(err, item) {\n reading = false;\n emit(err, item);\n check();\n }\n\n function onEmit() {\n dataQueue.push(arguments);\n check();\n }\n\n };\n}\n"},"25813ca237736876e980afc3bc4cd84a26927c3c":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"60fc73d687d0935a8d739f3991ad91d20dcb9ed6"},{"mode":33188,"name":"transform.js","hash":"a8b92759734e3ce720c7372e23ca76d77a412a66"}]},"f1e395c42a71f2d4a46cb3ff0f518a63f681a0c7":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"60fc73d687d0935a8d739f3991ad91d20dcb9ed6"},{"mode":33188,"name":"package.json","hash":"b13507592ded2b5ee6285d189b1e5c792cbaf69c"},{"mode":33188,"name":"test.js","hash":"59d5eb3f5c35d86ec6b397ef1b4bcc119763832b"},{"mode":33188,"name":"transform.js","hash":"fc3b27f0f702f67c068d2ee85d15e8fb8eeb2081"}]},"0a97fffc65ac2d079dbebf8617681ebe6ad583d3":{"type":"blob","body":"// input push-filter: (emit) -> emit\n// output is simple-stream pull-filter: (stream) -> stream\nmodule.exports = pushToPull;\nfunction pushToPull(pushFilter) {\n return function (stream) {\n var extras = Array.prototype.slice.call(arguments, 1);\n var queue = [];\n var output = null;\n var done = false;\n\n var emit = pushFilter.apply(null, [onEmit].concat(extras));\n\n return { read: read, abort: stream.abort };\n\n function read(callback) {\n if (done) return callback();\n if (queue.length) {\n return callback.apply(null, queue.shift());\n }\n if (output) return callback(new Error(\"Only one read allowed at a time\"));\n output = callback;\n stream.read(emit);\n }\n\n function onEmit(err, item) {\n if (output) {\n var callback = output;\n output = null;\n callback(err, item);\n }\n else {\n queue.push(arguments);\n }\n }\n\n };\n}\n"},"4fbb831ac5e49cc8f3a7f7ecf93c3b734b6b6246":{"type":"tree","body":[{"mode":33188,"name":"README.md","hash":"6d476138fcc2ded7d05a4ca027d9d2cf50c41e67"},{"mode":33188,"name":"package.json","hash":"e29ae3f19b5db780c90bf1a0cc2a58f02383d0af"},{"mode":33188,"name":"test.js","hash":"59d5eb3f5c35d86ec6b397ef1b4bcc119763832b"},{"mode":33188,"name":"transform.js","hash":"0a97fffc65ac2d079dbebf8617681ebe6ad583d3"}]},"9c905cd71f4f0bec271a77dafce3441bf20463c6":{"type":"blob","body":"var pushToPull = require('./.');\n\n// Create a stream that counts from 1 to infinity and emits the numbers after a\n// short delay specefied by `ms`.\n\n// source(ms) -> stream\nfunction source(ms) {\n var i = 0;\n var done = false;\n return { read: read, abort: abort };\n \n function read(callback) {\n if (done) return callback();\n var n = ++i;\n setTimeout(function () {\n callback(null, n);\n }, ms);\n }\n \n function abort(callback) {\n done = true;\n callback();\n }\n}\n\n// Consume a stream for at least num items and then tell upstream we're done\n// with it. Wait for the end event and report the events in a continuable.\n// sink(stream, maxNum) -> continuable\nfunction sink(stream, num) {\n var items = [];\n\n var finish;\n return function (callback) {\n var done = false;\n // Hide the real callback so that it can only be called once.\n finish = function (err, items) {\n if (done) return;\n done = true;\n callback(err, items);\n };\n stream.read(onRead);\n };\n \n function onRead(err, item) {\n // When the end is reached, resolve the continuable\n if (item === undefined) return finish(err, items);\n // Check to see if we've got enough\n if (items.length >= num) {\n stream.abort(onAbort);\n return finish(null, items);\n }\n else {\n items.push(item);\n stream.read(onRead);\n }\n }\n \n function onAbort(err) {\n if (err) return finish(err);\n }\n}\n\nvar filter = pushToPull(function (emit) {\n return function (item) {\n if (item === undefined) return emit();\n emit(item);\n emit(\"extra\");\n }\n});\n\nvar stream = source(16);\nstream = filter(stream);\nsink(stream, 10)(function (err, items) {\n if (err) throw err;\n console.log(items);\n});"},"3f521cc0326658c3e4ac8a78a91008a5487a0f5e":{"type":"blob","body":"{\n \"name\": \"push-to-pull\",\n \"version\": \"0.0.1\",\n \"description\": \"Convert a push-filter to a pull-filter (for simple streams)\",\n \"main\": \"transform.js\",\n \"scripts\": {\n \"test\": \"node test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git://github.com/creationix/push-to-pull.git\"\n },\n \"keywords\": [\n \"simple-stream\",\n \"push-filter\",\n \"stream\"\n ],\n \"author\": \"Tim Caswell \",\n \"license\": \"MIT\",\n \"readmeFilename\": \"README.md\",\n \"gitHead\": \"a31b6bc8d1233fc30a0aef6ad985b4444856962c\",\n \"bugs\": {\n \"url\": \"https://github.com/creationix/push-to-pull/issues\"\n }\n}\n"},"b13507592ded2b5ee6285d189b1e5c792cbaf69c":{"type":"blob","body":"{\n \"name\": \"push-to-pull\",\n \"version\": \"0.0.2\",\n \"description\": \"Convert a push-filter to a pull-filter (for simple streams)\",\n \"main\": \"transform.js\",\n \"scripts\": {\n \"test\": \"node test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git://github.com/creationix/push-to-pull.git\"\n },\n \"keywords\": [\n \"simple-stream\",\n \"push-filter\",\n \"stream\"\n ],\n \"author\": \"Tim Caswell \",\n \"license\": \"MIT\",\n \"readmeFilename\": \"README.md\",\n \"gitHead\": \"a31b6bc8d1233fc30a0aef6ad985b4444856962c\",\n \"bugs\": {\n \"url\": \"https://github.com/creationix/push-to-pull/issues\"\n }\n}\n"},"cca62655d422b7e98dc570f752e84936bedf41df":{"type":"blob","body":"{\n \"name\": \"push-to-pull\",\n \"version\": \"0.1.0\",\n \"description\": \"Convert a push-filter to a pull-filter (for simple streams)\",\n \"main\": \"transform.js\",\n \"scripts\": {\n \"test\": \"node test.js\"\n },\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"git://github.com/creationix/push-to-pull.git\"\n },\n \"keywords\": [\n \"simple-stream\",\n \"push-filter\",\n \"stream\"\n ],\n \"author\": \"Tim Caswell \",\n \"license\": \"MIT\",\n \"readmeFilename\": \"README.md\",\n \"gitHead\": \"a31b6bc8d1233fc30a0aef6ad985b4444856962c\",\n \"bugs\": {\n \"url\": \"https://github.com/creationix/push-to-pull/issues\"\n }\n}\n"},"60fc73d687d0935a8d739f3991ad91d20dcb9ed6":{"type":"blob","body":"push-to-pull\n============\n\nConvert a push-filter to a pull-filter (for simple streams)\n"},"d6ce7cc3df54eb0a42f3ea264c0a449c1d013fba":{"type":"blob","body":"push-to-pull\n============\n\nConvert a push-filter to a pull-filter (for simple streams)\n\nUsage is simple. Define your filter as an `(emit) -> emit` transform where `emit` is `(err, item)`. Then when you need an `(stream) -> stream` filter, use this module to convert it.\n\n```js\nvar pushToPull = require('push-to-pull');\n\n// Dumb filter that inputs numbers and outputs that many monkeys\n// Encoded as an (emit) -> emit transform filter.\nfunction pushFilter(emit) {\n return function (item) {\n if (item === undefined) return emit();\n for (var i = 1; i <= item; i++) {\n emit(\"MONKEY \" + i);\n } \n }\n}\n\n// Same filter, but as a (stream) -> stream transform filter\nvar pullFilter = pushToPull(pushFilter);\n```\n"}}} -------------------------------------------------------------------------------- /repos/uvrun.json: -------------------------------------------------------------------------------- 1 | {"refs":{"refs/heads/master":"aac4f31506eca0cda537f8575080e919363081a2","refs/tags/0.1.0":"3b55b2beaf2625b07bcfeddebcc8078f46dfc0bf","refs/tags/0.1.1":"e703e8216f07e2ec1a025c4d78359c8b7b528f88"},"objects":{"aac4f31506eca0cda537f8575080e919363081a2":{"type":"commit","body":{"tree":"c77ccbbee877f8b5579e6b65f9b755c04d192d70","parents":["895f71390087b5a1ab38994c7f370437be4d0c18"],"author":"Tim Caswell 1369845035 -0400","committer":"Tim Caswell 1369845035 -0400","message":"Fix json error\n"}},"895f71390087b5a1ab38994c7f370437be4d0c18":{"type":"commit","body":{"tree":"9177076585c82e0dd7e075b2562515dbcf0b0b41","parents":["e703e8216f07e2ec1a025c4d78359c8b7b528f88"],"author":"Tim Caswell 1369844993 -0400","committer":"Tim Caswell 1369844993 -0400","message":"Fix json error\n"}},"e703e8216f07e2ec1a025c4d78359c8b7b528f88":{"type":"commit","body":{"tree":"81bec946b8edd147a2662e6e702df9d3726eafb5","parents":["1eec793a62e9bb72e84f4776facf265facdaeedc"],"author":"Tim Caswell 1369844965 -0400","committer":"Tim Caswell 1369844965 -0400","message":"Bump version to 0.1.1\n"}},"3b55b2beaf2625b07bcfeddebcc8078f46dfc0bf":{"type":"commit","body":{"tree":"b29ac192d4c2c714a40933144809ddfe2d013148","parents":["fed9931ed15cf3f42623098b1ac2c5b8eb8201d4"],"author":"Tim Caswell 1366149551 -0500","committer":"Tim Caswell 1366149551 -0500","message":"Bump version to 0.1.0\n"}},"1eec793a62e9bb72e84f4776facf265facdaeedc":{"type":"commit","body":{"tree":"3d001e14dc81ec3c79dda5e8b0a56d8197906504","parents":["3b55b2beaf2625b07bcfeddebcc8078f46dfc0bf"],"author":"Tim Caswell 1369844944 -0400","committer":"Tim Caswell 1369844944 -0400","message":"Add README and repo link\n"}},"fed9931ed15cf3f42623098b1ac2c5b8eb8201d4":{"type":"commit","body":{"tree":"9b8d89fdde59abff8bd37ffd16fc0a19d8dac566","parents":["31e28adeb8a4ac49343234ccf8da7d3834eaba23"],"author":"Andrew Martens 1366146216 -0700","committer":"Andrew Martens 1366146216 -0700","message":"Updated for node v0.10 - uv_run method signature changed and uv_run_once was removed\n"}},"31e28adeb8a4ac49343234ccf8da7d3834eaba23":{"type":"commit","body":{"tree":"ccbcf5a7a3d1852bf2bfeb7609b831f993f5dc12","author":"Tim Caswell 1357073829 -0600","committer":"Tim Caswell 1357073829 -0600","message":"Initial commit\n"}},"81bec946b8edd147a2662e6e702df9d3726eafb5":{"type":"tree","body":[{"mode":33188,"name":".gitignore","hash":"378eac25d311703f3f2cd456d8036da525cd0366"},{"mode":33188,"name":"README.md","hash":"80797b0d8cc2f63853fc459ead02dcf7ea191192"},{"mode":33188,"name":"binding.gyp","hash":"ade921740b2ab7abc873c2257b61a02804fc6d25"},{"mode":33188,"name":"package.json","hash":"1e830613263da2985748dfa109f3a6437afa8922"},{"mode":33188,"name":"uvrun.cc","hash":"37e58218d378903063edc474e98a7eaa5592e16a"}]},"378eac25d311703f3f2cd456d8036da525cd0366":{"type":"blob","body":"build\n"},"80797b0d8cc2f63853fc459ead02dcf7ea191192":{"type":"blob","body":"# UVRun\n\nBindings to the uvrun functions in libuv to node.js.\n\nNormally these functions are implicitly called by node itself.\n\nBy using runOnce, you can have finer grained control of the event loop and know when it's idle.\n\n```js\nvar runOnce = require('uvrun').runOnce;\n \n// Do something here, like make a server to keep the event loop busy\nvar TCP = process.binding('tcp_wrap').TCP;\nvar server = new TCP();\nserver.onconnection = function () {\n console.log(\"connection!\");\n};\nserver.bind(\"0.0.0.0\", 3000);\nserver.listen(511);\n \n// Visualize each event loop tick using a custom event loop.\nconsole.log(\"Waiting for events...\");\ndo {\n var ret = runOnce();\n console.log(\"tick\", Date.now());\n} while(ret);\n// If the code gets here, there are no events left and node's built-in uv_run won't block.\n```\n"},"ade921740b2ab7abc873c2257b61a02804fc6d25":{"type":"blob","body":"{\n \"targets\": [\n {\n \"target_name\": \"uvrun\",\n \"sources\": [\"uvrun.cc\"],\n \"variables\": {\n \"node_version\": '\",\n \"main\": \"build/Release/obj.target/uvrun.node\",\n { \n \"type\" : \"git\"\n \"url\" : \"http://github.com/creationix/uvrun\"\n }\n}\n"},"37e58218d378903063edc474e98a7eaa5592e16a":{"type":"blob","body":"#include \n#include \n#include \n\nusing namespace v8;\n\nHandle Run(const Arguments& args) {\n HandleScope scope;\n#ifdef OLD_UV_RUN_SIGNATURE\n uv_run(uv_default_loop());\n#else\n uv_run(uv_default_loop(), UV_RUN_DEFAULT);\n#endif\n return scope.Close(Null());\n}\n\nHandle RunOnce(const Arguments& args) {\n HandleScope scope;\n#ifdef OLD_UV_RUN_SIGNATURE\n int r = uv_run_once(uv_default_loop());\n#else\n int r = uv_run(uv_default_loop(), UV_RUN_ONCE);\n#endif\n return scope.Close(Number::New(r));\n}\n\nvoid init(Handle target) {\n NODE_SET_METHOD(target, \"run\", Run);\n NODE_SET_METHOD(target, \"runOnce\", RunOnce);\n}\n\nNODE_MODULE(uvrun, init);\n"},"cd6b74a2ac53029d9b3e12d5fdb52898b6c32b6a":{"type":"blob","body":"{\n \"targets\": [\n {\n \"target_name\": \"uvrun\",\n \"sources\": [\"uvrun.cc\"]\n }\n ]\n}"},"553f34aecdb86c4e8a5984f4b9f06bd5ad7f3891":{"type":"blob","body":"#include \n#include \n#include \n\nusing namespace v8;\n\nHandle Run(const Arguments& args) {\n HandleScope scope;\n uv_run(uv_default_loop());\n return scope.Close(Null());\n}\n\nHandle RunOnce(const Arguments& args) {\n HandleScope scope;\n int r = uv_run_once(uv_default_loop());\n return scope.Close(Number::New(r));\n}\n\nvoid init(Handle target) {\n NODE_SET_METHOD(target, \"run\", Run);\n NODE_SET_METHOD(target, \"runOnce\", RunOnce);\n}\n\nNODE_MODULE(uvrun, init);"},"6bda9151960a8e030668de34c3ba0a9b79287775":{"type":"blob","body":"{\n \"name\": \"uvrun\",\n \"version\": \"0.0.0\",\n \"author\": \"Tim Caswell \",\n \"main\": \"build/Release/obj.target/uvrun.node\"\n}"},"faa7e89c129cf79cf34d4aabefbf41e3f16bfb90":{"type":"blob","body":"{\n \"name\": \"uvrun\",\n \"version\": \"0.1.0\",\n \"author\": \"Tim Caswell \",\n \"main\": \"build/Release/obj.target/uvrun.node\",\n { \n \"type\" : \"git\"\n \"url\" : \"http://github.com/creationix/uvrun\"\n }\n}\n"},"977d55b88bc49d7f48f241b58e83f40f2b4b3487":{"type":"blob","body":"{\n \"name\": \"uvrun\",\n \"version\": \"0.1.1\",\n \"author\": \"Tim Caswell \",\n \"main\": \"build/Release/obj.target/uvrun.node\",\n { \n \"type\" : \"git\",\n \"url\" : \"http://github.com/creationix/uvrun\"\n }\n}\n"},"e7808a39479632425e9eaa3f03f9154be5a63ab0":{"type":"blob","body":"{\n \"name\": \"uvrun\",\n \"version\": \"0.1.1\",\n \"author\": \"Tim Caswell \",\n \"main\": \"build/Release/obj.target/uvrun.node\",\n \"repository\": { \n \"type\": \"git\",\n \"url\": \"http://github.com/creationix/uvrun\"\n }\n}\n"},"3d001e14dc81ec3c79dda5e8b0a56d8197906504":{"type":"tree","body":[{"mode":33188,"name":".gitignore","hash":"378eac25d311703f3f2cd456d8036da525cd0366"},{"mode":33188,"name":"README.md","hash":"80797b0d8cc2f63853fc459ead02dcf7ea191192"},{"mode":33188,"name":"binding.gyp","hash":"ade921740b2ab7abc873c2257b61a02804fc6d25"},{"mode":33188,"name":"package.json","hash":"faa7e89c129cf79cf34d4aabefbf41e3f16bfb90"},{"mode":33188,"name":"uvrun.cc","hash":"37e58218d378903063edc474e98a7eaa5592e16a"}]},"9177076585c82e0dd7e075b2562515dbcf0b0b41":{"type":"tree","body":[{"mode":33188,"name":".gitignore","hash":"378eac25d311703f3f2cd456d8036da525cd0366"},{"mode":33188,"name":"README.md","hash":"80797b0d8cc2f63853fc459ead02dcf7ea191192"},{"mode":33188,"name":"binding.gyp","hash":"ade921740b2ab7abc873c2257b61a02804fc6d25"},{"mode":33188,"name":"package.json","hash":"977d55b88bc49d7f48f241b58e83f40f2b4b3487"},{"mode":33188,"name":"uvrun.cc","hash":"37e58218d378903063edc474e98a7eaa5592e16a"}]},"c77ccbbee877f8b5579e6b65f9b755c04d192d70":{"type":"tree","body":[{"mode":33188,"name":".gitignore","hash":"378eac25d311703f3f2cd456d8036da525cd0366"},{"mode":33188,"name":"README.md","hash":"80797b0d8cc2f63853fc459ead02dcf7ea191192"},{"mode":33188,"name":"binding.gyp","hash":"ade921740b2ab7abc873c2257b61a02804fc6d25"},{"mode":33188,"name":"package.json","hash":"e7808a39479632425e9eaa3f03f9154be5a63ab0"},{"mode":33188,"name":"uvrun.cc","hash":"37e58218d378903063edc474e98a7eaa5592e16a"}]},"9b8d89fdde59abff8bd37ffd16fc0a19d8dac566":{"type":"tree","body":[{"mode":33188,"name":".gitignore","hash":"378eac25d311703f3f2cd456d8036da525cd0366"},{"mode":33188,"name":"binding.gyp","hash":"ade921740b2ab7abc873c2257b61a02804fc6d25"},{"mode":33188,"name":"package.json","hash":"6bda9151960a8e030668de34c3ba0a9b79287775"},{"mode":33188,"name":"uvrun.cc","hash":"37e58218d378903063edc474e98a7eaa5592e16a"}]},"b29ac192d4c2c714a40933144809ddfe2d013148":{"type":"tree","body":[{"mode":33188,"name":".gitignore","hash":"378eac25d311703f3f2cd456d8036da525cd0366"},{"mode":33188,"name":"binding.gyp","hash":"ade921740b2ab7abc873c2257b61a02804fc6d25"},{"mode":33188,"name":"package.json","hash":"a493215589d7897a08526cb90ba9c929aa002c1a"},{"mode":33188,"name":"uvrun.cc","hash":"37e58218d378903063edc474e98a7eaa5592e16a"}]},"ccbcf5a7a3d1852bf2bfeb7609b831f993f5dc12":{"type":"tree","body":[{"mode":33188,"name":".gitignore","hash":"378eac25d311703f3f2cd456d8036da525cd0366"},{"mode":33188,"name":"binding.gyp","hash":"cd6b74a2ac53029d9b3e12d5fdb52898b6c32b6a"},{"mode":33188,"name":"package.json","hash":"6bda9151960a8e030668de34c3ba0a9b79287775"},{"mode":33188,"name":"uvrun.cc","hash":"553f34aecdb86c4e8a5984f4b9f06bd5ad7f3891"}]},"a493215589d7897a08526cb90ba9c929aa002c1a":{"type":"blob","body":"{\n \"name\": \"uvrun\",\n \"version\": \"0.1.0\",\n \"author\": \"Tim Caswell \",\n \"main\": \"build/Release/obj.target/uvrun.node\"\n}\n"}}} -------------------------------------------------------------------------------- /res/icon-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creationix/git-browser/89266992b4c5e35042231d82040e5f8c17191807/res/icon-128.png -------------------------------------------------------------------------------- /res/icon-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creationix/git-browser/89266992b4c5e35042231d82040e5f8c17191807/res/icon-16.png -------------------------------------------------------------------------------- /res/icon-60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creationix/git-browser/89266992b4c5e35042231d82040e5f8c17191807/res/icon-60.png -------------------------------------------------------------------------------- /res/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Git Browser 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /res/prism.css: -------------------------------------------------------------------------------- 1 | /** 2 | * prism.js default theme for JavaScript, CSS and HTML 3 | * Based on dabblet (http://dabblet.com) 4 | * @author Lea Verou 5 | */ 6 | 7 | code[class*="language-"], 8 | pre[class*="language-"] { 9 | color: black; 10 | text-shadow: 0 1px white; 11 | font-family: Consolas, Monaco, 'Andale Mono', monospace; 12 | direction: ltr; 13 | text-align: left; 14 | white-space: pre; 15 | word-spacing: normal; 16 | 17 | -moz-tab-size: 4; 18 | -o-tab-size: 4; 19 | tab-size: 4; 20 | 21 | -webkit-hyphens: none; 22 | -moz-hyphens: none; 23 | -ms-hyphens: none; 24 | hyphens: none; 25 | } 26 | 27 | pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection, 28 | code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection { 29 | text-shadow: none; 30 | background: #b3d4fc; 31 | } 32 | 33 | pre[class*="language-"]::selection, pre[class*="language-"] ::selection, 34 | code[class*="language-"]::selection, code[class*="language-"] ::selection { 35 | text-shadow: none; 36 | background: #b3d4fc; 37 | } 38 | 39 | @media print { 40 | code[class*="language-"], 41 | pre[class*="language-"] { 42 | text-shadow: none; 43 | } 44 | } 45 | 46 | /* Code blocks */ 47 | pre[class*="language-"] { 48 | padding: 1em; 49 | margin: .5em 0; 50 | overflow: auto; 51 | } 52 | 53 | :not(pre) > code[class*="language-"], 54 | pre[class*="language-"] { 55 | background: #f5f2f0; 56 | } 57 | 58 | /* Inline code */ 59 | :not(pre) > code[class*="language-"] { 60 | padding: .1em; 61 | border-radius: .3em; 62 | } 63 | 64 | .token.comment, 65 | .token.prolog, 66 | .token.doctype, 67 | .token.cdata { 68 | color: slategray; 69 | } 70 | 71 | .token.punctuation { 72 | color: #999; 73 | } 74 | 75 | .namespace { 76 | opacity: .7; 77 | } 78 | 79 | .token.property, 80 | .token.tag, 81 | .token.boolean, 82 | .token.number, 83 | .token.constant, 84 | .token.symbol { 85 | color: #905; 86 | } 87 | 88 | .token.selector, 89 | .token.attr-name, 90 | .token.string, 91 | .token.builtin { 92 | color: #690; 93 | } 94 | 95 | .token.operator, 96 | .token.entity, 97 | .token.url, 98 | .language-css .token.string, 99 | .style .token.string, 100 | .token.variable { 101 | color: #a67f59; 102 | background: hsla(0,0%,100%,.5); 103 | } 104 | 105 | .token.atrule, 106 | .token.attr-value, 107 | .token.keyword { 108 | color: #07a; 109 | } 110 | 111 | 112 | .token.regex, 113 | .token.important { 114 | color: #e90; 115 | } 116 | 117 | .token.important { 118 | font-weight: bold; 119 | } 120 | 121 | .token.entity { 122 | cursor: help; 123 | } 124 | -------------------------------------------------------------------------------- /res/sprites.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/creationix/git-browser/89266992b4c5e35042231d82040e5f8c17191807/res/sprites.png -------------------------------------------------------------------------------- /src/app/backend.js: -------------------------------------------------------------------------------- 1 | module.exports = function (git) { 2 | var metas = []; 3 | var dirty; 4 | var onAdd, onRemove; 5 | 6 | return { 7 | settings: git.settings, 8 | add: function (meta, callback) { 9 | for (var i = 0, l = metas.length; i < l; ++i) { 10 | if (metas[i].name === meta.name) { 11 | return callback(new Error(meta.name + " name already taken.")); 12 | } 13 | } 14 | addRepo(meta, function (err, repo) { 15 | if (err) return callback(err); 16 | saveMeta(); 17 | return callback(null, repo); 18 | }); 19 | }, 20 | remove: function (repo, callback) { 21 | var meta; 22 | for (var i = 0, l = metas.length; i < l; ++i) { 23 | meta = metas[i]; 24 | if (meta.name === repo.name) break; 25 | } 26 | if (i >= l) { 27 | return callback(new Error("Unknown repo name " + repo.name)); 28 | } 29 | metas.splice(i, 1); 30 | saveMeta(); 31 | repo.clear(function (err) { 32 | if (err) return callback(err); 33 | saveMeta(); 34 | onRemove(meta, i); 35 | return callback(null, meta); 36 | }); 37 | }, 38 | init: function (add, remove, callback) { 39 | onAdd = add; 40 | onRemove = remove; 41 | var metas = git.settings.get("metas"); 42 | if (!metas) return setImmediate(callback); 43 | var left = metas.length; 44 | if (!metas.length) return setImmediate(callback); 45 | var done = false; 46 | metas.forEach(function (meta) { 47 | addRepo(meta, check); 48 | }); 49 | function check(err) { 50 | if (done) return; 51 | if (err) { 52 | done = true; 53 | return callback(err); 54 | } 55 | if (!--left) { 56 | done = true; 57 | return callback(); 58 | } 59 | } 60 | } 61 | }; 62 | 63 | function addRepo(meta, callback) { 64 | var db = git.db(meta.name); 65 | var repo = git.repo(db); 66 | repo.clear = db.clear; 67 | var index = metas.length; 68 | metas[index] = meta; 69 | repo.remote = git.remote(meta.url); 70 | repo.name = meta.name; 71 | repo.url = meta.url; 72 | repo.description = meta.description || meta.url; 73 | db.init(function (err) { 74 | if (err) return callback(err); 75 | onAdd(repo, index); 76 | return callback(null, repo); 77 | }); 78 | } 79 | 80 | function saveMeta() { 81 | if (dirty) return; 82 | // Use dirty flag and setImmediate to coalesce many saves in a single tick. 83 | dirty = true; 84 | setImmediate(function () { 85 | dirty = false; 86 | git.settings.set("metas", metas); 87 | }); 88 | } 89 | 90 | }; 91 | -------------------------------------------------------------------------------- /src/app/phone-ui.js: -------------------------------------------------------------------------------- 1 | var domBuilder = require('dombuilder'); 2 | var progressParser = require('../lib/progress-parser.js'); 3 | var ui = require('./ui.js'); 4 | var prism = require('../prism/prism-core.js'); 5 | require('../prism/prism-javascript.js'); 6 | require('../prism/prism-c.js'); 7 | require('../prism/prism-bash.js'); 8 | require('../prism/prism-coffeescript.js'); 9 | require('../prism/prism-cpp.js'); 10 | require('../prism/prism-css-extras.js'); 11 | require('../prism/prism-markup.js'); 12 | 13 | // Patterns for different language mode names. 14 | var modes = { 15 | javascript: /\.(?:js|json|webapp)$/i, 16 | css: /\.(?:css|less)$/i, 17 | // markup: /\.(?:xml|html|svg)$/i, 18 | bash: /\.sh$/i, 19 | c: /\.(?:h|c)$/i, 20 | cpp: /\.(?:cpp|cxx|hxx|h)$/i, 21 | coffeescript: /\.(?:cs|coffee)$/i, 22 | }; 23 | 24 | var isText = /(?:\.(?:markdown|md|txt|html|svg|xml)|^(?:LICENSE|README|\.gitignore))$/i; 25 | 26 | var isImage = /\.(?:png|jpg|jpeg|gif)$/i; 27 | 28 | module.exports = function (backend) { 29 | ui.push(repoList(backend)); 30 | }; 31 | 32 | function repoList(backend) { 33 | var $ = {}; 34 | var pending; 35 | var children = {}; 36 | backend.init(onAdd, onRemove, onReady); 37 | 38 | return domBuilder(["section.page$page", {"data-position": "none", css: {opacity: 0.5}}, 39 | ["header", 40 | ["button", {onclick:onclick(add)}, [".icon-plus"]], 41 | ["h1", "Git Repositories"] 42 | ], 43 | ["ul.content.header$list"] 44 | ], $); 45 | 46 | function onAdd(repo) { 47 | var icon = ".icon.left.icon-"; 48 | if (/github\.com/.test(repo.url)) { 49 | icon += "github"; 50 | } 51 | else if (/bitbucket\.org/.test(repo.url)) { 52 | icon += "bitbucket"; 53 | } 54 | else { 55 | icon += "git"; 56 | } 57 | var $$ = {}; 58 | var child = domBuilder( 59 | ["li$li", { href: "#", onclick: onclick(fetch, repo, icon, $$) }, 60 | [icon], 61 | ["p", repo.name], 62 | ["p", repo.description], 63 | ["p.progress", 64 | ["progress$progress"], ["span$span", "Working..."] 65 | ], 66 | ], $$ 67 | ); 68 | children[repo.name] = child; 69 | $.list.appendChild(child); 70 | repo.remove = function (callback) { 71 | backend.remove(repo, callback); 72 | }; 73 | } 74 | 75 | function onRemove(meta) { 76 | var child = children[meta.name]; 77 | delete children[meta.name]; 78 | $.list.removeChild(child); 79 | } 80 | 81 | function onReady(err) { 82 | if (err) return ui.error(err); 83 | $.page.style.opacity = 1; 84 | } 85 | 86 | function fetch(repo, icon, $$) { 87 | var progress = $$.progress; 88 | var span = $$.span; 89 | var child = $$.li; 90 | pending = repo; 91 | 92 | return repo.getHead(function (err, head) { 93 | if (err) return ui.error(err); 94 | if (!head) return clone(); 95 | return onFetch(); 96 | }); 97 | 98 | function clone() { 99 | child.classList.add("active"); 100 | var old; 101 | repo.fetch(repo.remote, { 102 | onProgress: progressParser(function (message, num, max) { 103 | if (max) { 104 | progress.setAttribute("max", max); 105 | progress.setAttribute("value", num); 106 | } 107 | if (message !== old) { 108 | span.textContent = old = message; 109 | } 110 | }) 111 | }, onFetch); 112 | 113 | } 114 | 115 | function onFetch(err) { 116 | if (err) { 117 | return repo.remove(function () { 118 | return ui.error(err); 119 | }); 120 | } 121 | var oldChild = child; 122 | child = domBuilder( 123 | ["li", { href:"#", onclick: onclick(load, repo) }, 124 | [icon], 125 | [".icon.right.icon-right-open"], 126 | ["p", repo.name], 127 | ["p", repo.description] 128 | ] 129 | ); 130 | children[repo.name] = child; 131 | $$ = null; 132 | $.list.replaceChild(child, oldChild); 133 | if (pending === repo) load(repo); 134 | } 135 | 136 | } 137 | 138 | function load(repo) { 139 | pending = null; 140 | repo.logWalk("HEAD", function (err, stream) { 141 | if (err) return ui.error(err); 142 | ui.push(historyList(repo, stream)); 143 | }); 144 | } 145 | 146 | function add() { 147 | ui.push(addPage(backend)); 148 | } 149 | 150 | } 151 | 152 | function addPage(backend) { 153 | var $ = {}; 154 | var working = false; 155 | return domBuilder(["section.page", 156 | ["header", 157 | ["button.back", {onclick: ui.pop}, [".icon-left-open"]], 158 | ["h1", "Add Repository"] 159 | ], 160 | ["form.content.header", {onsubmit: submit}, 161 | ["label", {"for": "url"}, "Remote Url"], 162 | ["input", { 163 | type: "url", 164 | name: "url", 165 | placeholder: "Enter git url here", 166 | value: "git://github.com/creationix/conquest.git", 167 | required: true 168 | }], 169 | ["label", {"for": "name"}, "Name"], 170 | ["input", { 171 | type: "text", 172 | name: "name", 173 | placeholder: "Enter custom local name here", 174 | }], 175 | ["label", {"for": "description"}, "Description"], 176 | ["input", { 177 | type: "text", 178 | name: "description", 179 | placeholder: "Enter a short description here", 180 | }], 181 | ["input$submit", { 182 | type: "submit", 183 | value: "Add Repo" 184 | }] 185 | ] 186 | ], $); 187 | function submit(evt) { 188 | evt.preventDefault(); 189 | if (working) return; 190 | working = true; 191 | var url = this.url.value; 192 | var name = this.name.value; 193 | if (!name) { 194 | var index = url.lastIndexOf("/"); 195 | name = url.substr(index + 1); 196 | if (/\.git$/.test(name)) { 197 | name = name.substr(0, name.length - 4); 198 | } 199 | } 200 | var description = this.description.value; 201 | return backend.add({ 202 | name: name, 203 | url: url, 204 | description: description 205 | }, onRepo); 206 | 207 | function onRepo(err) { 208 | working = false; 209 | if (err) return ui.error(err); 210 | return ui.pop(); 211 | } 212 | } 213 | } 214 | 215 | function historyList(repo, stream) { 216 | var $ = {}, ul, end, reading = false; 217 | var root = domBuilder(["section.page", 218 | ["header", 219 | ["button", {onclick:onclick(remove)}, [".icon-minus"]], 220 | ["button", {onclick:onclick(update)}, [".icon-download"]], 221 | ["button.back", {onclick: ui.pop}, [".icon-left-open"]], 222 | ["h1", repo.name] 223 | ], 224 | ["ul$ul.content.header", 225 | ["li$li", {css:{height: "100px"}}, "Loading..."] 226 | ] 227 | ], $); 228 | ul = $.ul; 229 | end = $.li; 230 | $ = {}; 231 | ul.addEventListener('scroll', check, false); 232 | window.addEventListener('resize', check, false); 233 | setImmediate(function () { 234 | reading = true; 235 | stream.read(onRead); 236 | }); 237 | return root; 238 | 239 | function remove() { 240 | ui.confirm("Are you sure you want to delete this local repo?", function (res) { 241 | if (!res) return; 242 | repo.remove(function (err) { 243 | if (err) return ui.error(err); 244 | ui.pop(); 245 | }); 246 | }); 247 | } 248 | 249 | function update() { 250 | ui.error("TODO: Implement update"); 251 | } 252 | 253 | function load(commit) { 254 | ui.push(commitPage(repo, commit)); 255 | } 256 | 257 | function onRead(err, commit) { 258 | reading = false; 259 | if (err) return ui.error(err); 260 | if (commit === undefined) { 261 | ul.removeChild(end); 262 | end = null; 263 | ul.removeEventListener('scroll', check, false); 264 | window.removeEventListener('resize', check, false); 265 | return; 266 | } 267 | var title = truncate(commit.message, 80); 268 | var item = domBuilder( 269 | ["li", { href:"#", onclick: onclick(load, commit) }, 270 | [".icon.right.icon-right-open", { title: commit.hash }], 271 | ["p", title], 272 | ["p", commit.author.date] 273 | ] 274 | ); 275 | ul.insertBefore(item, end); 276 | check(); 277 | } 278 | 279 | function check() { 280 | if (reading) return; 281 | if (end.offsetTop > ul.offsetHeight + ul.scrollTop) return; 282 | reading = true; 283 | stream.read(onRead); 284 | } 285 | 286 | } 287 | 288 | function commitPage(repo, commit) { 289 | var details = []; 290 | var body = ["section.page", 291 | ["header", 292 | ["button.back", {onclick: ui.pop}, [".icon-left-open"]], 293 | ["h1", repo.name] 294 | ], 295 | ["form.content.header", {onsubmit: prevent}, details] 296 | ]; 297 | details.push( 298 | ["label", "Message"], 299 | ["p", {css:{whiteSpace:"pre-wrap"}}, commit.message]); 300 | details.push( 301 | ["label", "Tree"], 302 | ["button.recommend", {onclick: enter}, commit.tree]); 303 | if (commit.parents) { 304 | details.push(["label", 305 | "Parent" + (commit.parents.length === 1 ? "" : "s") 306 | ]); 307 | commit.parents.forEach(function (parent) { 308 | details.push(["button", {onclick: ascend(parent)}, parent]); 309 | }); 310 | } 311 | details.push( 312 | ["label", "Author"], 313 | ["p", commit.author.name + " <" + commit.author.email + "> " + commit.author.date]); 314 | if (commit.author.email !== commit.committer.email) { 315 | details.push( 316 | ["label", "Committer"], 317 | ["p", commit.committer.name + " <" + commit.committer.email + "> " + commit.committer.date]); 318 | } 319 | details.push( 320 | ["label", "Hash"], 321 | ["button", {disabled:true}, commit.hash]); 322 | 323 | return domBuilder(body); 324 | 325 | function prevent(evt) { 326 | evt.preventDefault(); 327 | } 328 | 329 | function enter() { 330 | repo.loadAs("tree", commit.tree, function (err, tree) { 331 | if (err) return ui.error(err); 332 | ui.push(filesList(repo, tree)); 333 | }); 334 | } 335 | 336 | function ascend(parent) { 337 | return function () { 338 | repo.loadAs("commit", parent, function (err, commit) { 339 | if (err) return ui.error(err); 340 | ui.peer(commitPage(repo, commit)); 341 | }); 342 | }; 343 | } 344 | } 345 | 346 | function filesList(repo, tree) { 347 | return domBuilder(["section.page", 348 | ["header", 349 | ["button.back", {onclick: ui.pop}, [".icon-left-open"]], 350 | ["h1", repo.name] 351 | ], 352 | ["ul.content.header", tree.map(function (file) { 353 | var icon = ".icon.left.icon-"; 354 | var action; 355 | if (file.mode === 16384) { 356 | icon += "folder-empty"; 357 | action = enterFolder; 358 | } 359 | else if (isImage.test(file.name)) { 360 | icon += "picture"; 361 | action = loadImage; 362 | } 363 | else if (isText.test(file.name)) { 364 | icon += "doc-text"; 365 | action = loadText; 366 | } 367 | else { 368 | var names = Object.keys(modes); 369 | for (var i = 0, l = names.length; i < l; ++i) { 370 | var name = names[i]; 371 | var regexp = modes[name]; 372 | if (regexp.test(file.name)) { 373 | icon += "doc-text"; 374 | action = loadCode.bind(null, name); 375 | break; 376 | } 377 | } 378 | } 379 | if (!action) { 380 | icon += "doc"; 381 | action = load; 382 | } 383 | return ["li", { href:"#", onclick: onclick(action, file) }, 384 | [icon], 385 | (file.mode === 16384 ? [".icon.right.icon-right-open"] : []), 386 | ["p", file.name], 387 | ["p", file.hash] 388 | ]; 389 | })] 390 | ]); 391 | 392 | function enterFolder(file) { 393 | return repo.loadAs("tree", file.hash, function (err, tree) { 394 | if (err) return ui.error(err); 395 | ui.push(filesList(repo, tree)); 396 | }); 397 | } 398 | 399 | function loadImage(file) { 400 | return repo.loadAs("blob", file.hash, function (err, blob) { 401 | if (err) return ui.error(err); 402 | ui.push(imagePage(file.name, blob)); 403 | }); 404 | } 405 | 406 | function loadCode(language, file) { 407 | return repo.loadAs("blob", file.hash, function (err, blob) { 408 | if (err) return ui.error(err); 409 | ui.push(codePage(file.name, blob, language)); 410 | }); 411 | } 412 | 413 | function loadText(file) { 414 | return repo.loadAs("blob", file.hash, function (err, blob) { 415 | if (err) return ui.error(err); 416 | ui.push(textPage(file.name, blob)); 417 | }); 418 | } 419 | 420 | function load(file) { 421 | console.log("TODO: load file"); 422 | } 423 | } 424 | 425 | function codePage(filename, blob, language) { 426 | var code = ""; 427 | for (var i = 0, l = blob.length; i < l; ++i) { 428 | code += String.fromCharCode(blob[i]); 429 | } 430 | var body = prism.parse(code, language); 431 | body[0] += ".content.header"; 432 | 433 | return domBuilder(["section.page", 434 | ["header", 435 | ["button.back", {onclick: ui.pop}, [".icon-left-open"]], 436 | ["h1", filename] 437 | ], 438 | body 439 | ]); 440 | } 441 | 442 | function textPage(filename, blob) { 443 | var text = ""; 444 | for (var i = 0, l = blob.length; i < l; ++i) { 445 | text += String.fromCharCode(blob[i]); 446 | } 447 | return domBuilder(["section.page", 448 | ["header", 449 | ["button.back", {onclick: ui.pop}, [".icon-left-open"]], 450 | ["h1", filename] 451 | ], 452 | ["pre.content.header", {css: { 453 | padding: "1rem", 454 | whiteSpace:"pre-wrap" 455 | }}, 456 | ["code", text] 457 | ] 458 | ]); 459 | } 460 | 461 | function imagePage(filename, blob) { 462 | blob = new Blob([blob]); 463 | var url = window.URL.createObjectURL(blob); 464 | return domBuilder(["section.page", 465 | ["header", 466 | ["button.back", {onclick: ui.pop}, [".icon-left-open"]], 467 | ["h1", filename], 468 | ], 469 | [".content.header", {css:{ 470 | backgroundImage: "url(" + url + ")", 471 | backgroundSize: "contain", 472 | backgroundRepeat: "no-repeat", 473 | backgroundPosition: "center" 474 | }}] 475 | ]); 476 | } 477 | 478 | 479 | function truncate(message, limit) { 480 | var title = message.split(/[\r\n]/)[0]; 481 | if (title.length > limit) title = title.substr(0, limit - 3) + "..."; 482 | return title; 483 | } 484 | 485 | 486 | 487 | function onclick(handler) { 488 | var args = Array.prototype.slice.call(arguments, 1); 489 | return function (evt) { 490 | evt.preventDefault(); 491 | evt.stopPropagation(); 492 | return handler.apply(this, args); 493 | }; 494 | } 495 | 496 | -------------------------------------------------------------------------------- /src/app/ui.js: -------------------------------------------------------------------------------- 1 | exports.push = push; 2 | exports.pop = pop; 3 | exports.peer = peer; 4 | exports.error = error; 5 | exports.confirm = confirm; 6 | 7 | document.body.textContent = ""; 8 | var pages = []; 9 | 10 | function push(next) { 11 | next.addEventListener("animationend", onAnimationEnd, false); 12 | next.addEventListener("webkitAnimationEnd", onAnimationEnd, false); 13 | 14 | var current = pages.length && pages[pages.length - 1]; 15 | if (current) { 16 | current.classList.remove("current"); 17 | current.classList.add("left"); 18 | } 19 | pages.push(next); 20 | if (!next.getAttribute("data-position")) { 21 | next.setAttribute("data-position", "right"); 22 | } 23 | next.classList.remove("right"); 24 | next.classList.add("current"); 25 | document.body.appendChild(next); 26 | } 27 | 28 | function pop() { 29 | if (!pages.length) return; 30 | var current = pages.pop(); 31 | var previous = pages.length && pages[pages.length - 1]; 32 | current.classList.remove("current"); 33 | current.classList.remove("current"); 34 | current.classList.add("right"); 35 | if (previous) { 36 | previous.classList.remove("left"); 37 | previous.classList.add("current"); 38 | } 39 | setTimeout(function () { 40 | document.body.removeChild(current); 41 | }, 400); 42 | } 43 | 44 | function onAnimationEnd(evt) { 45 | var page = evt.target; 46 | var classList = page.classList; 47 | if (classList.contains("current")) { 48 | page.setAttribute("data-position", "current"); 49 | } 50 | else if (classList.contains("left")) { 51 | page.setAttribute("data-position", "left"); 52 | } 53 | } 54 | 55 | function peer(next) { 56 | // TODO: make this prettier 57 | pop(); 58 | push(next); 59 | } 60 | 61 | function error(err) { 62 | setImmediate(function () { 63 | alert(err); 64 | }); 65 | throw err; 66 | } 67 | 68 | function confirm(message, callback) { 69 | callback(window.confirm(message)); 70 | } -------------------------------------------------------------------------------- /src/appinfo.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "com.creationix.git-browser", 3 | "version": "0.0.1", 4 | "vendor": "Tim Caswell", 5 | "type": "web", 6 | "main": "index.html", 7 | "title": "Git-Browser", 8 | "icon": "icon-128.png" 9 | } 10 | -------------------------------------------------------------------------------- /src/background.js: -------------------------------------------------------------------------------- 1 | /*global chrome*/ 2 | chrome.app.runtime.onLaunched.addListener(function() { 3 | chrome.app.window.create('/index.html', { 4 | id: "git-browse-app-main", 5 | }); 6 | }); 7 | -------------------------------------------------------------------------------- /src/chrome.js: -------------------------------------------------------------------------------- 1 | /*global chrome*/ 2 | 3 | // Configure the platform 4 | var platform = { 5 | tcp: require('./lib/chrome-tcp.js'), 6 | sha1: require('git-sha1'), 7 | bops: require('bops'), 8 | // inflate: require('git-zlib/inflate.js'), 9 | // deflate: require('git-zlib/deflate.js'), 10 | // trace: require('./trace.js'), 11 | }; 12 | platform.http = require('git-http')(platform); 13 | 14 | // Polyfill setImmediate 15 | if (!window.setImmediate) window.setImmediate = require('./lib/defer.js'); 16 | 17 | // Configure the backend 18 | var backend = require('./app/backend.js')({ 19 | repo: require('js-git'), 20 | remote: require('git-net')(platform), 21 | db: require('git-indexeddb')(platform), 22 | // db: require('./lib/git-chrome-localdb.js')(platform), 23 | // db: require('./lib/git-memdb.js'), 24 | settings: { get: get, set: set }, 25 | }); 26 | 27 | var sync = chrome.storage.sync; 28 | var settings; 29 | 30 | function get(key) { 31 | return settings[key]; 32 | } 33 | function set(key, value) { 34 | settings[key] = value; 35 | var dump = {}; 36 | dump[key] = value; 37 | sync.set(dump); 38 | } 39 | 40 | var ui = require('./app/ui.js'); 41 | ui.confirm = function (message, callback) { 42 | console.log("Confirm", message); 43 | callback(true); 44 | }; 45 | ui.error = function (err) { 46 | console.error(err.stack); 47 | }; 48 | 49 | sync.get(null, function (items) { 50 | settings = items; 51 | require('./app/phone-ui.js')(backend); 52 | }); 53 | 54 | -------------------------------------------------------------------------------- /src/chrome.less: -------------------------------------------------------------------------------- 1 | @import "style.less"; 2 | .page { 3 | .page(#6192dd, #4271c9) 4 | } 5 | form.content { 6 | .form(#6192dd, #4271c9, #456); 7 | } 8 | ::-webkit-scrollbar { 9 | background: none; 10 | width: 1rem; 11 | } 12 | ::-webkit-scrollbar-thumb { 13 | background-color: rgba(0, 0, 0, 0.3); 14 | border-radius: 1rem; 15 | } 16 | -------------------------------------------------------------------------------- /src/git-browser.appcache: -------------------------------------------------------------------------------- 1 | CACHE MANIFEST 2 | index.html 3 | style.css 4 | app.js 5 | prism.css 6 | 7 | -------------------------------------------------------------------------------- /src/icons.less: -------------------------------------------------------------------------------- 1 | // @font-face { 2 | // font-family: 'git-browser-icons'; 3 | // src: url('../font/git-browser-icons.eot?68701470'); 4 | // src: url('../font/git-browser-icons.eot?68701470#iefix') format('embedded-opentype'), 5 | // url('../font/git-browser-icons.svg?68701470#git-browser-icons') format('svg'); 6 | // font-weight: normal; 7 | // font-style: normal; 8 | // } 9 | @font-face { 10 | font-family: 'git-browser-icons'; 11 | src: url('data:application/octet-stream;base64,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') format('woff'), 12 | url('data:application/octet-stream;base64,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BCNCsAZFKyCwYFBYILBAUVizAiADIBuzAiYDGllCQiMgsAhDIIojRyNHI2EjRmCwBEOwgGJgILAAKyCKimEgsAJDYGQjsANDYWRQWLACQ2EbsANDYFmwAyWwgGJhIyAgsAQmI0ZhOBsjsAhDRrACJbAIQ0cjRyNhYCCwBEOwgGJgIyCwACsjsARDYLAAK7AFJWGwBSWwgGKwBCZhILAEJWBkI7ADJWBkUFghGyMhWSMgILAEJiNGYThZLbAyLLAAFiAgILAFJiAuRyNHI2EjPDgtsDMssAAWILAII0IgICBGI0ewACsjYTgtsDQssAAWsAMlsAIlRyNHI2GwAFRYLiA8IyEbsAIlsAIlRyNHI2EgsAUlsAQlRyNHI2GwBiWwBSVJsAIlYbABRWMjIFhiGyFZY7ABRWJgIy4jICA8ijgjIVktsDUssAAWILAIQyAuRyNHI2EgYLAgYGawgGIjICA8ijgtsDYsIyAuRrACJUZSWCA8WS6xJgEUKy2wNywjIC5GsAIlRlBYIDxZLrEmARQrLbA4LCMgLkawAiVGUlggPFkjIC5GsAIlRlBYIDxZLrEmARQrLbA5LLAwKyMgLkawAiVGUlggPFkusSYBFCstsDossDEriiAgPLAEI0KKOCMgLkawAiVGUlggPFkusSYBFCuwBEMusCYrLbA7LLAAFrAEJbAEJiAuRyNHI2GwBkUrIyA8IC4jOLEmARQrLbA8LLEIBCVCsAAWsAQlsAQlIC5HI0cjYSCwBCNCsAZFKyCwYFBYILBAUVizAiADIBuzAiYDGllCQiMgR7AEQ7CAYmAgsAArIIqKYSCwAkNgZCOwA0NhZFBYsAJDYRuwA0NgWbADJbCAYmGwAiVGYTgjIDwjOBshICBGI0ewACsjYTghWbEmARQrLbA9LLAwKy6xJgEUKy2wPiywMSshIyAgPLAEI0IjOLEmARQrsARDLrAmKy2wPyywABUgR7AAI0KyAAEBFRQTLrAsKi2wQCywABUgR7AAI0KyAAEBFRQTLrAsKi2wQSyxAAEUE7AtKi2wQiywLyotsEMssAAWRSMgLiBGiiNhOLEmARQrLbBELLAII0KwQystsEUssgAAPCstsEYssgABPCstsEcssgEAPCstsEgssgEBPCstsEkssgAAPSstsEossgABPSstsEsssgEAPSstsEwssgEBPSstsE0ssgAAOSstsE4ssgABOSstsE8ssgEAOSstsFAssgEBOSstsFEssgAAOystsFIssgABOystsFMssgEAOystsFQssgEBOystsFUssgAAPistsFYssgABPistsFcssgEAPistsFgssgEBPistsFkssgAAOistsFossgABOistsFsssgEAOistsFwssgEBOistsF0ssDIrLrEmARQrLbBeLLAyK7A2Ky2wXyywMiuwNystsGAssAAWsDIrsDgrLbBhLLAzKy6xJgEUKy2wYiywMyuwNistsGMssDMrsDcrLbBkLLAzK7A4Ky2wZSywNCsusSYBFCstsGYssDQrsDYrLbBnLLA0K7A3Ky2waCywNCuwOCstsGkssDUrLrEmARQrLbBqLLA1K7A2Ky2wayywNSuwNystsGwssDUrsDgrLbBtLCuwCGWwAyRQeLABFTAtAAAAS7gAyFJYsQEBjlm5CAAIAGMgsAEjRLADI3CyBCgJRVJEsgoCByqxBgFEsSQBiFFYsECIWLEGA0SxJgGIUVi4BACIWLEGAURZWVlZuAH/hbAEjbEFAEQAAA==') format('truetype'); 13 | } 14 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ 15 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ 16 | /* 17 | @media screen and (-webkit-min-device-pixel-ratio:0) { 18 | @font-face { 19 | font-family: 'git-browser-icons'; 20 | src: url('../font/git-browser-icons.svg?68701470#git-browser-icons') format('svg'); 21 | } 22 | } 23 | */ 24 | 25 | [class^="icon-"]:before, [class*=" icon-"]:before { 26 | font-family: "git-browser-icons"; 27 | font-style: normal; 28 | font-weight: normal; 29 | speak: none; 30 | 31 | display: inline-block; 32 | text-decoration: inherit; 33 | width: 1em; 34 | margin-right: .2em; 35 | text-align: center; 36 | /* opacity: .8; */ 37 | 38 | /* For safety - reset parent styles, that can break glyph codes*/ 39 | font-variant: normal; 40 | text-transform: none; 41 | 42 | /* fix buttons height, for twitter bootstrap */ 43 | line-height: 1em; 44 | 45 | /* Animation center compensation - margins should be symmetric */ 46 | /* remove if not needed */ 47 | margin-left: .2em; 48 | 49 | /* you can be more comfortable with increased icons size */ 50 | /* font-size: 120%; */ 51 | 52 | /* Uncomment for 3D effect */ 53 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 54 | } 55 | .icon-plus:before { content: '\e80c'; } /* '' */ 56 | .icon-minus:before { content: '\e80d'; } /* '' */ 57 | .icon-home:before { content: '\e810'; } /* '' */ 58 | .icon-up-dir:before { content: '\e816'; } /* '' */ 59 | .icon-right-dir:before { content: '\e818'; } /* '' */ 60 | .icon-down-dir:before { content: '\e815'; } /* '' */ 61 | .icon-left-dir:before { content: '\e817'; } /* '' */ 62 | .icon-music:before { content: '\e809'; } /* '' */ 63 | .icon-cog:before { content: '\e813'; } /* '' */ 64 | .icon-pencil:before { content: '\e812'; } /* '' */ 65 | .icon-trash:before { content: '\e804'; } /* '' */ 66 | .icon-database:before { content: '\e80b'; } /* '' */ 67 | .icon-down-open:before { content: '\e80e'; } /* '' */ 68 | .icon-left-open:before { content: '\e802'; } /* '' */ 69 | .icon-right-open:before { content: '\e803'; } /* '' */ 70 | .icon-up-open:before { content: '\e80f'; } /* '' */ 71 | .icon-git:before { content: '\e81d'; } /* '' */ 72 | .icon-doc-text:before { content: '\e806'; } /* '' */ 73 | .icon-github:before { content: '\e81b'; } /* '' */ 74 | .icon-folder-empty:before { content: '\e805'; } /* '' */ 75 | .icon-folder-open-empty:before { content: '\e814'; } /* '' */ 76 | .icon-info:before { content: '\e811'; } /* '' */ 77 | .icon-bitbucket:before { content: '\e81c'; } /* '' */ 78 | .icon-picture:before { content: '\e808'; } /* '' */ 79 | .icon-video:before { content: '\e80a'; } /* '' */ 80 | .icon-doc:before { content: '\e807'; } /* '' */ 81 | .icon-upload:before { content: '\e800'; } /* '' */ 82 | .icon-download:before { content: '\e801'; } /* '' */ -------------------------------------------------------------------------------- /src/lib/chrome-tcp.js: -------------------------------------------------------------------------------- 1 | /*global chrome*/ 2 | var socket = chrome.socket; 3 | var bops = require('bops'); 4 | 5 | exports.connect = connect; 6 | function connect(port, host, callback) { 7 | if (typeof host === "function" && typeof callback === "undefined") { 8 | callback = host; 9 | host = "127.0.0.1"; 10 | } 11 | if (!callback) return connect.bind(this, port, host); 12 | if (typeof port !== "number") throw new TypeError("port must be number"); 13 | if (typeof host !== "string") throw new TypeError("host must be string"); 14 | if (typeof callback !== "function") throw new TypeError("callback must be function"); 15 | 16 | socket.create("tcp", function (info) { 17 | socket.connect(info.socketId, host, port, function (result) { 18 | if (result < 0) { 19 | return callback(new Error("Error " + byNumber[result] + " while connecting to " + host + ":" + port)); 20 | } 21 | callback(null, wrapSocket(info.socketId)); 22 | }); 23 | }); 24 | } 25 | 26 | exports.wrapSocket = wrapSocket; 27 | function wrapSocket(id) { 28 | var stream = socketToStream(id); 29 | stream.sink = socketToSink(id); 30 | return stream; 31 | } 32 | 33 | exports.socketToStream = socketToStream; 34 | function socketToStream(id) { 35 | return { id: id, read: read, abort: abort }; 36 | 37 | function read(callback) { 38 | socket.read(id, function (info) { 39 | if (info.resultCode < 0) { 40 | var errname = byNumber[info.resultCode]; 41 | if (errname === "SOCKET_NOT_CONNECTED") { 42 | return callback(); 43 | } 44 | return callback(new Error("Error " + byNumber[info.resultCode] + " while reading from socket " + id)); 45 | } 46 | callback(null, new Uint8Array(info.data)); 47 | }); 48 | } 49 | 50 | function abort(callback) { 51 | socket.disconnect(id); 52 | socket.destroy(id); 53 | callback(); 54 | } 55 | 56 | } 57 | 58 | function noop() {} 59 | 60 | exports.socketToSink = socketToSink; 61 | function socketToSink(id) { 62 | return sink; 63 | function sink(stream, callback) { 64 | if (!callback) return sink.bind(this, stream); 65 | stream.read(onRead); 66 | function onRead(err, chunk) { 67 | if (chunk === undefined) { 68 | socket.disconnect(id); 69 | socket.destroy(id); 70 | return callback(err); 71 | } 72 | if (typeof chunk === "string") { 73 | chunk = bops.from(chunk); 74 | } 75 | socket.write(id, chunk.buffer, onWrite); 76 | } 77 | function onWrite(info) { 78 | if (info.bytesWritten < 0) { 79 | return callback(new Error("Error " + byNumber[info.bytesWritten] + " while writing to socket " + id), noop); 80 | } 81 | stream.read(onRead); 82 | } 83 | } 84 | } 85 | 86 | 87 | // Taken from chromium/src/net/base/net_error_list.h 88 | var errorCodes = {IO_PENDING: -1, FAILED: -2, ABORTED: -3, INVALID_ARGUMENT: -4, INVALID_HANDLE: -5, FILE_NOT_FOUND: -6, TIMED_OUT: -7, FILE_TOO_BIG: -8, UNEXPECTED: -9, ACCESS_DENIED: -10, NOT_IMPLEMENTED: -11, INSUFFICIENT_RESOURCES: -12, OUT_OF_MEMORY: -13, UPLOAD_FILE_CHANGED: -14, SOCKET_NOT_CONNECTED: -15, FILE_EXISTS: -16, FILE_PATH_TOO_LONG: -17, FILE_NO_SPACE: -18, FILE_VIRUS_INFECTED: -19, BLOCKED_BY_CLIENT: -20, NETWORK_CHANGED: -21, BLOCKED_BY_ADMINISTRATOR: -22, SOCKET_IS_CONNECTED: -23, CONNECTION_CLOSED: -100, CONNECTION_RESET: -101, CONNECTION_REFUSED: -102, CONNECTION_ABORTED: -103, CONNECTION_FAILED: -104, NAME_NOT_RESOLVED: -105, INTERNET_DISCONNECTED: -106, SSL_PROTOCOL_ERROR: -107, ADDRESS_INVALID: -108, ADDRESS_UNREACHABLE: -109, SSL_CLIENT_AUTH_CERT_NEEDED: -110, TUNNEL_CONNECTION_FAILED: -111, NO_SSL_VERSIONS_ENABLED: -112, SSL_VERSION_OR_CIPHER_MISMATCH: -113, SSL_RENEGOTIATION_REQUESTED: -114, PROXY_AUTH_UNSUPPORTED: -115, CERT_ERROR_IN_SSL_RENEGOTIATION: -116, BAD_SSL_CLIENT_AUTH_CERT: -117, CONNECTION_TIMED_OUT: -118, HOST_RESOLVER_QUEUE_TOO_LARGE: -119, SOCKS_CONNECTION_FAILED: -120, SOCKS_CONNECTION_HOST_UNREACHABLE: -121, NPN_NEGOTIATION_FAILED: -122, SSL_NO_RENEGOTIATION: -123, WINSOCK_UNEXPECTED_WRITTEN_BYTES: -124, SSL_DECOMPRESSION_FAILURE_ALERT: -125, SSL_BAD_RECORD_MAC_ALERT: -126, PROXY_AUTH_REQUESTED: -127, SSL_UNSAFE_NEGOTIATION: -128, SSL_WEAK_SERVER_EPHEMERAL_DH_KEY: -129, PROXY_CONNECTION_FAILED: -130, MANDATORY_PROXY_CONFIGURATION_FAILED: -131, PRECONNECT_MAX_SOCKET_LIMIT: -133, SSL_CLIENT_AUTH_PRIVATE_KEY_ACCESS_DENIED: -134, SSL_CLIENT_AUTH_CERT_NO_PRIVATE_KEY: -135, PROXY_CERTIFICATE_INVALID: -136, NAME_RESOLUTION_FAILED: -137, NETWORK_ACCESS_DENIED: -138, TEMPORARILY_THROTTLED: -139, HTTPS_PROXY_TUNNEL_RESPONSE: -140, SSL_CLIENT_AUTH_SIGNATURE_FAILED: -141, MSG_TOO_BIG: -142, SPDY_SESSION_ALREADY_EXISTS: -143, WS_PROTOCOL_ERROR: -145, PROTOCOL_SWITCHED: -146, ADDRESS_IN_USE: -147, SSL_HANDSHAKE_NOT_COMPLETED: -148, SSL_BAD_PEER_PUBLIC_KEY: -149, SSL_PINNED_KEY_NOT_IN_CERT_CHAIN: -150, CLIENT_AUTH_CERT_TYPE_UNSUPPORTED: -151, ORIGIN_BOUND_CERT_GENERATION_TYPE_MISMATCH: -152, CERT_COMMON_NAME_INVALID: -200, CERT_DATE_INVALID: -201, CERT_AUTHORITY_INVALID: -202, CERT_CONTAINS_ERRORS: -203, CERT_NO_REVOCATION_MECHANISM: -204, CERT_UNABLE_TO_CHECK_REVOCATION: -205, CERT_REVOKED: -206, CERT_INVALID: -207, CERT_WEAK_SIGNATURE_ALGORITHM: -208, CERT_NON_UNIQUE_NAME: -210, CERT_WEAK_KEY: -211, CERT_END: -212, INVALID_URL: -300, DISALLOWED_URL_SCHEME: -301, UNKNOWN_URL_SCHEME: -302, TOO_MANY_REDIRECTS: -310, UNSAFE_REDIRECT: -311, UNSAFE_PORT: -312, INVALID_RESPONSE: -320, INVALID_CHUNKED_ENCODING: -321, METHOD_NOT_SUPPORTED: -322, UNEXPECTED_PROXY_AUTH: -323, EMPTY_RESPONSE: -324, RESPONSE_HEADERS_TOO_BIG: -325, PAC_STATUS_NOT_OK: -326, PAC_SCRIPT_FAILED: -327, REQUEST_RANGE_NOT_SATISFIABLE: -328, MALFORMED_IDENTITY: -329, CONTENT_DECODING_FAILED: -330, NETWORK_IO_SUSPENDED: -331, SYN_REPLY_NOT_RECEIVED: -332, ENCODING_CONVERSION_FAILED: -333, UNRECOGNIZED_FTP_DIRECTORY_LISTING_FORMAT: -334, INVALID_SPDY_STREAM: -335, NO_SUPPORTED_PROXIES: -336, SPDY_PROTOCOL_ERROR: -337, INVALID_AUTH_CREDENTIALS: -338, UNSUPPORTED_AUTH_SCHEME: -339, ENCODING_DETECTION_FAILED: -340, MISSING_AUTH_CREDENTIALS: -341, UNEXPECTED_SECURITY_LIBRARY_STATUS: -342, MISCONFIGURED_AUTH_ENVIRONMENT: -343, UNDOCUMENTED_SECURITY_LIBRARY_STATUS: -344, RESPONSE_BODY_TOO_BIG_TO_DRAIN: -345, RESPONSE_HEADERS_MULTIPLE_CONTENT_LENGTH: -346, INCOMPLETE_SPDY_HEADERS: -347, PAC_NOT_IN_DHCP: -348, RESPONSE_HEADERS_MULTIPLE_CONTENT_DISPOSITION: -349, RESPONSE_HEADERS_MULTIPLE_LOCATION: -350, SPDY_SERVER_REFUSED_STREAM: -351, SPDY_PING_FAILED: -352, PIPELINE_EVICTION: -353, CONTENT_LENGTH_MISMATCH: -354, INCOMPLETE_CHUNKED_ENCODING: -355, QUIC_PROTOCOL_ERROR: -356, CACHE_MISS: -400, CACHE_READ_FAILURE: -401, CACHE_WRITE_FAILURE: -402, CACHE_OPERATION_NOT_SUPPORTED: -403, CACHE_OPEN_FAILURE: -404, CACHE_CREATE_FAILURE: -405, CACHE_RACE: -406, CACHE_CHECKSUM_READ_FAILURE: -407, CACHE_CHECKSUM_MISMATCH: -408, INSECURE_RESPONSE: -501, NO_PRIVATE_KEY_FOR_CERT: -502, ADD_USER_CERT_FAILED: -503, FTP_FAILED: -601, FTP_SERVICE_UNAVAILABLE: -602, FTP_TRANSFER_ABORTED: -603, FTP_FILE_BUSY: -604, FTP_SYNTAX_ERROR: -605, FTP_COMMAND_NOT_SUPPORTED: -606, FTP_BAD_COMMAND_SEQUENCE: -607, PKCS12_IMPORT_BAD_PASSWORD: -701, PKCS12_IMPORT_FAILED: -702, IMPORT_CA_CERT_NOT_CA: -703, IMPORT_CERT_ALREADY_EXISTS: -704, IMPORT_CA_CERT_FAILED: -705, IMPORT_SERVER_CERT_FAILED: -706, PKCS12_IMPORT_INVALID_MAC: -707, PKCS12_IMPORT_INVALID_FILE: -708, PKCS12_IMPORT_UNSUPPORTED: -709, KEY_GENERATION_FAILED: -710, ORIGIN_BOUND_CERT_GENERATION_FAILED: -711, PRIVATE_KEY_EXPORT_FAILED: -712, DNS_MALFORMED_RESPONSE: -800, DNS_SERVER_REQUIRES_TCP: -801, DNS_SERVER_FAILED: -802, DNS_TIMED_OUT: -803, DNS_CACHE_MISS: -804, DNS_SEARCH_EMPTY: -805, DNS_SORT_ERROR: -806 }; 89 | var byNumber = {}; 90 | Object.keys(errorCodes).forEach(function (name) { 91 | byNumber[errorCodes[name]] = name; 92 | }); 93 | -------------------------------------------------------------------------------- /src/lib/defer.js: -------------------------------------------------------------------------------- 1 | var timeouts = []; 2 | var messageName = "zero-timeout-message"; 3 | 4 | function handleMessage(event) { 5 | if (event.source == window && event.data == messageName) { 6 | event.stopPropagation(); 7 | if (timeouts.length > 0) { 8 | var fn = timeouts.shift(); 9 | fn(); 10 | } 11 | } 12 | } 13 | 14 | window.addEventListener("message", handleMessage, true); 15 | 16 | module.exports = function (fn) { 17 | timeouts.push(fn); 18 | window.postMessage(messageName, "*"); 19 | }; 20 | -------------------------------------------------------------------------------- /src/lib/git-chrome-localdb.js: -------------------------------------------------------------------------------- 1 | /*global chrome*/ 2 | 3 | function makeAsync(fn, callback) { 4 | if (!callback) return makeAsync.bind(this, fn); 5 | setImmediate(function () { 6 | var result; 7 | try { result = fn(); } 8 | catch (err) { return callback(err); } 9 | if (result === undefined) return callback(); 10 | return callback(null, result); 11 | }); 12 | } 13 | 14 | var local = chrome.storage.local; 15 | var deflate, inflate; 16 | module.exports = function (platform) { 17 | deflate = platform.deflate || fake; 18 | inflate = platform.inflate || fake; 19 | return localDb; 20 | }; 21 | function fake(input, callback) { 22 | // setImmediate(function () { 23 | callback(null, input); 24 | // }); 25 | } 26 | 27 | function localDb(prefix) { 28 | 29 | prefix; 30 | 31 | var refs; 32 | var isHash = /^[a-z0-9]{40}$/; 33 | 34 | return { 35 | get: get, 36 | set: set, 37 | has: has, 38 | del: del, 39 | keys: keys, 40 | init: init, 41 | clear: clear 42 | }; 43 | 44 | function get(key, callback) { 45 | if (!callback) return get.bind(this, key); 46 | if (isHash.test(key)) { 47 | return local.get(key, function (items) { 48 | if (chrome.runtime.lastError) { 49 | return callback(new Error(chrome.runtime.lastError.message)); 50 | } 51 | var deflated = fromString(items[key]); 52 | return inflate(deflated, callback); 53 | }); 54 | } 55 | setImmediate(function () { 56 | callback(null, refs[key]); 57 | }); 58 | } 59 | 60 | function set(key, value, callback) { 61 | if (!callback) return set.bind(this, key, value); 62 | if (isHash.test(key)) { 63 | return deflate(value, function (err, deflated) { 64 | var q = {}; 65 | q[key] = toString(deflated); 66 | local.set(q, onSet); 67 | }); 68 | } 69 | refs[key] = value.toString(); 70 | var q = {}; 71 | q[prefix] = refs; 72 | local.set(q, onSet); 73 | 74 | function onSet() { 75 | if (chrome.runtime.lastError) { 76 | return callback(new Error(chrome.runtime.lastError.message)); 77 | } 78 | return callback(); 79 | } 80 | } 81 | 82 | function has(key, callback) { 83 | if (isHash.test(key)) { 84 | return local.get(key, function (items) { 85 | callback(null, (key) in items); 86 | }); 87 | } 88 | return callback(null, key in refs); 89 | } 90 | 91 | function del(key, callback) { 92 | if (isHash.test(key)) { 93 | return local.remove(key, onRemove); 94 | } 95 | delete refs[key]; 96 | var q = {}; 97 | q[prefix] = refs; 98 | local.set(q, onRemove); 99 | 100 | function onRemove() { 101 | if (chrome.runtime.lastError) { 102 | return callback(new Error(chrome.runtime.lastError.message)); 103 | } 104 | return callback(); 105 | } 106 | } 107 | 108 | function keys(prefix, callback) { 109 | return makeAsync(function () { 110 | var list = Object.keys(refs); 111 | if (!prefix) return list; 112 | var length = prefix.length; 113 | return list.filter(function (key) { 114 | return key.substr(0, length) === prefix; 115 | }).map(function (key) { 116 | return key.substr(length); 117 | }); 118 | }, callback); 119 | } 120 | 121 | function init(callback) { 122 | if (!callback) return init.bind(this); 123 | local.get(prefix, function (items) { 124 | refs = items[prefix] || {}; 125 | callback(); 126 | }); 127 | } 128 | 129 | function clear(callback) { 130 | if (!callback) return clear.bind(this); 131 | refs = {}; 132 | local.remove(prefix, callback); 133 | } 134 | } 135 | 136 | 137 | // Here we're storing the data as base 16 since even binary strings seem to fail. 138 | function toString(buffer) { 139 | var string = ""; 140 | for (var i = 0, l = buffer.length; i < l; ++i) { 141 | var x = buffer[i]; 142 | if (x < 0x10) string += "0" + x.toString(16); 143 | else string += x.toString(16); 144 | } 145 | return string; 146 | } 147 | 148 | function fromString(string) { 149 | var length = string.length; 150 | var buffer = new Uint8Array(length >> 1); 151 | for (var i = 0; i < length; i += 2) { 152 | buffer[i >> 1] = parseInt(string.substr(i, 2), 16); 153 | } 154 | return buffer; 155 | } 156 | 157 | // function toString(buffer) { 158 | // var string = ""; 159 | // for (var i = 0, l = buffer.length; i < l; ++i) { 160 | // string += String.fromCharCode(buffer[i]); 161 | // } 162 | // return string; 163 | // } 164 | 165 | // function fromString(string) { 166 | // var length = string.length; 167 | // var buffer = new Uint8Array(length); 168 | // for (var i = 0; i < length; ++i) { 169 | // buffer[i] = string.charCodeAt(i); 170 | // } 171 | // return buffer; 172 | // } 173 | -------------------------------------------------------------------------------- /src/lib/progress-parser.js: -------------------------------------------------------------------------------- 1 | var progMatch = /^([^:]*):[^\(]*\(([0-9]+)\/([0-9]+)\)/; 2 | var progMatchBasic = /^([^:]*)/; 3 | module.exports = progressParser; 4 | function progressParser(emit) { 5 | var buffer = ""; 6 | return function (chunk) { 7 | var start = 0; 8 | for (var i = 0, l = chunk.length; i < l; ++i) { 9 | var c = chunk[i]; 10 | if (c === "\r" || c === "\n") { 11 | buffer += chunk.substr(start, i); 12 | start = i + 1; 13 | var match = buffer.match(progMatch) || 14 | buffer.match(progMatchBasic); 15 | buffer = ""; 16 | if (!match) continue; 17 | emit(match[1], parseInt(match[2], 10), parseInt(match[3], 10)); 18 | } 19 | } 20 | buffer += chunk.substr(start); 21 | }; 22 | } 23 | -------------------------------------------------------------------------------- /src/lib/trace.js: -------------------------------------------------------------------------------- 1 | var messages = { 2 | request: "\u21A0", 3 | response: "\u219E", 4 | input: "\u2190", 5 | output: "\u2192", 6 | save: "\u2907", 7 | load: "\u2906", 8 | remove: "\u2716", 9 | read: "\u2770", 10 | write: "\u2771", 11 | }; 12 | 13 | module.exports = function (type, stream, item) { 14 | var message = messages[type] || type; 15 | if (!stream) { 16 | console.log(message, item); 17 | return; 18 | } 19 | if (!message) return stream; 20 | return { read: traceRead, abort: stream.abort }; 21 | function traceRead(callback) { 22 | stream.read(function (err, item) { 23 | if (err) return callback(err); 24 | console.log(message, item); 25 | callback(null, item); 26 | }); 27 | } 28 | }; 29 | -------------------------------------------------------------------------------- /src/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Git-Browser", 3 | "version": "0.2.3", 4 | "manifest_version": 2, 5 | "offline_enabled": true, 6 | "description": "Browse Git Repos offline.", 7 | "icons": { 8 | "16": "icon-16.png", 9 | "60": "icon-60.png", 10 | "128": "icon-128.png" 11 | }, 12 | "app": { 13 | "background": { 14 | "scripts": ["background.js"] 15 | } 16 | }, 17 | "permissions": [ 18 | "storage", 19 | "unlimitedStorage", 20 | "http://*/*", 21 | "https://*/*", 22 | {"socket": [ "tcp-connect:*:*" ]} 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /src/manifest.webapp: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Git-Browser", 3 | "developer": { 4 | "name": "Tim Caswell", 5 | "url": "http://creationix.com/" 6 | }, 7 | "version": "0.2.3", 8 | "type": "privileged", 9 | "description": "Browse Git Repos offline.", 10 | "launch_path": "/index.html", 11 | "icons": { 12 | "16": "/icon-16.png", 13 | "60": "/icon-60.png", 14 | "128": "/icon-128.png" 15 | }, 16 | "permissions": { 17 | "tcp-socket": { 18 | "description": "Required for communicate with remote git servers" 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/moz-tcp-trace.js: -------------------------------------------------------------------------------- 1 | module.exports = traceSocket; 2 | 3 | function tap(name, fn, thisp) { 4 | return function () { 5 | var args = [name]; 6 | for (var i = 0, l = arguments.length; i < l; i++) { 7 | args.push(arguments[i]); 8 | } 9 | console.log.apply(null, args); 10 | return fn.apply(thisp || this, arguments); 11 | }; 12 | } 13 | function traceSocket(real) { 14 | console.log("TRACE", real); 15 | return { 16 | get onopen() { console.log("GET onopen"); return real.onopen; }, 17 | set onopen(fn) { console.log("SET onopen", fn); return real.onopen = tap("ONOPEN", fn); }, 18 | get ondata() { console.log("GET ondata"); return real.ondata; }, 19 | set ondata(fn) { console.log("SET ondata", fn); return real.ondata = tap("ONDATA", fn); }, 20 | get onclose() { console.log("GET onclose"); return real.onclose; }, 21 | set onclose(fn) { console.log("SET onclose", fn); return real.onclose = tap("ONCLOSE", fn); }, 22 | get onerror() { console.log("GET onerror"); return real.onerror; }, 23 | set onerror(fn) { console.log("SET onerror", fn); return real.onerror = tap("ONERROR", fn); }, 24 | get ondrain() { console.log("GET ondrain"); return real.ondrain; }, 25 | set ondrain(fn) { console.log("SET ondrain", fn); return real.ondrain = tap("ONDRAIN", fn); }, 26 | close: tap("CLOSE", real.close, real), 27 | send: tap("SEND", real.send, real), 28 | suspend: tap("SUSPEND", real.suspend, real), 29 | resume: tap("RESUME", real.resume, real), 30 | }; 31 | } 32 | -------------------------------------------------------------------------------- /src/moz-tcp.js: -------------------------------------------------------------------------------- 1 | var TCPSocket = navigator.TCPSocket || navigator.mozTCPSocket; 2 | 3 | module.exports = { 4 | tcp: { connect: connect.bind(null, false) }, 5 | tls: { connect: connect.bind(null, true) }, 6 | }; 7 | 8 | function connect(useSSL, port, host, callback) { 9 | if (typeof host === "function" && typeof callback === "undefined") { 10 | callback = host; 11 | host = "127.0.0.1"; 12 | } 13 | if (!callback) return connect.bind(this, port, host); 14 | if (typeof port !== "number") throw new TypeError("port must be number"); 15 | if (typeof host !== "string") throw new TypeError("host must be string"); 16 | if (typeof callback !== "function") throw new TypeError("callback must be function"); 17 | 18 | var socket = TCPSocket.open(host, port, { binaryType: "arraybuffer", useSSL: useSSL }); 19 | 20 | socket.onopen = function () { 21 | socket.onopen = null; 22 | callback(null, wrapSocket(socket)); 23 | }; 24 | 25 | socket.onerror = function (err) { 26 | callback(new Error(err.data.name)); 27 | }; 28 | } 29 | 30 | function wrapSocket(socket) { 31 | // socket = require('./trace-socket.js')(socket); 32 | var done = false; 33 | var cb = null; 34 | var queue = []; 35 | var reading = false; 36 | var source = null; 37 | var paused = false; 38 | var finish; 39 | 40 | socket.ondata = function (evt) { 41 | var chunk = new Uint8Array(evt.data); 42 | queue.push([null, chunk]); 43 | return check(); 44 | }; 45 | 46 | socket.onclose = function () { 47 | queue.push([]); 48 | return check(); 49 | }; 50 | 51 | socket.onerror = function (err) { 52 | err = new Error(err.data.name); 53 | queue.push([err]); 54 | return check(); 55 | }; 56 | 57 | socket.ondrain = function () { 58 | if (reading) return; 59 | reading = true; 60 | source.read(onRead); 61 | }; 62 | 63 | return { read: read, abort: abort, sink: sink }; 64 | 65 | function check() { 66 | if (cb && queue.length) { 67 | var callback = cb; 68 | cb = null; 69 | callback.apply(null, queue.shift()); 70 | } 71 | if (paused && cb && !queue.length) { 72 | paused = false; 73 | socket.resume(); 74 | } 75 | else if (!paused && !cb && queue.length) { 76 | paused = true; 77 | socket.suspend(); 78 | } 79 | } 80 | 81 | function read(callback) { 82 | if (done) return callback(); 83 | if (cb) return callback(new Error("Only one read at a time allowed")); 84 | cb = callback; 85 | return check(); 86 | } 87 | 88 | function abort(callback) { 89 | if (done) return callback(); 90 | done = true; 91 | socket.ondata = null; 92 | socket.onclose = null; 93 | socket.onerror = null; 94 | socket.suspend(); 95 | socket.close(); 96 | callback(); 97 | } 98 | 99 | function sink(stream, callback) { 100 | if (!callback) return sink.bind(this, stream); 101 | if (source) throw new Error("Already has source"); 102 | source = stream; 103 | finish = callback; 104 | reading = true; 105 | source.read(onRead); 106 | } 107 | 108 | function onRead(err, chunk) { 109 | reading = false; 110 | if (chunk === undefined) { 111 | socket.ondrain = null; 112 | socket.suspend(); 113 | socket.close(); 114 | return finish(err); 115 | } 116 | var needsMore; 117 | try { 118 | needsMore = socket.send(chunk.buffer); 119 | } 120 | catch (err) { 121 | console.error(err); 122 | } 123 | if (needsMore) { 124 | reading = true; 125 | source.read(onRead); 126 | } 127 | } 128 | 129 | } 130 | -------------------------------------------------------------------------------- /src/moz.js: -------------------------------------------------------------------------------- 1 | // Configure the platform 2 | var platform = { 3 | tcp: require('./moz-tcp.js').tcp, 4 | tls: require('./moz-tcp.js').tls, 5 | bops: require('bops'), 6 | sha1: require('git-sha1'), 7 | // inflate: require('git-zlib/inflate.js'), 8 | // deflate: require('git-zlib/deflate.js'), 9 | // trace: require('./trace.js'), 10 | }; 11 | platform.http = require('git-http')(platform); 12 | 13 | // Polyfill setImmediate 14 | if (!window.setImmediate) window.setImmediate = require('./lib/defer.js'); 15 | 16 | // Configure the backend 17 | var backend = require('./app/backend.js')({ 18 | repo: require('js-git'), 19 | remote: require('git-net')(platform), 20 | db: require('git-indexeddb')(platform), 21 | // db: require('git-localdb')(platform), 22 | // db: require('git-memdb'), 23 | settings: { get: get, set: set }, 24 | }); 25 | 26 | function get(key) { 27 | return JSON.parse(window.localStorage.getItem(key)); 28 | } 29 | 30 | function set(key, value) { 31 | window.localStorage.setItem(key, JSON.stringify(value)); 32 | } 33 | 34 | // Launch the GUI 35 | require('./app/phone-ui.js')(backend); 36 | -------------------------------------------------------------------------------- /src/moz.less: -------------------------------------------------------------------------------- 1 | @import "style.less"; 2 | .page { 3 | .page(#cd6723, #bb482d) 4 | } 5 | form.content { 6 | .form(#cd6723, #bb482d, #654); 7 | } 8 | -------------------------------------------------------------------------------- /src/old/chrome.js: -------------------------------------------------------------------------------- 1 | /*global FileError*/ 2 | window.requestFileSystem = window.requestFileSystem || window.webkitRequestFileSystem; 3 | 4 | var platform = { 5 | tcp: require('./chrome-tcp.js'), 6 | sha1: require('./sha1.js'), 7 | bops: require('./bops/index.js'), 8 | inflate: require('./inflate.js'), 9 | deflate: require('./deflate.js'), 10 | // trace: require('./trace.js'), 11 | }; 12 | 13 | var jsGit = require('js-git')(platform); 14 | var gitRemote = require('git-net')(platform); 15 | var fsDb = require('git-fs-db')(platform); 16 | var gitWebfs = require('./git-webfs.js'); 17 | var progressParser = require('./progress-parser.js'); 18 | 19 | var fs; 20 | function getFs(callback) { 21 | if (fs) return callback(null, fs); 22 | var done = false; 23 | window.requestFileSystem(window.TEMPORARY, null, onInitFs, errorHandler); 24 | function onInitFs(webfs) { 25 | if (done) return; 26 | done = true; 27 | fs = gitWebfs(webfs.root); 28 | callback(null, fs); 29 | } 30 | function errorHandler(err) { 31 | var message = 'Unknown Error'; 32 | for (var key in FileError) { 33 | if (!/_ERR$/.test(key)) continue; 34 | if (FileError[key] === err.code) { 35 | message = key; 36 | break; 37 | } 38 | } 39 | console.error('Error: ' + message); 40 | if (done) return; 41 | done = true; 42 | callback(new Error(message)); 43 | } 44 | } 45 | 46 | function isGitDir(path) { 47 | return (/\.git$/).test(path); 48 | } 49 | 50 | 51 | require('./main.js')({ 52 | // opts are host, path, and description 53 | addRepo: function (opts, onProgress, callback) { 54 | getFs(function (err, fs) { 55 | if (err) return callback(err); 56 | var url = "git://" + opts.hostname + opts.pathname; 57 | var description = opts.description || url; 58 | var path = (opts.hostname + opts.pathname).replace(/\//g, "_"); 59 | var repo = jsGit(fsDb(fs(path))); 60 | var remote = gitRemote(url); 61 | var name = opts.pathname; 62 | if (name[0] === "/") name = name.substr(1); 63 | if (name.substr(name.length - 4) === ".git") name = name.substr(0, name.length - 4); 64 | repo.name = name; 65 | repo.description = description; 66 | 67 | repo.fetch(remote, { 68 | includeTag: true, 69 | onProgress: progressParser(onProgress) 70 | }, function (err) { 71 | if (err) return callback(err); 72 | onProgress("Done"); 73 | callback(null, repo); 74 | }); 75 | }); 76 | }, 77 | getRepos: function (callback) { 78 | getFs(function (err, fs) { 79 | if (err) return callback(err); 80 | fs.readdir("/", function (err, files) { 81 | if (err) return callback(err); 82 | callback(null, files.filter(isGitDir).map(function (path) { 83 | var repo = jsGit(fsDb(fs(path))); 84 | repo.name = path; 85 | repo.description = path; 86 | return repo; 87 | })); 88 | }); 89 | }); 90 | }, 91 | getHistoryStream: function (repo, callback) { 92 | repo.logWalk("HEAD", callback); 93 | }, 94 | getCommit: function (repo, hash, callback) { 95 | repo.loadAs("commit", hash, function (err, commit) { 96 | if (err) return callback(err); 97 | commit.hash = hash; 98 | callback(null, commit); 99 | }); 100 | }, 101 | getTree: function (repo, hash, callback) { 102 | repo.loadAs("tree", hash, function (err, tree) { 103 | if (err) return callback(err); 104 | tree.hash = hash; 105 | callback(null, tree); 106 | }); 107 | } 108 | }); 109 | -------------------------------------------------------------------------------- /src/old/git-idb.js: -------------------------------------------------------------------------------- 1 | var leveljs = require('level-js') 2 | 3 | 4 | module.exports = function (name) { 5 | 6 | var db = leveljs('name'); 7 | db.open(function onOpen() { 8 | console.log("onOpen", this, arguments); 9 | }); 10 | 11 | return { 12 | load: load, 13 | save: save, 14 | remove: remove, 15 | has: has, 16 | read: read, 17 | write: write, 18 | unlink: unlink, 19 | readdir: readdir, 20 | }; 21 | 22 | function load(hash, callback) { 23 | if (!callback) return load.bind(this, hash); 24 | console.log("load", hash); 25 | throw "TODO: Implement load"; 26 | } 27 | 28 | function save(hash, buffer, callback) { 29 | if (!callback) return save.bind(this, hash, buffer); 30 | throw "TODO: Implement save"; 31 | } 32 | 33 | function remove(hash, callback) { 34 | if (!callback) return remove.bind(this, hash); 35 | throw "TODO: Implement remove"; 36 | } 37 | 38 | function has(hash, callback) { 39 | if (!callback) return has.bind(this, hash); 40 | throw "TODO: Implement has"; 41 | } 42 | 43 | function read(path, callback) { 44 | if (!callback) return read.bind(this, path)';' 45 | } 46 | 47 | function readdir(target, callback) { 48 | if (!callback) return readdir.bind(this, target); 49 | // TODO: Implement properly 50 | callback(null, []); 51 | } 52 | 53 | }; 54 | -------------------------------------------------------------------------------- /src/old/git-webfs.js: -------------------------------------------------------------------------------- 1 | 2 | // Given a directory entry from the HTML5 FileSystem interface, return a git-fs instance. 3 | module.exports = function (root) { 4 | chroot.read = read; 5 | chroot.read = read; 6 | chroot.write = write; 7 | chroot.unlink = unlink; 8 | chroot.readlink = readlink; 9 | chroot.symlink = symlink; 10 | chroot.readdir = readdir; 11 | chroot.rmdir = rmdir; 12 | chroot.mkdir = mkdir; 13 | chroot.rename = rename; 14 | chroot.mkdir = mkdir; 15 | return chroot; 16 | 17 | function chroot(root) { 18 | var exports = wrap(chroot); 19 | exports.root = root; 20 | exports.stat = wrap(stat); 21 | exports.read = wrap(read); 22 | exports.write = wrap(write); 23 | exports.unlink = wrap(unlink); 24 | exports.readlink = wrap(readlink); 25 | exports.symlink = wrap(symlink); 26 | exports.readdir = wrap(readdir); 27 | exports.rmdir = wrap(rmdir); 28 | exports.mkdir = wrap(mkdir); 29 | exports.rename = wrap(rename, true); 30 | return exports; 31 | 32 | function wrap(fn, two) { 33 | return function () { 34 | arguments[0] = pathJoin(root, pathJoin("/", arguments[0])); 35 | if (two) arguments[1] = pathJoin(root, pathJoin("/", arguments[1])); 36 | return fn.apply(this, arguments); 37 | }; 38 | } 39 | } 40 | 41 | // Given a path, return a continuable for the stat object. 42 | function stat(path, callback) { 43 | if (!callback) return stat.bind(this, path); 44 | var errorHandler = wrapCallback(callback); 45 | root.getFile(path, {create: false}, function (fileEntry) { 46 | fileEntry.getMetadata(function (metaData) { 47 | var mtime = metaData.modificationTime / 1000; 48 | var mseconds = Math.floor(mtime); 49 | var mtime = [mseconds, Math.floor((mtime - mseconds) * 1000000000)]; 50 | callback(null, { 51 | ctime: mtime, // Not available on this platform 52 | mtime: mtime, 53 | dev: 0, // Not available on this platform 54 | ino: 0, // Not available on this platform 55 | mode: 0100644, // Not supported on this platform 56 | uid: 0, // Not available on this platform 57 | gid: 0, // Not available on this platform 58 | size: metaData.size 59 | }); 60 | }, errorHandler); 61 | }, errorHandler); 62 | } 63 | 64 | function read(path, encoding, callback) { 65 | if (typeof encoding === "function") { 66 | callback = encoding; 67 | encoding = undefined; 68 | } 69 | if (!callback) return read.bind(this, path, encoding); 70 | var errorHandler = wrapCallback(callback); 71 | root.getFile(path, {}, function (fileEntry) { 72 | fileEntry.file(function(file) { 73 | var reader = new FileReader(); 74 | reader.onloadend = function (evt) { 75 | var value = this.result; 76 | if (!encoding) { 77 | value = new Uint8Array(value); 78 | } 79 | callback(null, value); 80 | }; 81 | if (!encoding) { 82 | return reader.readAsArrayBuffer(file); 83 | } 84 | if (encoding === "binary") { 85 | return reader.readAsBinaryString(file); 86 | } 87 | return reader.readAsText(file, encoding); 88 | }, errorHandler); 89 | }, errorHandler); 90 | } 91 | 92 | function write(path, value, mode, callback) { 93 | if (!callback) return write.bind(this, path, value, mode); 94 | var errorHandler = wrapCallback(callback); 95 | root.getFile(path, {create: true}, function (fileEntry) { 96 | fileEntry.createWriter(function(fileWriter) { 97 | fileWriter.onwriteend = function (evt) { 98 | callback(); 99 | }; 100 | fileWriter.onerror = function (evt) { 101 | callback(new Error("Write failed: " + evt.toString())); 102 | }; 103 | fileWriter.write(new Blob([value])); 104 | }, errorHandler); 105 | }, errorHandler); 106 | } 107 | 108 | function unlink(path, callback) { 109 | if (!callback) return unlink.bind(this, path); 110 | var errorHandler = wrapCallback(callback); 111 | root.getFile(path, {create: false}, function (fileEntry) { 112 | fileEntry.remove(function () { 113 | callback(); 114 | }, errorHandler); 115 | }, errorHandler); 116 | } 117 | 118 | function readlink(path, callback) { 119 | if (!callback) return readlink.bind(this, path); 120 | callback(new Error("Symlinks are not supported in webfs")); 121 | } 122 | 123 | function symlink(path, value, callback) { 124 | if (!callback) return symlink.bind(this, path, value); 125 | callback(new Error("Symlinks are not supported in webfs")); 126 | } 127 | 128 | function readdir(path, callback) { 129 | if (!callback) return readdir.bind(this, path); 130 | var errorHandler = wrapCallback(callback); 131 | root.getDirectory(path, {create: false}, function (dirEntry) { 132 | var dirReader = dirEntry.createReader(); 133 | var entries = []; 134 | dirReader.readEntries(onRead, errorHandler); 135 | function onRead(results) { 136 | if (results.length) { 137 | for (var i = 0, l = results.length; i < l; ++i) { 138 | entries.push(results[i].name); 139 | } 140 | dirReader.readEntries(onRead, errorHandler); 141 | } 142 | else { 143 | callback(null, entries); 144 | } 145 | } 146 | }, errorHandler); 147 | } 148 | 149 | function rmdir(path, callback) { 150 | if (!callback) return rmdir.bind(this, path); 151 | var errorHandler = wrapCallback(callback); 152 | root.getDirectory(path, {create: false}, function (dirEntry) { 153 | dirEntry.remove(function () { 154 | callback(); 155 | }, function (e) { 156 | var err = new Error(formatError(e)); 157 | if (e.code === FileError.INVALID_MODIFICATION_ERR) { 158 | err.code = "ENOTEMPTY"; 159 | } 160 | callback(err); 161 | }); 162 | }, errorHandler); 163 | } 164 | 165 | function mkdir(path, callback) { 166 | if (!callback) return mkdir.bind(this, path); 167 | if (path === "." || path === "/") return callback(); 168 | var errorHandler = wrapCallback(callback); 169 | root.getDirectory(path, {create: true}, function (dirEntry) { 170 | callback(); 171 | }, errorHandler); 172 | } 173 | 174 | function rename(source, target, callback) { 175 | if (!callback) return rename.bind(this, source, target); 176 | var errorHandler = wrapCallback(callback); 177 | root.getFile(source, {}, function (fileEntry) { 178 | var dirName = dirname(target); 179 | root.getDirectory(dirName, {}, function (dirEntry) { 180 | fileEntry.moveTo(dirEntry); 181 | }, errorHandler); 182 | }, errorHandler); 183 | } 184 | 185 | 186 | }; 187 | 188 | function toArray(list) { 189 | return Array.prototype.slice.call(list || [], 0); 190 | } 191 | 192 | function wrapCallback(callback) { 193 | return function (err) { 194 | callback(formatError(err)); 195 | }; 196 | } 197 | 198 | function formatError(e) { 199 | var message; 200 | var code; 201 | switch (e.code) { 202 | case FileError.QUOTA_EXCEEDED_ERR: 203 | message = 'QUOTA_EXCEEDED_ERR'; 204 | code = "EQUOTA"; 205 | break; 206 | case FileError.NOT_FOUND_ERR: 207 | message = 'NOT_FOUND_ERR'; 208 | code = "ENOENT"; 209 | break; 210 | case FileError.SECURITY_ERR: 211 | message = 'SECURITY_ERR'; 212 | code = "ESECURITY"; 213 | break; 214 | case FileError.INVALID_MODIFICATION_ERR: 215 | message = 'INVALID_MODIFICATION_ERR'; 216 | code = "EINVLDMOD"; 217 | break; 218 | case FileError.INVALID_STATE_ERR: 219 | message = 'INVALID_STATE_ERR'; 220 | code = "EINVLDSTATE"; 221 | break; 222 | default: 223 | message = "Unknown Error"; 224 | code = "EUNKNOWN"; 225 | break; 226 | }; 227 | var err = new Error(message); 228 | err.code = code; 229 | return err; 230 | } 231 | 232 | function dirname(path) { 233 | if (path[path.length - 1] === "/") path = path.substr(0, path.length - 1); 234 | var index = path.lastIndexOf("/"); 235 | if (index === 0) return "/"; 236 | if (index > 0) return path.substr(0, index); 237 | return "."; 238 | } 239 | 240 | function pathJoin(base, path) { 241 | if (base[base.length - 1] === "/") base = base.substr(0, base.length - 1); 242 | if (path[0] === "/") path = path.substr(1); 243 | return base + "/" + path; 244 | } 245 | -------------------------------------------------------------------------------- /src/old/moz.js: -------------------------------------------------------------------------------- 1 | 2 | var platform ={ 3 | tcp: require('./moz-tcp.js'), 4 | sha1: require('./sha1.js'), 5 | bops: require('./bops/index.js'), 6 | // trace: require('./trace.js'), 7 | }; 8 | 9 | var jsGit = require('js-git')(platform); 10 | var gitRemote = require('git-net')(platform); 11 | var gitMemdb = require('./git-memdb.js'); 12 | var progressParser = require('./progress-parser.js'); 13 | 14 | var repos = []; 15 | 16 | require('./main.js')({ 17 | // opts are host, path, and description 18 | addRepo: function (opts, onProgress, callback) { 19 | var url = "git://" + opts.hostname + opts.pathname; 20 | var description = opts.description || url; 21 | var repo = jsGit(gitMemdb()); 22 | var remote = gitRemote(url); 23 | var name = opts.pathname; 24 | if (name[0] === "/") name = name.substr(1); 25 | if (name.substr(name.length - 4) === ".git") name = name.substr(0, name.length - 4); 26 | repo.name = name; 27 | repo.description = description; 28 | repos.push(repo); 29 | 30 | repo.fetch(remote, { 31 | includeTag: true, 32 | onProgress: progressParser(onProgress) 33 | }, function (err) { 34 | if (err) return callback(err); 35 | onProgress("Done"); 36 | callback(null, repo); 37 | }); 38 | 39 | }, 40 | getRepos: function (callback) { 41 | callback(null, repos); 42 | }, 43 | getHistoryStream: function (repo, callback) { 44 | repo.logWalk("HEAD", callback); 45 | }, 46 | getCommit: function (repo, hash, callback) { 47 | repo.loadAs("commit", hash, function (err, commit) { 48 | if (err) return callback(err); 49 | commit.hash = hash; 50 | callback(null, commit); 51 | }); 52 | }, 53 | getTree: function (repo, hash, callback) { 54 | repo.loadAs("tree", hash, function (err, tree) { 55 | if (err) return callback(err); 56 | tree.hash = hash; 57 | callback(null, tree); 58 | }); 59 | } 60 | }); 61 | -------------------------------------------------------------------------------- /src/old/webos.js: -------------------------------------------------------------------------------- 1 | /*global PalmSystem */ 2 | var ui = require('./ui.js'); 3 | ui.push = push; 4 | ui.pop = pop; 5 | ui.peer = peer; 6 | var pages = []; 7 | 8 | require('./web.js'); 9 | PalmSystem.stageReady(); 10 | 11 | function removeClass(node, name) { 12 | var classes = node.getAttribute("class").split(" "); 13 | var index = classes.indexOf(name); 14 | if (index >= 0) classes.splice(index, 1); 15 | node.setAttribute("class", classes.join(" ")); 16 | } 17 | 18 | function addClass(node, name) { 19 | var classes = node.getAttribute("class").split(" "); 20 | classes.push(name); 21 | node.setAttribute("class", classes.join(" ")); 22 | } 23 | 24 | function push(next) { 25 | var current = pages.length && pages[pages.length - 1]; 26 | if (current) { 27 | removeClass(current, "current"); 28 | addClass(current, "left"); 29 | } 30 | pages.push(next); 31 | if (!next.getAttribute("data-position")) { 32 | next.setAttribute("data-position", "right"); 33 | } 34 | removeClass(next, "right"); 35 | addClass(next, "current"); 36 | document.body.appendChild(next); 37 | setTimeout(function () { 38 | if (current) { 39 | onAnimationEnd(current); 40 | } 41 | onAnimationEnd(next); 42 | }, 400); 43 | } 44 | 45 | function pop() { 46 | if (!pages.length) return; 47 | var current = pages.pop(); 48 | var previous = pages.length && pages[pages.length - 1]; 49 | removeClass(current, "current"); 50 | addClass(current, "right"); 51 | if (previous) { 52 | removeClass(previous, "left"); 53 | addClass(previous, "current"); 54 | } 55 | setTimeout(function () { 56 | if (previous) { 57 | onAnimationEnd(previous); 58 | } 59 | document.body.removeChild(current); 60 | }, 400); 61 | } 62 | 63 | function onAnimationEnd(page) { 64 | var classes = page.getAttribute("class").split(" "); 65 | 66 | if (classes.indexOf("current") >= 0) { 67 | page.setAttribute("data-position", "current"); 68 | } 69 | else if (classes.indexOf("left") >= 0) { 70 | page.setAttribute("data-position", "left"); 71 | } 72 | } 73 | 74 | function peer(next) { 75 | // TODO: make this prettier 76 | pop(); 77 | push(next); 78 | } 79 | -------------------------------------------------------------------------------- /src/prism/prism-bash.js: -------------------------------------------------------------------------------- 1 | var Prism = require('./prism-core.js'); 2 | require('./prism-clike.js'); 3 | 4 | Prism.languages.bash = Prism.languages.extend('clike', { 5 | 'comment': { 6 | pattern: /(^|[^"{\\])(#.*?(\r?\n|$))/g, 7 | lookbehind: true 8 | }, 9 | 'string': { 10 | //allow multiline string 11 | pattern: /("|')(\\?[\s\S])*?\1/g, 12 | inside: { 13 | //'property' class reused for bash variables 14 | 'property': /\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^\}]+\})/g 15 | } 16 | }, 17 | 'keyword': /\b(if|then|else|elif|fi|for|break|continue|while|in|case|function|select|do|done|until|echo|exit|return|set|declare)\b/g 18 | }); 19 | 20 | Prism.languages.insertBefore('bash', 'keyword', { 21 | //'property' class reused for bash variables 22 | 'property': /\$([a-zA-Z0-9_#\?\-\*!@]+|\{[^}]+\})/g 23 | }); 24 | Prism.languages.insertBefore('bash', 'comment', { 25 | //shebang must be before comment, 'important' class from css reused 26 | 'important': /(^#!\s*\/bin\/bash)|(^#!\s*\/bin\/sh)/g 27 | }); 28 | -------------------------------------------------------------------------------- /src/prism/prism-c.js: -------------------------------------------------------------------------------- 1 | var Prism = require('./prism-core.js'); 2 | require('./prism-clike.js'); 3 | 4 | Prism.languages.c = Prism.languages.extend('clike', { 5 | 'keyword': /\b(asm|typeof|inline|auto|break|case|char|const|continue|default|do|double|else|enum|extern|float|for|goto|if|int|long|register|return|short|signed|sizeof|static|struct|switch|typedef|union|unsigned|void|volatile|while)\b/g, 6 | 'operator': /[-+]{1,2}|!=?|<{1,2}=?|>{1,2}=?|\->|={1,2}|\^|~|%|(&){1,2}|\|?\||\?|\*|\//g 7 | }); 8 | 9 | Prism.languages.insertBefore('c', 'keyword', { 10 | //property class reused for macro statements 11 | 'property': /#\s*[a-zA-Z]+/g 12 | }); 13 | -------------------------------------------------------------------------------- /src/prism/prism-clike.js: -------------------------------------------------------------------------------- 1 | var Prism = require('./prism-core.js'); 2 | 3 | Prism.languages.clike = { 4 | 'comment': { 5 | pattern: /(^|[^\\])(\/\*[\w\W]*?\*\/|(^|[^:])\/\/.*?(\r?\n|$))/g, 6 | lookbehind: true 7 | }, 8 | 'string': /("|')(\\?.)*?\1/g, 9 | 'class-name': { 10 | pattern: /((?:(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[a-z0-9_\.\\]+/ig, 11 | lookbehind: true, 12 | inside: { 13 | punctuation: /(\.|\\)/ 14 | } 15 | }, 16 | 'keyword': /\b(if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/g, 17 | 'boolean': /\b(true|false)\b/g, 18 | 'function': { 19 | pattern: /[a-z0-9_]+\(/ig, 20 | inside: { 21 | punctuation: /\(/ 22 | } 23 | }, 24 | 'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?)\b/g, 25 | 'operator': /[-+]{1,2}|!|<=?|>=?|={1,3}|(&){1,2}|\|?\||\?|\*|\/|\~|\^|\%/g, 26 | 'ignore': /&(lt|gt|amp);/gi, 27 | 'punctuation': /[{}[\];(),.:]/g 28 | }; 29 | -------------------------------------------------------------------------------- /src/prism/prism-coffeescript.js: -------------------------------------------------------------------------------- 1 | var Prism = require('./prism-core.js'); 2 | require('./prism-javascript.js'); 3 | 4 | Prism.languages.coffeescript = Prism.languages.extend('javascript', { 5 | 'block-comment': /([#]{3}\s*\r?\n(.*\s*\r*\n*)\s*?\r?\n[#]{3})/g, 6 | 'comment': /(\s|^)([#]{1}[^#^\r^\n]{2,}?(\r?\n|$))/g, 7 | 'keyword': /\b(this|window|delete|class|extends|namespace|extend|ar|let|if|else|while|do|for|each|of|return|in|instanceof|new|with|typeof|try|catch|finally|null|undefined|break|continue)\b/g 8 | }); 9 | 10 | Prism.languages.insertBefore('coffeescript', 'keyword', { 11 | 'function': { 12 | pattern: /[a-z|A-z]+\s*[:|=]\s*(\([.|a-z\s|,|:|{|}|\"|\'|=]*\))?\s*->/gi, 13 | inside: { 14 | 'function-name': /[_?a-z-|A-Z-]+(\s*[:|=])| @[_?$?a-z-|A-Z-]+(\s*)| /g, 15 | 'operator': /[-+]{1,2}|!|=?<|=?>|={1,2}|(&){1,2}|\|?\||\?|\*|\//g 16 | } 17 | }, 18 | 'attr-name': /[_?a-z-|A-Z-]+(\s*:)| @[_?$?a-z-|A-Z-]+(\s*)| /g 19 | }); 20 | -------------------------------------------------------------------------------- /src/prism/prism-core.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Prism: Lightweight, robust, elegant syntax highlighting 3 | * MIT license http://www.opensource.org/licenses/mit-license.php/ 4 | * @author Lea Verou http://lea.verou.me 5 | */ 6 | 7 | // Private helper vars 8 | var lang = /\blang(?:uage)?-(?!\*)(\w+)\b/i; 9 | 10 | var _ = module.exports = { 11 | util: { 12 | type: function (o) { 13 | return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1]; 14 | }, 15 | 16 | // Deep clone a language definition (e.g. to extend it) 17 | clone: function (o) { 18 | var type = _.util.type(o); 19 | 20 | switch (type) { 21 | case 'Object': 22 | var clone = {}; 23 | 24 | for (var key in o) { 25 | if (o.hasOwnProperty(key)) { 26 | clone[key] = _.util.clone(o[key]); 27 | } 28 | } 29 | 30 | return clone; 31 | 32 | case 'Array': 33 | return o.slice(); 34 | } 35 | 36 | return o; 37 | } 38 | }, 39 | 40 | languages: { 41 | extend: function (id, redef) { 42 | var lang = _.util.clone(_.languages[id]); 43 | 44 | for (var key in redef) { 45 | lang[key] = redef[key]; 46 | } 47 | 48 | return lang; 49 | }, 50 | 51 | // Insert a token before another token in a language literal 52 | insertBefore: function (inside, before, insert, root) { 53 | root = root || _.languages; 54 | var grammar = root[inside]; 55 | var ret = {}; 56 | 57 | for (var token in grammar) { 58 | 59 | if (grammar.hasOwnProperty(token)) { 60 | 61 | if (token == before) { 62 | 63 | for (var newToken in insert) { 64 | 65 | if (insert.hasOwnProperty(newToken)) { 66 | ret[newToken] = insert[newToken]; 67 | } 68 | } 69 | } 70 | 71 | ret[token] = grammar[token]; 72 | } 73 | } 74 | 75 | return root[inside] = ret; 76 | }, 77 | 78 | // Traverse a language definition with Depth First Search 79 | DFS: function(o, callback) { 80 | for (var i in o) { 81 | callback.call(o, i, o[i]); 82 | 83 | if (_.util.type(o) === 'Object') { 84 | _.languages.DFS(o[i], callback); 85 | } 86 | } 87 | } 88 | }, 89 | 90 | parse: function (text, language) { 91 | var grammar = _.languages[language]; 92 | if (!grammar) { 93 | return ["code", text]; 94 | } 95 | var tokens = _.tokenize(text, grammar); 96 | return ["pre.language-" + language, 97 | ["code.language-" + language, 98 | ["span.token.script", _.Token.toJson(tokens)] 99 | ] 100 | ]; 101 | }, 102 | 103 | tokenize: function(text, grammar) { 104 | var Token = _.Token; 105 | 106 | var strarr = [text]; 107 | 108 | var rest = grammar.rest; 109 | 110 | if (rest) { 111 | for (var token in rest) { 112 | grammar[token] = rest[token]; 113 | } 114 | 115 | delete grammar.rest; 116 | } 117 | 118 | tokenloop: for (var token in grammar) { 119 | if(!grammar.hasOwnProperty(token) || !grammar[token]) { 120 | continue; 121 | } 122 | 123 | var pattern = grammar[token], 124 | inside = pattern.inside, 125 | lookbehind = !!pattern.lookbehind, 126 | lookbehindLength = 0; 127 | 128 | pattern = pattern.pattern || pattern; 129 | 130 | for (var i=0; i text.length) { 135 | // Something went terribly wrong, ABORT, ABORT! 136 | break tokenloop; 137 | } 138 | 139 | if (str instanceof Token) { 140 | continue; 141 | } 142 | 143 | pattern.lastIndex = 0; 144 | 145 | var match = pattern.exec(str); 146 | 147 | if (match) { 148 | if(lookbehind) { 149 | lookbehindLength = match[1].length; 150 | } 151 | 152 | var from = match.index - 1 + lookbehindLength, 153 | match = match[0].slice(lookbehindLength), 154 | len = match.length, 155 | to = from + len, 156 | before = str.slice(0, from + 1), 157 | after = str.slice(to + 1); 158 | 159 | var args = [i, 1]; 160 | 161 | if (before) { 162 | args.push(before); 163 | } 164 | 165 | var wrapped = new Token(token, inside? _.tokenize(match, inside) : match); 166 | 167 | args.push(wrapped); 168 | 169 | if (after) { 170 | args.push(after); 171 | } 172 | 173 | Array.prototype.splice.apply(strarr, args); 174 | } 175 | } 176 | } 177 | 178 | return strarr; 179 | }, 180 | 181 | hooks: { 182 | all: {}, 183 | 184 | add: function (name, callback) { 185 | var hooks = _.hooks.all; 186 | 187 | hooks[name] = hooks[name] || []; 188 | 189 | hooks[name].push(callback); 190 | }, 191 | 192 | run: function (name, env) { 193 | var callbacks = _.hooks.all[name]; 194 | 195 | if (!callbacks || !callbacks.length) { 196 | return; 197 | } 198 | 199 | for (var i=0, callback; callback = callbacks[i++];) { 200 | callback(env); 201 | } 202 | } 203 | } 204 | }; 205 | 206 | var Token = _.Token = function(type, content) { 207 | this.type = type; 208 | this.content = content; 209 | }; 210 | 211 | Token.toJson = function(o, language, parent) { 212 | if (typeof o == 'string') { 213 | return o; 214 | } 215 | 216 | if (Array.isArray(o) && typeof o[0] !== "string") { 217 | return o.map(function(element) { 218 | return Token.toJson(element, language, o); 219 | }); 220 | } 221 | 222 | var env = { 223 | type: o.type, 224 | content: o.content && Token.toJson(o.content, language, parent), 225 | tag: 'span', 226 | classes: ['token', o.type], 227 | attributes: {}, 228 | language: language, 229 | parent: parent 230 | }; 231 | 232 | if (env.type == 'comment') { 233 | env.attributes['spellcheck'] = 'true'; 234 | } 235 | 236 | _.hooks.run('wrap', env); 237 | 238 | if (env.classes.length) { 239 | env.tag += "." + env.classes.join("."); 240 | } 241 | var element = [env.tag]; 242 | 243 | var attr = {}; 244 | var has = false; 245 | 246 | for (var name in env.attributes) { 247 | has = true; 248 | attr[name] = env.attributes[name] || ''; 249 | } 250 | if (has) element.push(attr); 251 | 252 | if (env.content) element.push(env.content); 253 | return element; 254 | 255 | }; 256 | 257 | -------------------------------------------------------------------------------- /src/prism/prism-cpp.js: -------------------------------------------------------------------------------- 1 | var Prism = require('./prism-core.js'); 2 | require('./prism-c.js'); 3 | 4 | Prism.languages.cpp = Prism.languages.extend('c', { 5 | 'keyword': /\b(alignas|alignof|asm|auto|bool|break|case|catch|char|char16_t|char32_t|class|compl|const|constexpr|const_cast|continue|decltype|default|delete|delete\[\]|do|double|dynamic_cast|else|enum|explicit|export|extern|float|for|friend|goto|if|inline|int|long|mutable|namespace|new|new\[\]|noexcept|nullptr|operator|private|protected|public|register|reinterpret_cast|return|short|signed|sizeof|static|static_assert|static_cast|struct|switch|template|this|thread_local|throw|try|typedef|typeid|typename|union|unsigned|using|virtual|void|volatile|wchar_t|while)\b/g, 6 | 'operator': /[-+]{1,2}|!=?|<{1,2}=?|>{1,2}=?|\->|:{1,2}|={1,2}|\^|~|%|(&){1,2}|\|?\||\?|\*|\/|\b(and|and_eq|bitand|bitor|not|not_eq|or|or_eq|xor|xor_eq)\b/g 7 | }); 8 | -------------------------------------------------------------------------------- /src/prism/prism-css-extras.js: -------------------------------------------------------------------------------- 1 | var Prism = require('./prism-core.js'); 2 | require('./prism-css.js'); 3 | 4 | Prism.languages.css.selector = { 5 | pattern: /[^\{\}\s][^\{\}]*(?=\s*\{)/g, 6 | inside: { 7 | 'pseudo-element': /:(?:after|before|first-letter|first-line|selection)|::[-\w]+/g, 8 | 'pseudo-class': /:[-\w]+(?:\(.*\))?/g, 9 | 'class': /\.[-:\.\w]+/g, 10 | 'id': /#[-:\.\w]+/g 11 | } 12 | }; 13 | 14 | Prism.languages.insertBefore('css', 'ignore', { 15 | 'hexcode': /#[\da-f]{3,6}/gi, 16 | 'entity': /\\[\da-f]{1,8}/gi, 17 | 'number': /[\d%\.]+/g, 18 | 'function': /(attr|calc|cross-fade|cycle|element|hsla?|image|lang|linear-gradient|matrix3d|matrix|perspective|radial-gradient|repeating-linear-gradient|repeating-radial-gradient|rgba?|rotatex|rotatey|rotatez|rotate3d|rotate|scalex|scaley|scalez|scale3d|scale|skewx|skewy|skew|steps|translatex|translatey|translatez|translate3d|translate|url|var)/ig 19 | }); -------------------------------------------------------------------------------- /src/prism/prism-css.js: -------------------------------------------------------------------------------- 1 | var Prism = require('./prism-core.js'); 2 | require('./prism-markup.js'); 3 | 4 | Prism.languages.css = { 5 | 'comment': /\/\*[\w\W]*?\*\//g, 6 | 'atrule': { 7 | pattern: /@[\w-]+?.*?(;|(?=\s*{))/gi, 8 | inside: { 9 | 'punctuation': /[;:]/g 10 | } 11 | }, 12 | 'url': /url\((["']?).*?\1\)/gi, 13 | 'selector': /[^\{\}\s][^\{\};]*(?=\s*\{)/g, 14 | 'property': /(\b|\B)[\w-]+(?=\s*:)/ig, 15 | 'string': /("|')(\\?.)*?\1/g, 16 | 'important': /\B!important\b/gi, 17 | 'ignore': /&(lt|gt|amp);/gi, 18 | 'punctuation': /[\{\};:]/g 19 | }; 20 | 21 | if (Prism.languages.markup) { 22 | Prism.languages.insertBefore('markup', 'tag', { 23 | 'style': { 24 | pattern: /(<|<)style[\w\W]*?(>|>)[\w\W]*?(<|<)\/style(>|>)/ig, 25 | inside: { 26 | 'tag': { 27 | pattern: /(<|<)style[\w\W]*?(>|>)|(<|<)\/style(>|>)/ig, 28 | inside: Prism.languages.markup.tag.inside 29 | }, 30 | rest: Prism.languages.css 31 | } 32 | } 33 | }); 34 | } -------------------------------------------------------------------------------- /src/prism/prism-javascript.js: -------------------------------------------------------------------------------- 1 | var Prism = require('./prism-core.js'); 2 | require('./prism-clike.js'); 3 | 4 | Prism.languages.javascript = Prism.languages.extend('clike', { 5 | 'keyword': /\b(var|let|if|else|while|do|for|return|in|instanceof|function|new|with|typeof|try|throw|catch|finally|null|break|continue)\b/g, 6 | 'number': /\b-?(0x[\dA-Fa-f]+|\d*\.?\d+([Ee]-?\d+)?|NaN|-?Infinity)\b/g 7 | }); 8 | 9 | Prism.languages.insertBefore('javascript', 'keyword', { 10 | 'regex': { 11 | pattern: /(^|[^/])\/(?!\/)(\[.+?]|\\.|[^/\r\n])+\/[gim]{0,3}(?=\s*($|[\r\n,.;})]))/g, 12 | lookbehind: true 13 | } 14 | }); 15 | 16 | if (Prism.languages.markup) { 17 | Prism.languages.insertBefore('markup', 'tag', { 18 | 'script': { 19 | pattern: /(<|<)script[\w\W]*?(>|>)[\w\W]*?(<|<)\/script(>|>)/ig, 20 | inside: { 21 | 'tag': { 22 | pattern: /(<|<)script[\w\W]*?(>|>)|(<|<)\/script(>|>)/ig, 23 | inside: Prism.languages.markup.tag.inside 24 | }, 25 | rest: Prism.languages.javascript 26 | } 27 | } 28 | }); 29 | } 30 | -------------------------------------------------------------------------------- /src/prism/prism-markup.js: -------------------------------------------------------------------------------- 1 | var Prism = require('./prism-core.js'); 2 | 3 | Prism.languages.markup = { 4 | 'comment': /<!--[\w\W]*?-->/g, 5 | 'prolog': /<\?.+?\?>/, 6 | 'doctype': /<!DOCTYPE.+?>/, 7 | 'cdata': /<!\[CDATA\[[\w\W]*?]]>/i, 8 | 'tag': { 9 | pattern: /<\/?[\w:-]+\s*(?:\s+[\w:-]+(?:=(?:("|')(\\?[\w\W])*?\1|\w+))?\s*)*\/?>/gi, 10 | inside: { 11 | 'tag': { 12 | pattern: /^<\/?[\w:-]+/i, 13 | inside: { 14 | 'punctuation': /^<\/?/, 15 | 'namespace': /^[\w-]+?:/ 16 | } 17 | }, 18 | 'attr-value': { 19 | pattern: /=(?:('|")[\w\W]*?(\1)|[^\s>]+)/gi, 20 | inside: { 21 | 'punctuation': /=|>|"/g 22 | } 23 | }, 24 | 'punctuation': /\/?>/g, 25 | 'attr-name': { 26 | pattern: /[\w:-]+/g, 27 | inside: { 28 | 'namespace': /^[\w-]+?:/ 29 | } 30 | } 31 | 32 | } 33 | }, 34 | 'entity': /&#?[\da-z]{1,8};/gi 35 | }; 36 | 37 | // Plugin to make entity title show the real entity, idea by Roman Komarov 38 | Prism.hooks.add('wrap', function(env) { 39 | 40 | if (env.type === 'entity') { 41 | env.attributes['title'] = env.content.replace(/&/, '&'); 42 | } 43 | }); -------------------------------------------------------------------------------- /src/server.js: -------------------------------------------------------------------------------- 1 | var http = require('http'); 2 | var WebSocketServer = require('ws').Server; 3 | var send = require('send'); 4 | var net = require('net'); 5 | var tls = require('tls'); 6 | var urlParse = require('url').parse; 7 | 8 | var server = http.createServer(onRequest); 9 | var wss = new WebSocketServer({server: server}); 10 | server.listen(8001); 11 | console.log("HTTP server listening on", server.address()); 12 | 13 | function onRequest(req, res) { 14 | var uri = urlParse(req.url); 15 | send(req, uri.pathname) 16 | .root(__dirname) 17 | .pipe(res); 18 | } 19 | 20 | wss.on('connection', function(ws) { 21 | var req = ws.upgradeReq; 22 | if (req.host !== req.origin) { 23 | ws.send("Only same origin allowed"); 24 | ws.close(); 25 | return; 26 | } 27 | var match = req.url.match(/^\/(tcp|tls)\/([^\/]+)\/([0-9]+)$/); 28 | if (!match) { 29 | ws.send("Invalid request url.\nMust be /:protocol/:host/:port"); 30 | ws.close(); 31 | return; 32 | } 33 | var protocol = match[1]; 34 | console.log("ws<->%s Client connected", protocol); 35 | var host = match[2]; 36 | var port = parseInt(match[3], 10); 37 | var base = protocol === "tcp" ? net : tls; 38 | console.log("Connecting to %s:%s", host, port) 39 | var s = base.connect({host: host, port: port}, onConnect); 40 | s.on("error", function (err) { 41 | try { 42 | ws.send(err); 43 | ws.close(); 44 | } catch (err) {} 45 | }); 46 | function onConnect() { 47 | ws.send("connect"); 48 | console.log("Connected to %s:%s", host, port); 49 | s.on("error", function (err) { 50 | try { 51 | ws.send(err); 52 | ws.close(); 53 | } catch (err) {} 54 | }); 55 | ws.on('message', function (message) { 56 | try { 57 | s.write(message); 58 | } catch (err) {} 59 | }); 60 | ws.on('close', function () { 61 | try { 62 | s.end(); 63 | } catch (err) {} 64 | }); 65 | s.on('data', function (chunk) { 66 | try { 67 | ws.send(chunk); 68 | } catch (err) {} 69 | }); 70 | s.on('close', function () { 71 | try { 72 | ws.close(); 73 | } catch (err) {} 74 | }); 75 | } 76 | }); 77 | -------------------------------------------------------------------------------- /src/style.less: -------------------------------------------------------------------------------- 1 | @import "transitions.less"; 2 | @import "icons.less"; 3 | @import "prism.css"; 4 | 5 | *, *:before, *:after { 6 | -moz-box-sizing: border-box; 7 | box-sizing: border-box; 8 | } 9 | 10 | html { 11 | font-size: 10px; 12 | font-family: MozTT, sans-serif; 13 | } 14 | 15 | body, .content, .page { 16 | margin: 0; 17 | font-size: 1rem; 18 | position: absolute; 19 | overflow: hidden; 20 | left: 0; 21 | right: 0; 22 | top: 0; 23 | bottom: 0; 24 | } 25 | 26 | code { 27 | font-size: 1.5rem; 28 | line-height: 1.8rem; 29 | } 30 | 31 | .page (@top, @bottom) { 32 | 33 | ul.content li { 34 | &:hover { 35 | background-color: fadeout(mix(@top, @bottom), 90%); 36 | } 37 | &:active { 38 | background-color: fadeout(mix(@top, @bottom), 70%); 39 | } 40 | } 41 | 42 | top: 0; 43 | height: 100%; 44 | bottom: 0; 45 | &> header { 46 | background: linear-gradient(to bottom, @top 0%, @bottom 100%); 47 | background-color: @bottom; 48 | border-bottom: 1px solid darken(@bottom, 10%); 49 | box-shadow: 0 0 20px rgba(0,0,0,0.5); 50 | color: #fff; 51 | text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3); 52 | height: 5rem; 53 | line-height: 5rem; 54 | text-overflow: ellipsis; 55 | white-space: nowrap; 56 | overflow: hidden; 57 | h1 { 58 | margin: 0 2rem; 59 | padding: 0; 60 | font-size: 2.5rem; 61 | } 62 | button { 63 | font-size: 25px; 64 | text-shadow: inherit; 65 | color: inherit; 66 | line-height: inherit; 67 | background: none; 68 | border: 0; 69 | height: 5rem; 70 | width: 5rem; 71 | padding: 0; 72 | z-index: 5; 73 | float: right; 74 | &.back { 75 | text-align: left; 76 | float: left; 77 | margin-left: -0.5rem; 78 | margin-right: -1.5rem; 79 | } 80 | &:hover { 81 | background-color: rgba(0, 0, 0, 0.1); 82 | } 83 | &:active { 84 | background-color: rgba(0, 0, 0, 0.7); 85 | color: #fff; 86 | } 87 | } 88 | } 89 | } 90 | 91 | .form(@normal, @hover, @active) { 92 | // width: 100%; 93 | display: block; 94 | padding-left: 2rem; 95 | padding-right: 2rem; 96 | label, button { 97 | text-overflow: ellipsis; 98 | white-space: nowrap; 99 | overflow: hidden; 100 | display: block; 101 | } 102 | label { 103 | clear: left; 104 | font-size: 1.8rem; 105 | line-height: 2.2rem; 106 | color: #000; 107 | padding: 1rem 0 0; 108 | } 109 | progress { 110 | display: block; 111 | margin: 0 0; 112 | height: 4rem; 113 | padding: 0 1rem; 114 | width: 100%; 115 | } 116 | p { 117 | font-size: 1.5rem; 118 | } 119 | input, button { 120 | display: block; 121 | margin: 0 0; 122 | height: 4rem; 123 | padding: 0 1rem; 124 | width: 100%; 125 | line-height: 4rem; 126 | font-size: 1.7rem; 127 | border: 1px solid #bbb; 128 | } 129 | input[type=radio] { 130 | float: left; 131 | width: 4rem; 132 | &+ label { 133 | float: left; 134 | padding-right: 1em; 135 | padding-left: 0.5em; 136 | clear: inherit; 137 | } 138 | } 139 | input[type=text], button { 140 | -webkit-appearance: none; -moz-appearance: none; 141 | border-radius: 0.5rem; 142 | } 143 | input[type=submit], button { 144 | border-radius: 1rem; 145 | margin: 1rem 0; 146 | color: #fff; 147 | text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.3); 148 | background: linear-gradient(to bottom, @normal 0%, darken(@normal, 10%) 100%); 149 | &:hover { 150 | background: linear-gradient(to bottom, @hover 0%, darken(@hover, 10%) 100%); 151 | } 152 | &:active { 153 | background: linear-gradient(to top, @active 0%, darken(@active, 10%) 100%); 154 | } 155 | &[disabled] { 156 | background: #888; 157 | opacity: 0.5; 158 | } 159 | 160 | } 161 | } 162 | 163 | .content { 164 | overflow-x: hidden; 165 | overflow-y: auto; 166 | &.header { 167 | margin-top: 5rem; 168 | } 169 | } 170 | 171 | ul.content { 172 | list-style-type: none; 173 | margin: 0; 174 | padding: 0; 175 | li { 176 | height: 6rem; 177 | line-height: 6rem; 178 | padding: 0 2rem; 179 | border-bottom: 1px solid rgba(0,0,0,0.1); 180 | .icon { 181 | line-height: 6rem; 182 | font-size: 2rem; 183 | &.right { 184 | float: right; 185 | margin-right: -10px; 186 | margin-left: 10px; 187 | } 188 | &.left { 189 | float: left; 190 | margin-left: -10px; 191 | margin-right: 10px; 192 | } 193 | } 194 | button { 195 | font-size: 2rem; 196 | color: inherit; 197 | line-height: inherit; 198 | background: none; 199 | border: 0; 200 | height: 100%; 201 | width: 5rem; 202 | float:right; 203 | margin-right: -15px; 204 | padding: 0; 205 | &:hover { 206 | background-color: rgba(0,0,0,0.1); 207 | } 208 | &:active { 209 | background-color: rgba(0,0,0,0.8); 210 | color: #fff; 211 | } 212 | } 213 | p { 214 | color: #5b5b5b; 215 | font-size: 1.5rem; 216 | line-height: 2rem; 217 | text-overflow: ellipsis; 218 | white-space: nowrap; 219 | overflow: hidden; 220 | display: block; 221 | margin: 0; 222 | } 223 | p:first-of-type { 224 | font-size: 1.8rem; 225 | line-height: 2.2rem; 226 | color: #000; 227 | padding: 1rem 0 0; 228 | } 229 | &> :first-child { 230 | margin-top: 0; 231 | } 232 | &> :last-child { 233 | margin-bottom: 0; 234 | } 235 | p.progress { 236 | display: none; 237 | span { 238 | margin-left: 0.5em; 239 | } 240 | } 241 | &.active { 242 | height: 8rem; 243 | p.progress { 244 | display: inherit; 245 | } 246 | } 247 | } 248 | } 249 | 250 | -------------------------------------------------------------------------------- /src/transitions.less: -------------------------------------------------------------------------------- 1 | @keyframes fadeIn { 2 | 0% { opacity: 0; } 3 | 100% { opacity: 1; } 4 | } 5 | @-webkit-keyframes fadeIn { 6 | 0% { opacity: 0; } 7 | 100% { opacity: 1; } 8 | } 9 | 10 | @keyframes rightToCurrent { 11 | 0% { transform: translateX(100%); } 12 | 100% { transform: translateX(0); } 13 | } 14 | @-webkit-keyframes rightToCurrent { 15 | 0% { -webkit-transform: translateX(100%); } 16 | 100% { -webkit-transform: translateX(0); } 17 | } 18 | 19 | @keyframes currentToLeft { 20 | 0% { transform: translateX(0); } 21 | 100% { transform: translateX(-100%); } 22 | } 23 | @-webkit-keyframes currentToLeft { 24 | 0% { -webkit-transform: translateX(0); } 25 | 100% { -webkit-transform: translateX(-100%); } 26 | } 27 | 28 | @keyframes currentToRight { 29 | 0% { transform: translateX(0); } 30 | 100% { transform: translateX(100%); } 31 | } 32 | @-webkit-keyframes currentToRight { 33 | 0% { -webkit-transform: translateX(0); } 34 | 100% { -webkit-transform: translateX(100%); } 35 | } 36 | 37 | @keyframes leftToCurrent { 38 | 0% { transform: translateX(-100%); } 39 | 100% { transform: translateX(0); } 40 | } 41 | @-webkit-keyframes leftToCurrent { 42 | 0% { -webkit-transform: translateX(-100%); } 43 | 100% { -webkit-transform: translateX(0); } 44 | } 45 | 46 | [data-position="none"] { 47 | &.current { 48 | animation: fadeIn 0.2s forwards; 49 | -webkit-animation: fadeIn 0.2s forwards; 50 | pointer-events: none; 51 | -webkit-pointer-events: none; 52 | } 53 | } 54 | [data-position="right"] { 55 | &.right { 56 | display: none; 57 | } 58 | &.current { 59 | animation: rightToCurrent 0.4s forwards; 60 | -webkit-animation: rightToCurrent 0.4s forwards; 61 | pointer-events: none; 62 | -webkit-pointer-events: none; 63 | } 64 | } 65 | 66 | [data-position="left"] { 67 | &.left { 68 | display: none; 69 | } 70 | &.current { 71 | animation: leftToCurrent 0.4s forwards; 72 | -webkit-animation: leftToCurrent 0.4s forwards; 73 | pointer-events: none; 74 | -webkit-pointer-events: none; 75 | } 76 | } 77 | 78 | [data-position="current"] { 79 | &.right { 80 | animation: currentToRight 0.4s forwards; 81 | -webkit-animation: currentToRight 0.4s forwards; 82 | pointer-events: none; 83 | -webkit-pointer-events: none; 84 | } 85 | &.left { 86 | animation: currentToLeft 0.4s forwards; 87 | -webkit-animation: currentToLeft 0.4s forwards; 88 | pointer-events: none; 89 | -webkit-pointer-events: none; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/web.js: -------------------------------------------------------------------------------- 1 | // Check if a new cache is available on page load. 2 | window.addEventListener('load', function(e) { 3 | window.applicationCache.addEventListener('updateready', function(e) { 4 | if (window.applicationCache.status == window.applicationCache.UPDATEREADY) { 5 | // Browser downloaded a new app cache. 6 | // if (confirm('A new version of this site is available. Load it?')) { 7 | window.location.reload(); 8 | // } 9 | } else { 10 | // Manifest didn't changed. Nothing new to server. 11 | } 12 | }, false); 13 | }, false); 14 | 15 | // Configure the platform 16 | var platform = { 17 | bops: require('bops'), 18 | // inflate: require('git-zlib/inflate.js'), 19 | // deflate: require('git-zlib/deflate.js'), 20 | tcp: require('websocket-tcp-client').tcp, 21 | tls: require('websocket-tcp-client').tls, 22 | }; 23 | platform.http = require('git-http')(platform); 24 | if (/\btrace\b/.test(document.location.search)) { 25 | platform.trace = require('./lib/trace.js'); 26 | } 27 | 28 | // Polyfill setImmediate 29 | if (!window.setImmediate) window.setImmediate = require('./lib/defer.js'); 30 | 31 | // Configure the backend 32 | var backend = require('./app/backend.js')({ 33 | repo: require('js-git'), 34 | remote: require('git-net')(platform), 35 | db: require('git-indexeddb')(platform), 36 | // db: require('git-memdb'), 37 | settings: { get: get, set: set }, 38 | }); 39 | 40 | function get(key) { 41 | return JSON.parse(window.localStorage.getItem(key)); 42 | } 43 | 44 | function set(key, value) { 45 | window.localStorage.setItem(key, JSON.stringify(value)); 46 | } 47 | 48 | // Launch the GUI 49 | require('./app/phone-ui.js')(backend); 50 | 51 | -------------------------------------------------------------------------------- /src/web.less: -------------------------------------------------------------------------------- 1 | @import "style.less"; 2 | .page { 3 | .page(#7d5, #6a4) 4 | } 5 | form.content { 6 | .form(#7a7, #696, #555); 7 | } 8 | -------------------------------------------------------------------------------- /src/webos.less: -------------------------------------------------------------------------------- 1 | @import "style.less"; 2 | .page { 3 | .page(#9ab, #678) 4 | } 5 | form.content { 6 | .form(#7a7, #696, #555); 7 | } 8 | --------------------------------------------------------------------------------