Showing preview only (1,855K chars total). Download the full file or copy to clipboard to get everything.
Repository: ikreymer/netcapsule
Branch: master
Commit: 8f1370c2ff91
Files: 127
Total size: 1.7 MB
Directory structure:
gitextract_on4keuxv/
├── .gitattributes
├── .gitignore
├── LICENSE.txt
├── README.md
├── app/
│ ├── Dockerfile
│ ├── browser_app.py
│ ├── config.yaml
│ ├── main.py
│ ├── requirements.txt
│ ├── run_browser
│ ├── static/
│ │ ├── dropdown.css
│ │ ├── main.css
│ │ ├── main.js
│ │ ├── normalize.css
│ │ ├── novnc/
│ │ │ ├── base.css
│ │ │ ├── base64.js
│ │ │ ├── black.css
│ │ │ ├── blue.css
│ │ │ ├── des.js
│ │ │ ├── display.js
│ │ │ ├── inflator.js
│ │ │ ├── input.js
│ │ │ ├── jsunzip.js
│ │ │ ├── keyboard.js
│ │ │ ├── keysym.js
│ │ │ ├── keysymdef.js
│ │ │ ├── logo.js
│ │ │ ├── playback.js
│ │ │ ├── rfb.js
│ │ │ ├── ui.js
│ │ │ ├── util.js
│ │ │ ├── websock.js
│ │ │ └── webutil.js
│ │ ├── shared.js
│ │ ├── skeleton.css
│ │ └── timemap.js
│ ├── templates/
│ │ ├── archives-list.html
│ │ ├── browser-select.html
│ │ ├── index.html
│ │ ├── replay.html
│ │ └── tracking.html
│ └── uwsgi.ini
├── archives.yaml
├── browsers/
│ ├── base-basilisk2-browser/
│ │ ├── Dockerfile
│ │ ├── basilisk_ii_prefs
│ │ ├── performa.rom
│ │ └── quadra650.rom
│ ├── base-browser/
│ │ ├── Dockerfile
│ │ ├── entry_point.sh
│ │ └── requirements.txt
│ ├── base-chromium/
│ │ ├── Dockerfile
│ │ ├── jwmrc
│ │ └── run.sh
│ ├── base-sheepshaver/
│ │ ├── Dockerfile
│ │ ├── NetscapePreferences
│ │ ├── SheepShaver
│ │ ├── newworld86.rom
│ │ ├── oldworld.rom
│ │ ├── run.sh
│ │ └── sheepshaver_prefs
│ ├── base-wine-browser/
│ │ ├── Dockerfile
│ │ └── proxy.reg
│ ├── build-browsers.sh
│ ├── build-me.sh
│ ├── chrome/
│ │ ├── Dockerfile
│ │ ├── jwmrc
│ │ └── run.sh
│ ├── chromium10/
│ │ └── Dockerfile
│ ├── chromium5/
│ │ └── Dockerfile
│ ├── firefox/
│ │ ├── Dockerfile
│ │ ├── ffprofile/
│ │ │ └── user.js
│ │ ├── jwmrc
│ │ └── run.sh
│ ├── ie4/
│ │ ├── Dockerfile
│ │ └── run.sh
│ ├── ie4.01-mac/
│ │ ├── Dockerfile
│ │ └── run.sh
│ ├── ie5.1-mac/
│ │ └── Dockerfile
│ ├── ie5.5/
│ │ ├── Dockerfile
│ │ ├── proxy.reg
│ │ └── run.sh
│ ├── lynx/
│ │ ├── Dockerfile
│ │ └── run.sh
│ ├── mosaic/
│ │ ├── Dockerfile
│ │ ├── fvwm2rc
│ │ ├── proxy
│ │ └── run.sh
│ ├── netscape/
│ │ ├── Dockerfile
│ │ ├── fvwm2rc
│ │ ├── install.sh
│ │ ├── preferences.js
│ │ └── run.sh
│ ├── netscape-mac-3.04/
│ │ ├── Dockerfile
│ │ └── run.sh
│ ├── netscape-mac-4.08/
│ │ ├── Dockerfile
│ │ ├── NetscapePreferences
│ │ └── run.sh
│ ├── netscape4.8-mac/
│ │ └── Dockerfile
│ ├── netscape4.8-win/
│ │ ├── Dockerfile
│ │ ├── prefs.js
│ │ └── run.sh
│ ├── safari3/
│ │ ├── Dockerfile
│ │ └── run.sh
│ ├── safari5/
│ │ ├── Dockerfile
│ │ └── run.sh
│ └── www/
│ ├── Dockerfile
│ ├── previous.cfg
│ ├── proxy.py
│ ├── run.sh
│ └── tars.iso.dmg
├── docker-compose.yml
├── nginx/
│ ├── Dockerfile
│ └── nginx.conf
├── pull-containers.sh
├── push-containers.sh
├── pywb/
│ ├── Dockerfile
│ ├── archivereplayview.py
│ ├── ca/
│ │ └── .gitignore
│ ├── config.yaml
│ ├── mementoquery.py
│ ├── redisclient.py
│ ├── requirements.txt
│ ├── templates/
│ │ ├── blank.html
│ │ ├── head_insert.html
│ │ └── not_found.html
│ └── uwsgi.ini
└── run-local.sh
================================================
FILE CONTENTS
================================================
================================================
FILE: .gitattributes
================================================
*.tar.gz filter=lfs diff=lfs merge=lfs -text
================================================
FILE: .gitignore
================================================
*.py[cod]
# C extensions
*.so
# Packages
*.egg
*.egg-info
dist
build
eggs
.eggs
parts
bin
var
sdist
develop-eggs
.installed.cfg
lib
lib64
__pycache__
#ignore shareddata
shareddata/*
# Installer logs
pip-log.txt
# Unit test / coverage reports
.coverage
.tox
nosetests.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
.vagrant
pywb/ca/certs/*
nginx/cache/
archives.gen.json
================================================
FILE: LICENSE.txt
================================================
Netcapsule is Copyright, 2015 Ilya Kreymer
Netcapsule source code is subject to the terms of the Mozilla Public
License, v. 2.0. If a copy of the MPL was not distributed with this
file, You can obtain one at http://mozilla.org/MPL/2.0/.
Netcapsule bundles the noVNC core library, under the ./app/static/include directory.
noVNC is Copyright (C) 2011 Joel Martin <github@martintribe.org>
The noVNC core library files are licensed under the MPL 2.0 (Mozilla
Public License 2.0). The noVNC core library is composed of the
Javascript code necessary for full noVNC operation. This includes (but
is not limited to):
include/base64.js
include/des.js
include/display.js
include/input.js
include/jsunzip.js
include/keysym.js
include/logo.js
include/playback.js
include/rfb.js
include/ui.js
include/util.js
include/websock.js
include/webutil.js
================================================
FILE: README.md
================================================
# OldWeb.today / Netcapsule #
## Browse old websites the old way ##
**This system is now deployed on http://oldweb.today/**
Netcapsule is a proof-of-concept system for browsing old web sites from existing web archives in old browsers, inside a modern browser.
When the Netcapsule web page is loaded, the old browser is loaded in an emulator-like setup (Docker container) connecting to an existing archiving service through an HTTP/S proxy (powered by [pywb] (https://github.com/ikreymer/pywb) software). The proxy configures the date/time
setting and proxies the content from the archive in its original form (whenever possible).
Any web archive (supporting CDX or Memento protocol interfaces) can be a source, and any browser running under Linux can be used.
Currently included browsers are:
- **Mosaic**
- *Mosaic 2.7 on Linux*
- **Netscape**
- *Netscape 3.04 on Mac*
- *Netscape 4.08 on Mac*
- *Netscape 4.79 on Linux*
- *Netscape 4.8 on Windows*
- *Netscape 4.8 on Mac*
- **Internet Explorer**
- *IE 4.0 on Windows*
- *IE 4.01 on Mac*
- *IE 5.1.7 on Mac*
- *IE 5.5 on Windows*
- **Safari**
- *Safari 3 on Windows*
- **Firefox**
- *Firefox 40 on Linux*
- **Chrome**
- *Chrome 46 on Linux*
The system allows user to navigate by both url and by time.
The date can be changed dynamically by entering a new date/time (in 14-digit format currently) and hitting *Update*.
The virtual browser must then be reloaded to reflect the new date/time settings.
Note: Netcapsule now requires latest Docker Engine 1.9 and Docker Compose 1.5
### Sample screenshots
*TODO: Update screenshots to new UI that is now on http://oldweb.today These screenshots reflect old prototype UI*
First US web site, from 1991, in NCSA Mosaic (archive courtesy of Stanford web archives), see http://www.slac.stanford.edu/history/earlyweb/firstpages.shtml)
(URL: `<HOST>/mosaic/1991/http://slacvm.slac.stanford.edu/FIND/default.html`)

Another early SLAC site, from 1994, in NCSA Mosaic (archive courtesy of Stanford web archives):
(URL: `<HOST>/mosaic/19940102000000/http://slacvm.slac.stanford.edu/FIND/slac.html`)

`http://geocities.com` from end of 1996, in Netscape Navigator 4 (archive courtesy of Internet Archive):
(URL: `<HOST>/netscape/1997/http://geocities.com/`)

Scrollbar - Composition Blue archive from 2006, in Internet Explorer 4 (archive courtesy of Internet Archive):
(URL: `<HOST>/ie4/2006/http://www.leegte.org/works/online/composition_blue/index.htm`)

`https://twitter.com/` from early 2015 in modern Firefox (archive courtesy of Internet Archive):
(URL: `<HOST>/firefox/201501/https://twitter.com/`)

### Running
To run locally, Docker and Docker Compose are required.
1. Clone this repo
2. Run `pull-containers.sh` to pull all existing containers from Docker Hub. Alternatively, you can run `browsers/build-containers.sh` to build them locally.
3. Run `run-local.sh`, which will invoke Docker Compose build and run steps. (Netcapsule uses the new Docker networking,
requiring Docker Engine 1.9 and Compose 1.5)
5. You can now access the different browsers under:
`http://<DOCKER_HOST>:9020/<BROWSER>/<TS>/<URL>` where `<BROWSER>` is one of `netscape`, `firefox`, `mosaic`, etc..
Netcapsule main app runs on port `9020` and can be changed in `docker-compose.yml`.
### How It Works
### Supported Browsers
The `browsers` directory corresponds to each supported browser and a `base-browser` image which serves as the base.
Current Browsers include:
* Moscaic (built from https://github.com/alandipert/ncsa-mosaic) `netcapsule/mosaic`
* Netscape 4.79 (built based on [instructions from here](https://www.ailis.de/~k/archives/75-Netscape-Navigator-4-on-Ubuntu-Linux-12.10.html) `netcapsule/netscape`
* Firefox 40 `netcapsule/firefox`
* Internet Explorer 4.02 `netcapsule/ie4` (using WINE, built with [steps from here](https://appdb.winehq.org/objectManager.php?sClass=version&iId=2743)
* Internet Explorer 5.5 `netcapsule/ie5.5` (using WINE, built with [steps from here](https://appdb.winehq.org/objectManager.php?sClass=version&iId=240)
* Safari 5 `netcapsule/safari5` (using WINE, built with `winetricks`)
* Netscape 4.08 `netcapsule/netscape-mac-4.08` on 68K MacOS Using [Basilik2](http://basilisk.cebix.net/)
* See [Browsers](browsers) for all currently available browsers.
#### Adding new browsers
To add a new browser, a new image should be created to extend `netcapsule/base-browser`.
This base images sets up a number of settings, such as Xvfb, VNC server, [noVNC](https://github.com/kanaka/noVNC), [Fluxbox](http://fluxbox.org)
For adding [WINE](https://www.winehq.org/) (Windows-based browsers), extend the `netcapsule/base-wine-browser` image which
provides a latest stable build of WINE.
For adding [Basilik2](http://basilisk.cebix.net/) (Mac-based browsers), extend the `netcapsule/base-basilisk2-browser` image
which includes an installation of BasiliskII.
* A `run.sh` file is usually used to start the browser.
* The `$URL` environment variable can be used to start browser at requested url.
* To read data from the archives, The HTTP (and optionally HTTPS) proxy servers should be set to
`netcapsule_pywb_1:8080`. These setting are browser dependent.
* A [fluxbox-apps](http://fluxbox.org/help/man-fluxbox-apps.php) config is usually provided to have Fluxbox start the browser in either a fullscreen or maximized state.
* Finally, add the browser to the `app/config.yaml` browser configuration and rebuild with `docker-compose build`
For convenience, the new browser Docker image build can be added to `browsers/build-browsers.sh` script.
Consult the existing browser setups for examples on how to add browsers.
### General Workflow
The system operates by running a simple web app as the entry point. When a request is received, an appropriate Docker container is created
for that user session. The session establishes a VNC connection (using noVNC) over a websocket (using websockify), which streams X session
running in the Docker container.
A small python app is also running on the container and is pinged periodically to indicate the session is still active, and to receive
information about the current web content viewed.
The HTTP/S proxy is a version of [pywb](https://github.com/ikreymer/pywb) which also stores additional state info in Redis, per Docker container. This allows the proxy to track which urls are loaded per session, which hosts are used, etc..
This idea is extended from the [Memento Reconstruct](https://github.com/ikreymer/memento-reconstruct)
### Configuration
The system can be configured to read from two different types of web archive sources, a Memento aggregator or a CDX server.
These sources are specified as two collections, `memento_reconstruct` and `single_archive` in the pywb `config.yaml` file,
in the pywb directory.
#### Memento API Source
This is the default configuration and uses the Memento JSON API (see: http://timetravel.mementoweb.org/guide/api/#memento-json) to read across multiple web archives which support the Memento protocol.
This setting uses the Memento aggregator hosted by Los Alamos National Laboratory (LANL). It uses as a spec the following list of archives: http://labs.mementoweb.org/aggregator_config/archivelist.xml
This can be changed by specifying a different file in the `pywb/config.yaml`
```
# Specify memento archivelist XML
memento_archive_xml: 'http://labs.mementoweb.org/aggregator_config/archivelist.xml'
```
The relevant Memento timegate and timemap settings are also set as:
```
memento_reconstruct:
index_paths:
- http://timetravel.mementoweb.org/api/json/
- http://labs.mementoweb.org/timemap/json/
```
This setting may point to a local version of the `archivelist.xml` which can be modified as needed.
The source is configured via the default collection in `config.yaml`
```
use_default_coll: 'memento_reconstruct'
```
Note: This system is partially adapted from the [Memento Reconstruct project](https://github.com/ikreymer/memento-reconstruct) deployed at
http://timetravel.mementoweb.org/ when clicking the *Reconstruct* option.
#### CDX API Source
Alternatively, any CDX server source can be used, including any deployment of pywb or OpenWayback which supports the [pywb CDX Server API](https://github.com/ikreymer/pywb/wiki/CDX-Server-API) or [OpenWayback CDX Server API](https://github.com/iipc/openwayback/blob/master/wayback-cdx-server-webapp/README.md)
For example, the default settings are configured to use the Internet Archive Wayback CDX Server as follows:
```
single_archive:
index_paths: 'http://web.archive.org/cdx/search/cdx'
archive_template: 'http://web.archive.org/web/{timestamp}id_/{url}'
archive_name: 'Internet Archive'
```
This can be altered to point to any other CDX server.
To enable this source, ensure that it is set as the default collection:
```
use_default_coll: 'single_archive'
```
### TODO List
This system is still an early proof-of-concept and there is much room for improvement. I would encourage and welcome contributions to this project any one interested. Here are some things that could be improved:
* UI improvements: Add a timeline of archived captures/mementos, indicating a timeline of archive copies, better data about source archives, etc...
* Actual emulator support: Run actual emulators in Docker to support browsers from different OSes and browsers that do not run on Linux
* Add support for Memento Link format API, to support archives that use this format (and do not support CDX).
* Input validation, eg. for Date/Time, URL, etc...
* Improved documentation.
* Better abstraction for Docker customization.
* Additional browsers, list supported browsers, etc...
* Any VNC configuration optimizations.
### LICENSE
This software is released under the [MPL 2.0](http://www.mozilla.org/MPL/2.0/). Created by Ilya Kreymer, September, 2015.
Portions under the `./app/static/include/*.js` which are part of the [noVNC](https://github.com/kanaka/noVNC) project, also licensed under [MPL 2.0](http://www.mozilla.org/MPL/2.0/).
See LICENSE.txt for details.
Honored to have received support for this project from:

and

================================================
FILE: app/Dockerfile
================================================
FROM python:2.7
MAINTAINER Ilya Kreymer <ikreymer at gmail.com>
WORKDIR /app
ADD requirements.txt /app/
RUN pip install -r requirements.txt
ADD main.py /app/
ADD uwsgi.ini /app/
ADD . /app/
VOLUME /app/static/
CMD ["uwsgi", "uwsgi.ini"]
#CMD ["python", "main.py"]
================================================
FILE: app/browser_app.py
================================================
from gevent import monkey, spawn, Timeout, sleep
monkey.patch_all()
from bottle import route, default_app, run, request, response, redirect
import requests
import logging
from redis import StrictRedis
import time
import sys
import os
import json
import traceback
from argparse import ArgumentParser
from bottle.ext.websocket import GeventWebSocketServer
from bottle.ext.websocket import websocket
from geventwebsocket.exceptions import WebSocketError
PYWB_HOST_PORT = os.environ.get('PYWB_HOST_PORT', 'netcapsule_pywb_1:8080')
LOCAL_REDIS_HOST = 'netcapsule_redis_1'
REDIS_HOST = os.environ.get('REDIS_HOST', LOCAL_REDIS_HOST)
BROWSER = os.environ.get('BROWSER')
my_ip = '127.0.0.1'
pywb_ip = None
start_url = None
curr_ts = None
redis = None
local_redis = None
stat_key_expire_time = 40
HOST = os.environ.get('HOSTNAME', 'localhost')
closed = False
def set_timestamp(timestamp):
params = {'ts': timestamp,
'ip': my_ip}
try:
r = requests.get('http://set.pywb.proxy/', params=params,
proxies={'http': PYWB_HOST_PORT,
'https': PYWB_HOST_PORT})
if r.status_code == 200:
global curr_ts
curr_ts = timestamp
return {'success': r.json()}
else:
return {'error': r.body}
except Exception as e:
return {'error': str(e)}
#@route('/set')
#def route_set_ts():
# ts = request.query.get('ts')
# res = set_timestamp(ts)
# return res
@route('/pingsock', apply=[websocket])
def pingsock(ws):
if ws:
spawn(receiver, ws)
last_data = None
sleep_timeout = 0.5
duration = int(redis.get('container_expire_secs'))
global closed
if closed:
try:
# reentrancy: user returned likely after back/forward
# using cached response
remainder = redis.get('c:' + HOST)
if remainder.startswith('REM:'):
old_time = int(remainder[len('REM:'):])
# subtract time user was away
old_time -= (stat_key_expire_time - redis.ttl('c:' + HOST))
duration = old_time
except Exception as e:
traceback.print_exc(e)
finally:
closed = False
redis.expire('c:' + HOST, duration)
logging.debug('Controller for: ' + BROWSER)
while not closed:
try:
data = get_update()
if data != last_data:
data['ttl'] = redis.ttl('c:' + HOST)
logging.debug('Sending ' + str(data))
ws.send(json.dumps(data))
last_data = data
# for comparison check
del last_data['ttl']
except WebSocketError as e:
traceback.print_exc(e)
mark_for_removal()
break
except Exception as e:
traceback.print_exc(e)
sleep(sleep_timeout)
def receiver(ws):
while not closed and ws:
try:
data = ws.receive()
logging.debug('Received ' + str(data))
if data is None:
continue
data = json.loads(data)
if data['ts']:
set_timestamp(data['ts'])
except WebSocketError as e:
traceback.print_exc()
mark_for_removal()
break
except Exception as e:
print(e)
#traceback.print_exc(e)
def mark_for_removal():
logging.debug('Marked for removal')
ttl = redis.ttl('c:' + HOST)
redis.setex('c:' + HOST, stat_key_expire_time, 'REM:' + str(ttl))
redis.rpush('remove_q', HOST + ' ' + my_ip)
global closed
closed = True
# just exit to shutdown container
# will prevent reentrancy, but much safer for now
sys.exit(0)
def shutdown():
duration = int(redis.get('container_expire_secs'))
sleep(duration + 10)
mark_for_removal()
def get_update():
# if not redis.hget('all_containers', HOST):
# return
# global expire_time
# expire_time = redis.get('container_expire_time')
# if not expire_time:
# expire_time = DEF_EXPIRE_TIME
# redis.expire('c:' + HOST, expire_time)
#ts = request.query.get('ts')
base_key = my_ip + ':' + curr_ts + ':'
pi = local_redis.pipeline(transaction=False)
pi.hgetall(base_key + 'urls')
pi.smembers(base_key + 'hosts')
pi.get(base_key + 'ref')
pi.get(base_key + 'base')
pi.expire(base_key + 'urls', stat_key_expire_time)
pi.expire(base_key + 'hosts', stat_key_expire_time)
pi.expire(base_key + 'ref', stat_key_expire_time)
pi.expire(base_key + 'base', stat_key_expire_time)
result = pi.execute()
# all urls
all_urls = result[0]
count = 0
min_sec = sys.maxint
max_sec = 0
for url, sec in all_urls.iteritems():
count += 1
sec = int(sec)
min_sec = min(sec, min_sec)
max_sec = max(sec, max_sec)
# all_hosts
all_hosts = result[1]
referrer = result[2]
base = result[3]
page_url = referrer
if not referrer:
# never sends referrer so just stick with initial url
# or thinks will get confusing..
if BROWSER == 'mosaic':
page_url = start_url
else:
page_url = base
page_url_secs = int(all_urls.get(page_url, 0))
return {'urls': count,
'req_ts': curr_ts,
'min_sec': min_sec,
'max_sec': max_sec,
'hosts': list(all_hosts),
'page_url': page_url,
'page_url_secs': page_url_secs,
}
@route('/')
def homepage():
global start_url
redirect(start_url, code=302)
PROXY_PAC = """
function FindProxyForURL(url, host)
{
if (isInNet(host, "10.0.2.2") || shExpMatch(url, "http://10.0.2.2:6082/*")) {
return "DIRECT";
}
return "PROXY %s:8080";
}
"""
@route('/proxy.pac')
def proxy():
response.content_type = 'application/x-ns-proxy-autoconfig'
return PROXY_PAC % pywb_ip
def do_init():
logging.basicConfig(format='%(asctime)s: [%(levelname)s]: %(message)s',
level=logging.DEBUG)
parser = ArgumentParser('netcapsule browser manager')
parser.add_argument('--my-ip')
parser.add_argument('--pywb-ip')
parser.add_argument('--start-url')
parser.add_argument('--start-ts')
r = parser.parse_args()
global my_ip
my_ip = r.my_ip
global pywb_ip
pywb_ip = r.pywb_ip
global start_url
start_url = r.start_url
if '://' not in start_url:
start_url = 'http://' + start_url
# not used here for now
global curr_ts
curr_ts = r.start_ts
global redis
redis = StrictRedis(REDIS_HOST)
global local_redis
if REDIS_HOST != LOCAL_REDIS_HOST:
local_redis = StrictRedis(LOCAL_REDIS_HOST)
else:
local_redis = redis
# set initial url
#base_key = my_ip + ':' + curr_ts + ':'
#local_redis.set(base_key + 'r', start_url)
return default_app()
application = do_init()
@application.hook('after_request')
def enable_cors():
response.headers['Access-Control-Allow-Origin'] = '*'
response.headers['Access-Control-Allow-Methods'] = 'PUT, GET, POST, DELETE, OPTIONS'
response.headers['Access-Control-Allow-Headers'] = 'Origin, Accept, Content-Type, X-Requested-With, X-CSRF-Token'
spawn(shutdown)
if __name__ == "__main__":
run(host='0.0.0.0', port='6082', server=GeventWebSocketServer)
================================================
FILE: app/config.yaml
================================================
api_version: '1.21'
vnc_port: 6080
cmd_port: 6082
max_containers: 100
init_container_expire_secs: 30
full_container_expire_secs: 600
throttle_max_avg: 0.60
throttle_samples: 10
throttle_expire_secs: 60
queue_expire_secs: 30
remove_expired_secs: 30
image_prefix: netcapsule
default_browser: firefox
random_page_file: /app/urls.txt
redirect_paths:
'': firefox
'ie': ie5.5
'ns': nswin
'safari3': safari
browsers:
# firefox
- id: firefox
name: 'Mozilla Firefox'
os: 'Linux'
version: '40'
icon: 'firefox.png'
path: firefox
about: 'https://en.wikipedia.org/wiki/Firefox'
# chrome
- id: chrome
name: 'Google Chrome'
os: 'Linux'
version: '46'
icon: 'chrome.png'
path: chrome
about: 'https://en.wikipedia.org/wiki/Google_Chrome'
- id: chromium5
name: 'Chromium'
os: 'Linux'
version: '5'
icon: 'chromium.png'
path: chromium5
about: 'https://en.wikipedia.org/wiki/Chromium_(web_browser)'
- id: chromium10
name: 'Chromium'
os: 'Linux'
version: '10'
icon: 'chromium.png'
path: chromium10
about: 'https://en.wikipedia.org/wiki/Chromium_(web_browser)'
skip_random: true
# Lynx
- id: lynx
name: Lynx
os: 'Linux'
icon: 'lynx.png'
path: lynx
version: '2.8.9'
about: 'https://en.wikipedia.org/wiki/Lynx_(web_browser)'
skip_random: true
# WWW
- id: www
name: 'WWW'
os: 'NextSTEP'
icon: 'next-www.png'
path: WWW
version: '1.0'
about: 'https://en.wikipedia.org/wiki/WorldWideWeb'
req_width: 1120
req_height: 856
skip_random: true
# mosaic
- id: mosaic
name: 'NCSA Mosaic'
os: 'Linux'
version: '2.2'
icon: 'mosaic.png'
path: mosaic
about: 'https://en.wikipedia.org/wiki/Mosaic_%28web_browser%29'
# netscape
- id: netscape
name: 'Netscape Navigator'
os: 'Linux'
version: '4.79'
icon: 'win32-ns4.png'
path: nslinux
about: 'https://en.wikipedia.org/wiki/Netscape_%28web_browser%29'
- id: netscape4.8-win
name: 'Netscape Navigator'
os: 'Windows'
version: '4.8'
icon: 'win32-ns4.png'
path: nswin
about: 'https://en.wikipedia.org/wiki/Netscape_%28web_browser%29'
- id: netscape-mac-3.04
name: 'Netscape Navigator'
os: 'Macintosh'
version: '3.04'
icon: 'mac-ns2-3.png'
path: nsmac3
about: 'https://en.wikipedia.org/wiki/Netscape_%28web_browser%29'
- id: netscape-mac-4.08
name: 'Netscape Navigator'
os: 'Macintosh'
version: '4.08'
icon: 'mac-ns4.png'
path: nsmac4
about: 'https://en.wikipedia.org/wiki/Netscape_%28web_browser%29'
- id: netscape4.8-mac
name: 'Netscape Navigator'
os: 'Macintosh'
version: '4.8'
icon: 'mac-ns4.png'
path: nsmac4.8
about: 'https://en.wikipedia.org/wiki/Netscape_%28web_browser%29'
# Internet Explorer
- id: ie4.01-mac
name: 'Internet Explorer'
os: 'Macintosh'
version: '4.01'
icon: 'mac-ie4.png'
path: ie4mac
about: 'https://en.wikipedia.org/wiki/Internet_Explorer_for_Mac#Internet_Explorer_4.0_for_Macintosh'
- id: ie5.1-mac
name: 'Internet Explorer'
os: 'Macintosh'
version: '5.1.7'
icon: 'mac-ie5.1.png'
path: ie5mac
about: 'https://en.wikipedia.org/wiki/Internet_Explorer_for_Mac#Internet_Explorer_5_Macintosh_Edition'
- id: ie4
name: 'Internet Explorer'
os: 'Windows'
version: '4.01'
icon: 'win32-ie4.png'
path: ie4
about: 'https://en.wikipedia.org/wiki/Internet_Explorer_4'
- id: ie5.5
name: 'Internet Explorer'
os: 'Windows'
version: '5.5'
icon: 'win32-ie5.5.png'
path: ie5.5
about: 'https://en.wikipedia.org/wiki/Internet_Explorer_5'
# Safari
- id: safari3
name: 'Apple Safari'
os: 'Windows'
version: '3.2.3'
icon: 'safari.png'
path: safari
about: 'https://en.wikipedia.org/wiki/Safari_(web_browser)#Safari_3'
- id: safari5
name: 'Apple Safari'
os: 'Windows'
version: '5.0'
icon: 'safari.png'
path: safari5
about: 'https://en.wikipedia.org/wiki/Safari_%28web_browser%29#Safari_5'
================================================
FILE: app/main.py
================================================
from docker.client import Client
from docker.utils import kwargs_from_env
from bottle import route, run, template, request, default_app, jinja2_view
from bottle import redirect, static_file, response
import os
import base64
import datetime
import time
import re
import atexit
import redis
import yaml
import json
import random
import itertools
import traceback
from uwsgidecorators import timer, mulefunc
import uwsgi
#=============================================================================
class DockerController(object):
def _load_config(self):
with open('./config.yaml') as fh:
config = yaml.load(fh)
return config
def __init__(self):
config = self._load_config()
self.LOCAL_REDIS_HOST = 'netcapsule_redis_1'
self.REDIS_HOST = os.environ.get('REDIS_HOST', self.LOCAL_REDIS_HOST)
self.PYWB_HOST = os.environ.get('PYWB_HOST', 'netcapsule_pywb_1')
self.C_EXPIRE_TIME = config['init_container_expire_secs']
self.Q_EXPIRE_TIME = config['queue_expire_secs']
self.REMOVE_EXP_TIME = config['remove_expired_secs']
self.VERSION = config['api_version']
self.VNC_PORT = config['vnc_port']
self.CMD_PORT = config['cmd_port']
self.MAX_CONT = config['max_containers']
self.image_prefix = config['image_prefix']
self.browser_list = config['browsers']
self.browser_paths = {}
for browser in self.browser_list:
path = browser['path']
if path in self.browser_paths:
raise Exception('Already a browser for path {0}'.format(path))
self.browser_paths[path] = browser
self.default_browser = config['default_browser']
self.redirect_paths = config['redirect_paths']
self.randompages = []
try:
with open(config['random_page_file']) as fh:
self.randompages = list([line.rstrip() for line in fh])
except Exception as e:
print(e)
self.redis = redis.StrictRedis(host=self.REDIS_HOST)
self.redis.setnx('next_client', '1')
self.redis.setnx('max_containers', self.MAX_CONT)
self.redis.setnx('num_containers', '0')
self.redis.setnx('cpu_auto_adjust', 5.5)
throttle_samples = config['throttle_samples']
self.redis.setnx('throttle_samples', throttle_samples)
throttle_max_avg = config['throttle_max_avg']
self.redis.setnx('throttle_max_avg', throttle_max_avg)
self.redis.setnx('container_expire_secs',
config['full_container_expire_secs'])
self.T_EXPIRE_TIME = config['throttle_expire_secs']
if os.path.exists('/var/run/docker.sock'):
self.cli = Client(base_url='unix://var/run/docker.sock',
version=self.VERSION)
else:
kwargs = kwargs_from_env(assert_hostname=False)
kwargs['version'] = self.VERSION
self.cli = Client(**kwargs)
def _get_host_port(self, info, port, default_host):
info = info['NetworkSettings']['Ports'][str(port) + '/tcp']
info = info[0]
host = info['HostIp']
if host == '0.0.0.0' and default_host:
host = default_host
return host + ':' + info['HostPort']
def timed_new_container(self, browser, env, host, client_id):
start = time.time()
info = self.new_container(browser, env, host)
end = time.time()
dur = end - start
time_key = 't:' + client_id
self.redis.setex(time_key, self.T_EXPIRE_TIME, dur)
throttle_samples = int(self.redis.get('throttle_samples'))
print('INIT DUR: ' + str(dur))
self.redis.lpush('init_timings', time_key)
self.redis.ltrim('init_timings', 0, throttle_samples - 1)
return info
def new_container(self, browser_id, env=None, default_host=None):
browser = self.browser_paths.get(browser_id)
# get default browser
if not browser:
browser = self.browser_paths.get(self.default_browser)
if browser.get('req_width'):
env['SCREEN_WIDTH'] = browser.get('req_width')
if browser.get('req_height'):
env['SCREEN_HEIGHT'] = browser.get('req_height')
container = self.cli.create_container(image=self.image_prefix + '/' + browser['id'],
ports=[self.VNC_PORT, self.CMD_PORT],
environment=env,
)
short_id = None
try:
id_ = container.get('Id')
short_id = id_[:12]
res = self.cli.start(container=id_,
port_bindings={self.VNC_PORT: None, self.CMD_PORT: None},
volumes_from=['netcapsule_shared_data_1'],
network_mode='netcapsule',
)
info = self.cli.inspect_container(id_)
ip = info['NetworkSettings']['IPAddress']
if not ip:
ip = info['NetworkSettings']['Networks']['netcapsule']['IPAddress']
#self.redis.hset('all_containers', short_id, ip)
self.redis.incr('num_containers')
self.redis.setex('c:' + short_id, self.C_EXPIRE_TIME, 1)
return {'vnc_host': self._get_host_port(info, self.VNC_PORT, default_host),
'cmd_host': self._get_host_port(info, self.CMD_PORT, default_host),
}
except Exception as e:
if short_id:
self.remove_container(short_id)
traceback.print_exc(e)
return {}
def remove_container(self, short_id, ip=None):
print('REMOVING ' + short_id)
try:
self.cli.remove_container(short_id, force=True)
except Exception as e:
print(e)
#self.redis.hdel('all_containers', short_id)
self.redis.delete('c:' + short_id)
if ip:
ip_keys = self.redis.keys(ip + ':*')
for key in ip_keys:
self.redis.delete(key)
def remove_expired(self):
print('Start Expired Check')
while True:
try:
value = self.redis.blpop('remove_q', 1000)
if not value:
continue
short_id, ip = value[1].split(' ')
self.remove_container(short_id, ip)
self.redis.decr('num_containers')
except Exception as e:
traceback.print_exc(e)
def check_nodes(self):
print('Check Nodes')
try:
scale = self.redis.get('cpu_auto_adjust')
if not scale:
return
info = self.cli.info()
cpus = int(info.get('NCPU', 0))
if cpus <= 1:
return
total = int(float(scale) * cpus)
self.redis.set('max_containers', total)
except Exception as e:
print(e)
def add_new_client(self):
client_id = self.redis.incr('clients')
enc_id = base64.b64encode(os.urandom(27))
self.redis.setex('cm:' + enc_id, self.Q_EXPIRE_TIME, client_id)
self.redis.setex('q:' + str(client_id), self.Q_EXPIRE_TIME, 1)
return enc_id, client_id
def am_i_next(self, enc_id):
client_id = None
if enc_id:
self.redis.expire('cm:' + enc_id, self.Q_EXPIRE_TIME)
client_id = self.redis.get('cm:' + enc_id)
if not client_id:
enc_id, client_id = self.add_new_client()
client_id = int(client_id)
next_client = int(self.redis.get('next_client'))
# not next client
if client_id != next_client:
# if this client expired, delete it from queue
if not self.redis.get('q:' + str(next_client)):
print('skipping expired', next_client)
self.redis.incr('next_client')
# missed your number somehow, get a new one!
if client_id < next_client:
enc_id, client_id = self.add_new_client()
diff = client_id - next_client
if self.throttle():
self.redis.expire('q:' + str(client_id), self.Q_EXPIRE_TIME)
return enc_id, client_id - next_client
#num_containers = self.redis.hlen('all_containers')
num_containers = int(self.redis.get('num_containers'))
max_containers = self.redis.get('max_containers')
max_containers = int(max_containers) if max_containers else self.MAX_CONT
if diff <= (max_containers - num_containers):
self.redis.incr('next_client')
return enc_id, -1
else:
self.redis.expire('q:' + str(client_id), self.Q_EXPIRE_TIME)
return enc_id, client_id - next_client
def throttle(self):
timings = self.redis.lrange('init_timings', 0, -1)
if not timings:
return False
timings = self.redis.mget(*timings)
avg = 0
count = 0
for val in timings:
if val is not None:
avg += float(val)
count += 1
if count == 0:
return False
avg = avg / count
print('AVG: ', avg)
throttle_max_avg = float(self.redis.get('throttle_max_avg'))
if avg >= throttle_max_avg:
print('Throttling, too slow...')
return True
return False
def do_init(self, browser, url, ts, host, client_id):
env = {}
env['URL'] = url
env['TS'] = ts
env['SCREEN_WIDTH'] = os.environ.get('SCREEN_WIDTH')
env['SCREEN_HEIGHT'] = os.environ.get('SCREEN_HEIGHT')
env['REDIS_HOST'] = dc.REDIS_HOST
env['PYWB_HOST_PORT'] = dc.PYWB_HOST + ':8080'
env['BROWSER'] = browser
info = self.timed_new_container(browser, env, host, client_id)
info['queue'] = 0
return info
def get_randompage(self):
if not self.randompages:
return '/'
url, ts = random.choice(self.randompages).split(' ', 1)
print(url, ts)
path = self.get_random_browser()
return '/' + path + '/' + ts + '/' + url
def get_random_browser(self):
while True:
id_ = random.choice(self.browser_paths.keys())
if self.browser_paths[id_].get('skip_random'):
continue
return id_
# Routes Below
# ===================
@route('/static/<filepath:path>')
def server_static(filepath):
return static_file(filepath, root='/app/static/')
@route(['/init_browser'])
def init_container():
host = request.environ.get('HTTP_HOST', '')
host = host.split(':')[0]
client_id, queue_pos = dc.am_i_next(request.query.get('id'))
if queue_pos < 0:
browser = request.query.get('browser')
url = request.query.get('url')
ts = request.query.get('ts')
resp = dc.do_init(browser, url, ts, host, client_id)
else:
resp = {'queue': queue_pos, 'id': client_id}
response.headers['Cache-Control'] = 'no-cache, no-store, max-age=0, must-revalidate'
return resp
@route(['/', '/index.html', '/index.htm'])
@jinja2_view('index.html', template_lookup=['templates'])
def index():
return {}
@route(['/<path>/<ts:re:[0-9-]+>/<url:re:.*>', '/<path>/<url:re:.*>'])
@jinja2_view('replay.html', template_lookup=['templates'])
def route_load_url(path='', url='', ts=''):
browser = dc.browser_paths.get(path)
if not browser:
if path == 'random':
path = dc.get_random_browser()
else:
path = dc.redirect_paths.get(path)
if not path:
path = dc.default_browser
if ts:
ts += '/'
redirect('/' + path + '/' + ts + url)
if not ts:
ts = re.sub('[ :-]', '', str(datetime.datetime.utcnow()).split('.')[0])
browser_info = dict(name=browser['name'],
os=browser['os'],
version=browser['version'],
icon=browser['icon'])
return {'coll': path,
'url': url,
'ts': ts,
'browser': browser_info}
@route('/random')
def randompage():
redirect(dc.get_randompage())
# Init
# ======================
dc = DockerController()
application = default_app()
def init_cleanup_timer(dc, expire_time):
@mulefunc(1)
def check_abandonded():
dc.remove_expired()
@timer(30, target='mule2')
def check_node(signum):
dc.check_nodes()
check_abandonded()
init_cleanup_timer(dc, dc.REMOVE_EXP_TIME)
#run(host='0.0.0.0', port='9020')
================================================
FILE: app/requirements.txt
================================================
docker-py
bottle
gevent
uwsgi
jinja2
redis
pyyaml
================================================
FILE: app/run_browser
================================================
#!/bin/bash
while 'true'
do
"$@"
sleep 0.2
done
================================================
FILE: app/static/dropdown.css
================================================
/* dropdown menus */
.dropdown {
width: 303px;
height: 58px;
background-color: white;
cursor: pointer;
margin-bottom: 4px;
border: 1px solid #d1d1d1;
border-radius: 4px;
box-shadow: 4px 4px 8px rgba(0,0,0,0.4);
position: relative;
font-size: 13px;
}
.dropdown-button {
position: absolute;
right:0;
top:0;
bottom: 0;
width: 33px;
padding-top: 19px;
padding-left: 19px;
}
/* button triangle */
.arrow-down {
width: 0;
height: 0;
margin-right: 7px;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-top: 8px solid #222;
display: inline-block;
}
.dropdown:hover .arrow-down {border-top-color: #33C3F0}
.dropdown-shown .arrow-down {border-top-color: #1EAEDB} /* when selector is visible*/
.dropdown.dropdown-shown {
border: 1px solid #1EAEDB;
}
/* close menu via × */
/* browser selector */
th.menu-selector-close {
text-align: right !important;
cursor: pointer;
font-size: 15px;
}
/* date selector */
div.menu-selector-close {
cursor: pointer;
font-size: 15px;
position: absolute;
right: 10px;
top: 7px;
}
/* unfolded menus */
/* Both Selector Menus */
.selector-menu {
width: 304px;
position: absolute;
float: left;
background-color: white;
box-shadow: 4px 4px 8px rgba(0,0,0,0.4);
display: none;
}
.menu-selector-close span { margin-right: 8px; }
.menu-selector-close:hover { color: #33C3F0; }
/* Browser selector */
#browser-icon-group {
display: block;
position: relative;
float: left;
padding: 6px 8px;
}
/* version number of browser */
#browser-icon-group label {
display: block;
margin: 0 auto;
text-align: center;
font-weight: bold;
margin-top: 2px;
line-height: 0;
}
#browser-icon {
width: 32px;
height: 32px;
}
#browser-text {
line-height: 58px;
}
/* Datetime selector */
#datetime-dropdown {}
#datetime-dropdown .dropdown-button {
border-left: 1px solid #eee;
}
#datetime { /* text input field */
background-color: transparent;
display: block;
width: 224px;
height: 100%;
padding-left: 25px;
border: 0;
}
/* hover effect only needed when pointer is over button, not
all of the dropdown */
#datetime-dropdown:hover .arrow-down {border-top-color: #222}
#datetime-dropdown .dropdown-button:hover .arrow-down {border-top-color: #33C3F0}
.dropdown-shown .arrow-down {border-top-color: #1EAEDB} /* when selector is visible*/
/* menus that unwrap when the dropdowns are opened */
/* Browser Selector */
#browser-selector {
top: 40px;
left: 0px;
font-size: 11px;
}
#browser-selector table {
border-spacing: 0;
margin-bottom: 0px;
}
#browser-selector thead {height: 40px;}
/* table top row */
#browser-selector thead th {
width: 56px;
padding: 6px 10px;
text-align: center;
}
/* background colors / stripes */
#browser-selector tr.r-odd {background: #f8f8f8;}
#browser-selector tr.r-odd td:nth-child(odd) {background: #f0f0f0;}
#browser-selector tr.r-even {background: #e8e8e8;}
#browser-selector tr.r-even td:nth-child(odd) {background: #e0e0e0;}
#browser-selector tbody th {
padding: 3px 6px 0;
text-align: center;
}
#browser-selector td {
border-bottom: 0;
padding: 8px 10px 2px;
}
#browser-selector th {font-weight: normal; background: #fff;}
#browser-selector tbody th {text-align: left; vertical-align: top;}
/* selection colors */
#browser-selector td:not(:empty):hover {background-image: radial-gradient(circle, rgba(0,0,0,0), rgba(51,195,240,.5)) !important;}
#browser-selector td.selected,
#browser-selector td.selected:hover
{background-image: radial-gradient(circle, #fff, #1EAEDB) !important;}
/* display of browser icons */
#browser-selector img {width: 32px; height: 32px;}
#browser-selector img,
#browser-selector label {
display: block;
margin: 0 auto;
}
#browser-selector label {
text-align: center;
line-height: inherit;
font-weight: bold;
} /* browser version numbers*/
#browser-selector input {display: none;} /* make radio buttons invisible */
/* Datetime selector */
#datetime-info {
padding: 10px 0 0 8px;
font-size: 11px;
}
/* Sparklines */
#spark {
width: auto;
height: auto;
text-align: left;
margin-left: 10px;
position: relative;
font-size: 11px;
}
rect.pane {
cursor: pointer;
fill: transparent;
}
.axis {
font: inherit;
fill: #222;
}
.axis path,
.axis line {
fill: none;
stroke: #222;
shape-rendering: crispEdges;
}
.axis text {font-size: 11px;}
.bar {fill: rgba(153,153,153,.25)}
.tooltip {
position: absolute;
border: 1px solid #33C3F0;
border-radius: 4px;
background: #fff;
pointer-events: none;
width: 90px;
height: 18px;
text-align: center;
font-size: 11px;
}
.highlight {
fill: #33C3F0 !important;
}
.curr-dt-marker {
fill: blue !important;
}
.spark-selected {
fill: #1EAEDB !important;
}
================================================
FILE: app/static/main.css
================================================
html, body {
height: 100%;
margin: 0px;
padding: 0px;
border: 0px;
background-color: #f0f0f0;
font-size: 13px;
}
iframe {
width: 100%;
height: 100%;
overflow: hidden;
}
.frame_div {
position: absolute;
padding-top: 50px;
width: 100%;
height: 100%;
overflow: hidden;
}
.loading {
background-image: url(spinner.gif);
background-position: 50% 50%;
background-repeat: no-repeat;
}
.alert {
animation: pulse 1s ease infinite;
}
@keyframes pulse {
0% {color: #222;}
50% {color: #fc0;}
100% {color: #222;}
}
ul {padding-left: 2ex; margin-bottom: 2px;}
li {
margin: 0;
position: relative;
list-style: none;
}
li:before {
content: '-';
font-weight: bold;
position: absolute;
left: -1.5ex;
}
/* front page */
#frontpage {font-size: 14px;}
#frontpage h1 {
margin-bottom: 3rem;
margin-top: 1rem;
font-size: 32px;
font-weight: normal;
}
#frontpage form {}
#frontpage form .row {
margin-bottom: 1rem;
}
#frontpage form .one.columns label {
width: 100%;
text-align: right;
}
label[for=browser-dropdown],
label[for=datetime-dropdown] {
line-height: 60px;
}
label[for=urlinput] {
line-height: 38px;
}
#frontpage button {margin: 3rem 0 1rem;}
.section {
margin-top: 5rem;
}
#frontpage .browser-about {
padding-left: 4px;
}
#frontpage div.support {
margin: 1rem 0;
}
a.support {display: inline-block; opacity: 0.7;}
a.support:hover{opacity: 1;}
a.support.rhizome {padding: 6px 0 0 20px;}
a.support.shuttleworth {margin: -6px 0 6px -3px;}
h2 {
margin: 2rem 0 0.5rem 0;
font-size: 20px;
font-weight: normal;
}
.fold {}
.fold > label:before {
content: "";
display: inline-block;
width: 0;
height: 0;
border-top: 5px solid transparent;
border-bottom: 5px solid transparent;
border-left: 5px solid #222;
margin-right: 8px
}
.fold.open > label:before {
content: "";
display: inline-block;
width: 0;
height: 0;
border-right: 5px solid transparent;
border-left: 5px solid transparent;
border-top: 5px solid #222;
margin-right: 8px
}
.fold > div {display: none}
.fold.open > div {display: block}
#invalid-form {
display: none;
font-size: 16px;
color: red;
}
/* replay page */
#page-replay {overflow: inherit;}
/* fonts */
#page-replay label,
#browser-label,
.browser-about {
font-size: 11px;
}
label {font-weight: normal;}
.datetime-string {
font-family: "Monaco", "DejaVu Sans Mono", "Consolas", "Courier New", monospace;
font-size: 16px;
font-weight: normal;
}
/* app menu on the left */
.header {
padding: 8px;
width: 310px;
position: absolute;
left: 0;
top: 0;
bottom: 0;
overflow: visible;
}
.widget-group {margin-bottom: 1rem;}
.widget-group label {margin-left: 4px;}
.widget-group.timer {
margin-bottom: 3rem;
}
#page-replay h1 {
font-size: 32px;
font-weight: normal;
margin: 2px 0 4rem 4px;
}
h1 a {
color: inherit;
text-decoration: inherit;
}
.widget-group.timer div.timer {
position: absolute;
top: 7px;
text-align: center;
right: 12px;
width: 62px;
}
#expire {
display: block;
margin-left: 0;
text-align: center;
}
.widget-group.browser {
margin-bottom: 4rem;
}
.widget-group.datetime {
margin-bottom: 5rem;
}
#curr-date-info {margin-bottom: 1rem;}
#curr-date {padding-left: 26px;}
.rel_message {
display: none;
background: #1EAEDB;
border-radius: 4px;
color: white;
margin: 1rem 4px 1rem 0;
padding: 1rem 8px;
}
.widget-group.session-info {
padding-left: 4px;
display: none;
}
.spanning {
margin: 1em;
}
.spanning div {text-align: center;}
.spanning div:nth-child(2) {padding-top: 1em;}
#statsCount {
font-weight: bold;
}
.support-info {
position: absolute;
top: 700px;
left: 8px;
font-size: 13px;
}
.about {
position: absolute;
top: 750px;
left: 4px;
font-size: 11px;
}
/* Area for browser */
#browser {
position: absolute;
top: 0px;
left: 326px;
right: 0;
bottom: 0;
}
#browserMsg {
position: absolute;
top: 50%;
text-align: center;
margin: -7rem auto 0px;
font-size: 26px;
width: 100%;
background-position: top center;
padding-top: 40px;
}
/* noVNC classes */
#noVNC_screen {
display: table;
width: auto;
height: auto;
margin: auto;
background-color:#313131;
line-height: 0;
margin-top: 8px;
}
#noVNC_canvas {
margin: 0px;
left: 0px;
top: 0px;
}
.hidden {
display: none;
}
================================================
FILE: app/static/main.js
================================================
window.INCLUDE_URI = "/static/novnc/";
var cmd_host = undefined;
var vnc_host = undefined;
var connected = false;
var ping_id = undefined;
var ping_interval = undefined;
var page_change = false;
var pingsock = undefined;
var fail_count = 0;
var curr_hosts = undefined;
var end_time = undefined;
var cid = undefined;
var waiting_for_container = false;
// Load supporting scripts
Util.load_scripts(["webutil.js", "base64.js", "websock.js", "des.js",
"keysymdef.js", "keyboard.js", "input.js", "display.js",
"inflator.js", "rfb.js", "keysym.js"]);
$(function() {
function init_container() {
var params = {"url": url, "ts": curr_ts, "browser": coll, "state": "ping"};
function send_request() {
var init_url = "/init_browser?" + $.param(params);
if (waiting_for_container) {
return;
}
waiting_for_container = true;
$.getJSON(init_url, handle_browser_response)
.fail(function() {
fail_count++;
if (fail_count <= 3) {
$("#browserMsg").text("Retrying browser init...");
setTimeout(send_request, 5000);
} else {
$("#browserMsg").text("Failed to init browser... Please try again later");
}
$("#browserMsg").show();
}).complete(function() {
waiting_for_container = false;
});
}
function handle_browser_response(data) {
params.id = data.id;
if (data.cmd_host && data.vnc_host) {
cmd_host = data.cmd_host;
vnc_host = data.vnc_host;
$("#currLabel").html("Loading <b>" + url + "</b>");
window.setTimeout(do_init, 1000);
} else if (data.queue != undefined) {
var msg = "Waiting for empty slot... ";
if (data.queue == 0) {
msg += "<b>You are next!</b>";
} else {
msg += "At most <b>" + data.queue + " user(s)</b> ahead of you";
}
$("#browserMsg").html(msg);
window.setTimeout(send_request, 3000);
}
}
send_request();
}
function do_init() {
var res = do_vnc();
if (!res) {
window.setTimeout(do_init, 1000);
}
}
function lose_focus() {
if (!rfb) return;
rfb.get_keyboard().set_focused(false);
rfb.get_mouse().set_focused(false);
}
function grab_focus() {
if (!rfb) return;
rfb.get_keyboard().set_focused(true);
rfb.get_mouse().set_focused(true);
}
$("#noVNC_screen").blur(lose_focus);
$("#noVNC_screen").mouseleave(lose_focus);
$("#noVNC_screen").mouseenter(grab_focus);
$("#datetime").click(lose_focus);
function update_replay_state() {
var full_url = "/" + coll + "/" + curr_ts + "/" + url;
window.history.replaceState({}, "", full_url);
}
function establish_ping_sock()
{
try {
pingsock = new WebSocket("ws://" + cmd_host + "/pingsock");
} catch (e) {
console.log(e);
}
pingsock.onerror = function(e) {
//console.log("Sock Error");
pingsock = undefined;
window.setTimeout(establish_ping_sock, 1000);
}
pingsock.onclose = function(e) {
//console.log("Sock Close");
pingsock = undefined;
}
pingsock.onmessage = function(e) {
handle_data_update(JSON.parse(e.data));
// hide cursor for nextstep for now due to mouse issues
if (coll == "WWW") {
$("#noVNC_canvas").css("cursor", "none");
}
}
}
function format_date(date) {
return date.toISOString().slice(0, -5).replace("T", " ");
}
function handle_data_update(data) {
if (data.page_url && data.page_url_secs) {
var date = new Date(data.page_url_secs * 1000);
var date_time = format_date(date).split(" ");
//$("#currLabel").html("Loaded <b>" + data.page_url + "</b> from <b>" + url_date + "</b>");
$(".rel_message").hide();
$("#curr-date").html(date_time[0]);
$("#curr-time").html(date_time[1]);
$("#curr-date-info").removeClass("loading");
//url = data.page_url;
if (page_change) {
ping_interval = 10000;
page_change = false;
}
if (sparkline) {
sparkline.move_current(date);
}
}
var any_data = false;
if (data.hosts && data.hosts.length > 0) {
if (data.hosts != curr_hosts) {
//$("#statsHosts").empty();
$("#statsHosts li").hide();
$.each(data.hosts, function(i, host) {
//var elem = document.createElement("li");
//$(elem).text(host);
//$("#statsHosts").append(elem);
$("#statsHosts li[data-id='" + host + "']").show();
});
data.hosts = curr_hosts;
$("#statsHostsWrap").show();
}
any_data = true;
}
if (data.urls) {
$("#statsCount").text(data.urls);
$("#statsCountWrap").show();
any_data = true;
}
if (data.min_sec && data.max_sec) {
var min_date = new Date(data.min_sec * 1000);
var max_date = new Date(data.max_sec * 1000);
$(".rel_message").hide();
$("#statsFrom").html(format_date(min_date).replace(" ", "<br>"));
$("#statsTo").html(format_date(max_date).replace(" ", "<br>"));
$("#statsSpanWrap").show();
any_data = true;
}
if (any_data) {
$(".session-info").show();
$("#session-loading").hide();
}
if (data.ttl != undefined) {
set_time_left(data.ttl);
}
update_replay_state();
}
function set_time_left(time_left) {
end_time = Math.floor(new Date().getTime() / 1000 + time_left);
}
// function ping() {
// $.getJSON("http://" + cmd_host + "/ping?ts=" + curr_ts, handle_data_update)
// .complete(function() {
// ping_id = window.setTimeout(ping, ping_interval);
// });
// }
var rfb;
var resizeTimeout;
function UIresize() {
if (WebUtil.getQueryVar('resize', false)) {
var innerW = window.innerWidth;
var innerH = window.innerHeight;
var controlbarH = $D('noVNC_status_bar').offsetHeight;
var padding = 5;
if (innerW !== undefined && innerH !== undefined)
rfb.setDesktopSize(innerW, innerH - controlbarH - padding);
}
}
function FBUComplete(rfb, fbu) {
UIresize();
rfb.set_onFBUComplete(function() { });
}
function onVNCCopyCut(rfb, text)
{
//$("#clipcontent").text(text);
}
function do_vnc() {
try {
rfb = new RFB({'target': $D('noVNC_canvas'),
'encrypt': WebUtil.getQueryVar('encrypt',
(window.location.protocol === "https:")),
'repeaterID': WebUtil.getQueryVar('repeaterID', ''),
'true_color': WebUtil.getQueryVar('true_color', true),
'local_cursor': WebUtil.getQueryVar('cursor', true),
'shared': WebUtil.getQueryVar('shared', true),
'view_only': WebUtil.getQueryVar('view_only', false),
'onUpdateState': updateState,
'onClipboard': onVNCCopyCut,
'onFBUComplete': FBUComplete});
} catch (exc) {
//updateState(null, 'fatal', null, 'Unable to create RFB client -- ' + exc);
console.warn(exc);
return false; // don't continue trying to connect
}
var hostport = vnc_host.split(":");
var host = hostport[0];
var port = hostport[1];
var password = "secret";
var path = "websockify";
try {
rfb.connect(host, port, password, path);
} catch (exc) {
console.warn(exc);
return false;
}
return true;
}
function updateState(rfb, state, oldstate, msg) {
if (state == "failed" || state == "fatal") {
// if not connected yet, attempt to connect until succeed
if (!connected) {
window.setTimeout(do_vnc, 1000);
}
} else if (state == "disconnected") {
if (connected) {
connected = false;
$("#noVNC_canvas").hide();
$("#browserMsg").show();
if (ping_id) {
window.clearInterval(ping_id);
}
init_container();
}
} else if (state == "normal") {
$("#noVNC_canvas").show();
$("#browserMsg").hide();
connected = true;
ping_interval = 1000;
page_change = true;
fail_count = 0;
// start ping at regular intervals
//ping_id = window.setTimeout(ping, ping_interval);
establish_ping_sock();
}
// var s, sb, cad, level;
// s = $D('noVNC_status');
// sb = $D('noVNC_status_bar');
// cad = $D('sendCtrlAltDelButton');
// switch (state) {
// case 'failed': level = "error"; break;
// case 'fatal': level = "error"; break;
// case 'normal': level = "normal"; break;
// case 'disconnected': level = "normal"; break;
// case 'loaded': level = "normal"; break;
// default: level = "warn"; break;
// }
//
// if (state === "normal") {
// cad.disabled = false;
// } else {
// cad.disabled = true;
// xvpInit(0);
// }
//
// if (typeof(msg) !== 'undefined') {
// sb.setAttribute("class", "noVNC_status_" + level);
// s.innerHTML = msg;
// }
console.log(msg);
}
window.onresize = function () {
// When the window has been resized, wait until the size remains
// the same for 0.5 seconds before sending the request for changing
// the resolution of the session
clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(function(){
UIresize();
}, 500);
};
// Browser navigate
$("#browser-selector td:not(:empty)").click(function(e) {
var path = $(this).attr("data-path");
var full_url = window.location.origin + "/" + path + "/" + curr_ts + "/" + url;
window.location.href = full_url;
});
// Update request dt
window.on_change_curr_ts = function(ts) {
if (pingsock) {
pingsock.send(JSON.stringify({"ts": ts}));
$(".rel_message").show();
}
}
function update_countdown() {
if (!end_time) {
return;
}
var curr = Math.floor(new Date().getTime() / 1000);
var secdiff = end_time - curr;
if (secdiff < 0) {
window.location.href = window.location.origin + "/";
return;
}
var min = Math.floor(secdiff / 60);
var sec = secdiff % 60;
if (sec <= 9) {
sec = "0" + sec;
}
if (min <= 9) {
min = "0" + min;
}
$("#expire").text(min + ":" + sec);
}
// Countdown updater
cid = setInterval(update_countdown, 1000);
// INIT
init_container();
if (coll) {
var browser = $("#browser-selector td[data-path='" + coll + "']");
browser.addClass("selected");
$("#about-link").attr("href", browser.attr("data-about-url"));
$(".about-browser").show();
}
});
================================================
FILE: app/static/normalize.css
================================================
/*! normalize.css v3.0.2 | MIT License | git.io/normalize */
/**
* 1. Set default font family to sans-serif.
* 2. Prevent iOS text size adjust after orientation change, without disabling
* user zoom.
*/
html {
font-family: sans-serif; /* 1 */
-ms-text-size-adjust: 100%; /* 2 */
-webkit-text-size-adjust: 100%; /* 2 */
}
/**
* Remove default margin.
*/
body {
margin: 0;
}
/* HTML5 display definitions
========================================================================== */
/**
* Correct `block` display not defined for any HTML5 element in IE 8/9.
* Correct `block` display not defined for `details` or `summary` in IE 10/11
* and Firefox.
* Correct `block` display not defined for `main` in IE 11.
*/
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
display: block;
}
/**
* 1. Correct `inline-block` display not defined in IE 8/9.
* 2. Normalize vertical alignment of `progress` in Chrome, Firefox, and Opera.
*/
audio,
canvas,
progress,
video {
display: inline-block; /* 1 */
vertical-align: baseline; /* 2 */
}
/**
* Prevent modern browsers from displaying `audio` without controls.
* Remove excess height in iOS 5 devices.
*/
audio:not([controls]) {
display: none;
height: 0;
}
/**
* Address `[hidden]` styling not present in IE 8/9/10.
* Hide the `template` element in IE 8/9/11, Safari, and Firefox < 22.
*/
[hidden],
template {
display: none;
}
/* Links
========================================================================== */
/**
* Remove the gray background color from active links in IE 10.
*/
a {
background-color: transparent;
}
/**
* Improve readability when focused and also mouse hovered in all browsers.
*/
a:active,
a:hover {
outline: 0;
}
/* Text-level semantics
========================================================================== */
/**
* Address styling not present in IE 8/9/10/11, Safari, and Chrome.
*/
abbr[title] {
border-bottom: 1px dotted;
}
/**
* Address style set to `bolder` in Firefox 4+, Safari, and Chrome.
*/
b,
strong {
font-weight: bold;
}
/**
* Address styling not present in Safari and Chrome.
*/
dfn {
font-style: italic;
}
/**
* Address variable `h1` font-size and margin within `section` and `article`
* contexts in Firefox 4+, Safari, and Chrome.
*/
h1 {
font-size: 2em;
margin: 0.67em 0;
}
/**
* Address styling not present in IE 8/9.
*/
mark {
background: #ff0;
color: #000;
}
/**
* Address inconsistent and variable font size in all browsers.
*/
small {
font-size: 80%;
}
/**
* Prevent `sub` and `sup` affecting `line-height` in all browsers.
*/
sub,
sup {
font-size: 75%;
line-height: 0;
position: relative;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
/* Embedded content
========================================================================== */
/**
* Remove border when inside `a` element in IE 8/9/10.
*/
img {
border: 0;
}
/**
* Correct overflow not hidden in IE 9/10/11.
*/
svg:not(:root) {
overflow: hidden;
}
/* Grouping content
========================================================================== */
/**
* Address margin not present in IE 8/9 and Safari.
*/
figure {
margin: 1em 40px;
}
/**
* Address differences between Firefox and other browsers.
*/
hr {
-moz-box-sizing: content-box;
box-sizing: content-box;
height: 0;
}
/**
* Contain overflow in all browsers.
*/
pre {
overflow: auto;
}
/**
* Address odd `em`-unit font size rendering in all browsers.
*/
code,
kbd,
pre,
samp {
font-family: monospace, monospace;
font-size: 1em;
}
/* Forms
========================================================================== */
/**
* Known limitation: by default, Chrome and Safari on OS X allow very limited
* styling of `select`, unless a `border` property is set.
*/
/**
* 1. Correct color not being inherited.
* Known issue: affects color of disabled elements.
* 2. Correct font properties not being inherited.
* 3. Address margins set differently in Firefox 4+, Safari, and Chrome.
*/
button,
input,
optgroup,
select,
textarea {
color: inherit; /* 1 */
font: inherit; /* 2 */
margin: 0; /* 3 */
}
/**
* Address `overflow` set to `hidden` in IE 8/9/10/11.
*/
button {
overflow: visible;
}
/**
* Address inconsistent `text-transform` inheritance for `button` and `select`.
* All other form control elements do not inherit `text-transform` values.
* Correct `button` style inheritance in Firefox, IE 8/9/10/11, and Opera.
* Correct `select` style inheritance in Firefox.
*/
button,
select {
text-transform: none;
}
/**
* 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio`
* and `video` controls.
* 2. Correct inability to style clickable `input` types in iOS.
* 3. Improve usability and consistency of cursor style between image-type
* `input` and others.
*/
button,
html input[type="button"], /* 1 */
input[type="reset"],
input[type="submit"] {
-webkit-appearance: button; /* 2 */
cursor: pointer; /* 3 */
}
/**
* Re-set default cursor for disabled elements.
*/
button[disabled],
html input[disabled] {
cursor: default;
}
/**
* Remove inner padding and border in Firefox 4+.
*/
button::-moz-focus-inner,
input::-moz-focus-inner {
border: 0;
padding: 0;
}
/**
* Address Firefox 4+ setting `line-height` on `input` using `!important` in
* the UA stylesheet.
*/
input {
line-height: normal;
}
/**
* It's recommended that you don't attempt to style these elements.
* Firefox's implementation doesn't respect box-sizing, padding, or width.
*
* 1. Address box sizing set to `content-box` in IE 8/9/10.
* 2. Remove excess padding in IE 8/9/10.
*/
input[type="checkbox"],
input[type="radio"] {
box-sizing: border-box; /* 1 */
padding: 0; /* 2 */
}
/**
* Fix the cursor style for Chrome's increment/decrement buttons. For certain
* `font-size` values of the `input`, it causes the cursor style of the
* decrement button to change from `default` to `text`.
*/
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
height: auto;
}
/**
* 1. Address `appearance` set to `searchfield` in Safari and Chrome.
* 2. Address `box-sizing` set to `border-box` in Safari and Chrome
* (include `-moz` to future-proof).
*/
input[type="search"] {
-webkit-appearance: textfield; /* 1 */
-moz-box-sizing: content-box;
-webkit-box-sizing: content-box; /* 2 */
box-sizing: content-box;
}
/**
* Remove inner padding and search cancel button in Safari and Chrome on OS X.
* Safari (but not Chrome) clips the cancel button when the search input has
* padding (and `textfield` appearance).
*/
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
-webkit-appearance: none;
}
/**
* Define consistent border, margin, and padding.
*/
fieldset {
border: 1px solid #c0c0c0;
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
}
/**
* 1. Correct `color` not being inherited in IE 8/9/10/11.
* 2. Remove padding so people aren't caught out if they zero out fieldsets.
*/
legend {
border: 0; /* 1 */
padding: 0; /* 2 */
}
/**
* Remove default vertical scrollbar in IE 8/9/10/11.
*/
textarea {
overflow: auto;
}
/**
* Don't inherit the `font-weight` (applied by a rule above).
* NOTE: the default cannot safely be changed in Chrome and Safari on OS X.
*/
optgroup {
font-weight: bold;
}
/* Tables
========================================================================== */
/**
* Remove most spacing between table cells.
*/
table {
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0;
}
================================================
FILE: app/static/novnc/base.css
================================================
/*
* noVNC base CSS
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
*/
body {
margin:0;
padding:0;
font-family: Helvetica;
/*Background image with light grey curve.*/
/*
background-color:#494949;
background-repeat:no-repeat;
background-position:right bottom;
height:100%;
*/
}
html {
height:100%;
}
#noVNC_controls ul {
list-style: none;
margin: 0px;
padding: 0px;
}
#noVNC_controls li {
padding-bottom:8px;
}
#noVNC_host {
width:150px;
}
#noVNC_port {
width: 80px;
}
#noVNC_password {
width: 150px;
}
#noVNC_encrypt {
}
#noVNC_path {
width: 100px;
}
#noVNC_connect_button {
width: 110px;
float:right;
}
#noVNC_buttons {
white-space: nowrap;
}
#noVNC_view_drag_button {
display: none;
}
#sendCtrlAltDelButton {
display: none;
}
#fullscreenButton {
display: none;
}
#noVNC_xvp_buttons {
display: none;
}
#noVNC_mobile_buttons {
display: none;
}
#noVNC_extra_keys {
display: inline;
list-style-type: none;
padding: 0px;
margin: 0px;
position: relative;
}
.noVNC-buttons-left {
float: left;
z-index: 1;
position: relative;
}
.noVNC-buttons-right {
float:right;
right: 0px;
z-index: 2;
position: absolute;
}
#noVNC_status {
font-size: 12px;
padding-top: 4px;
height:32px;
text-align: center;
font-weight: bold;
color: #fff;
}
#noVNC_settings_menu {
margin: 3px;
text-align: left;
}
#noVNC_settings_menu ul {
list-style: none;
margin: 0px;
padding: 0px;
}
#noVNC_apply {
float:right;
}
#VNC_clipboard_clear_button {
float:right;
}
#VNC_clipboard_text {
font-size: 11px;
}
#noVNC_clipboard_clear_button {
float:right;
}
/*Bubble contents divs*/
#noVNC_settings {
display:none;
margin-top:73px;
right:20px;
position:fixed;
}
#noVNC_controls {
display:none;
margin-top:73px;
right:12px;
position:fixed;
}
#noVNC_controls.top:after {
right:15px;
}
#noVNC_description {
display:none;
position:fixed;
margin-top:73px;
right:20px;
left:20px;
padding:15px;
color:#000;
background:#eee; /* default background for browsers without gradient support */
border:2px solid #E0E0E0;
-webkit-border-radius:10px;
-moz-border-radius:10px;
border-radius:10px;
}
#noVNC_popup_status {
display:none;
position: fixed;
z-index: 1;
margin:15px;
margin-top:60px;
padding:15px;
width:auto;
text-align:center;
font-weight:bold;
word-wrap:break-word;
color:#fff;
background:rgba(0,0,0,0.65);
-webkit-border-radius:10px;
-moz-border-radius:10px;
border-radius:10px;
}
#noVNC_xvp {
display:none;
margin-top:73px;
right:30px;
position:fixed;
}
#noVNC_xvp.top:after {
right:125px;
}
#noVNC_clipboard {
display:none;
margin-top:73px;
right:30px;
position:fixed;
}
#noVNC_clipboard.top:after {
right:85px;
}
#keyboardinput {
width:1px;
height:1px;
background-color:#fff;
color:#fff;
border:0;
position: relative;
left: -40px;
z-index: -1;
ime-mode: disabled;
}
/*
* Advanced Styling
*/
.noVNC_status_normal {
background: #b2bdcd; /* Old browsers */
background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
}
.noVNC_status_error {
background: #f04040; /* Old browsers */
background: -moz-linear-gradient(top, #f04040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
background: linear-gradient(top, #f04040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
}
.noVNC_status_warn {
background: #f0f040; /* Old browsers */
background: -moz-linear-gradient(top, #f0f040 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
background: linear-gradient(top, #f0f040 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
}
/* Control bar */
#noVNC-control-bar {
position:fixed;
display:block;
height:36px;
left:0;
top:0;
width:100%;
z-index:200;
}
.noVNC_status_button {
padding: 4px 4px;
vertical-align: middle;
border:1px solid #869dbc;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
border-radius: 6px;
background: #b2bdcd; /* Old browsers */
background: -moz-linear-gradient(top, #b2bdcd 0%, #899cb3 49%, #7e93af 51%, #6e84a3 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#b2bdcd), color-stop(49%,#899cb3), color-stop(51%,#7e93af), color-stop(100%,#6e84a3)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* IE10+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#b2bdcd', endColorstr='#6e84a3',GradientType=0 ); /* IE6-9 */
background: linear-gradient(top, #b2bdcd 0%,#899cb3 49%,#7e93af 51%,#6e84a3 100%); /* W3C */
/*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/
}
.noVNC_status_button_selected {
padding: 4px 4px;
vertical-align: middle;
border:1px solid #4366a9;
-webkit-border-radius: 6px;
-moz-border-radius: 6px;
background: #779ced; /* Old browsers */
background: -moz-linear-gradient(top, #779ced 0%, #3970e0 49%, #2160dd 51%, #2463df 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#779ced), color-stop(49%,#3970e0), color-stop(51%,#2160dd), color-stop(100%,#2463df)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* IE10+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#779ced', endColorstr='#2463df',GradientType=0 ); /* IE6-9 */
background: linear-gradient(top, #779ced 0%,#3970e0 49%,#2160dd 51%,#2463df 100%); /* W3C */
/*box-shadow:inset 0.4px 0.4px 0.4px #000000;*/
}
.noVNC_status_button:disabled {
opacity: 0.4;
}
/*Settings Bubble*/
.triangle-right {
position:relative;
padding:15px;
margin:1em 0 3em;
color:#fff;
background:#fff; /* default background for browsers without gradient support */
/* css3 */
/*background:-webkit-gradient(linear, 0 0, 0 100%, from(#2e88c4), to(#075698));
background:-moz-linear-gradient(#2e88c4, #075698);
background:-o-linear-gradient(#2e88c4, #075698);
background:linear-gradient(#2e88c4, #075698);*/
-webkit-border-radius:10px;
-moz-border-radius:10px;
border-radius:10px;
color:#000;
border:2px solid #E0E0E0;
}
.triangle-right.top:after {
border-color: transparent #E0E0E0;
border-width: 20px 20px 0 0;
bottom: auto;
left: auto;
right: 50px;
top: -20px;
}
.triangle-right:after {
content:"";
position:absolute;
bottom:-20px; /* value = - border-top-width - border-bottom-width */
left:50px; /* controls horizontal position */
border-width:20px 0 0 20px; /* vary these values to change the angle of the vertex */
border-style:solid;
border-color:#E0E0E0 transparent;
/* reduce the damage in FF3.0 */
display:block;
width:0;
}
.triangle-right.top:after {
top:-40px; /* value = - border-top-width - border-bottom-width */
right:50px; /* controls horizontal position */
bottom:auto;
left:auto;
border-width:40px 40px 0 0; /* vary these values to change the angle of the vertex */
border-color:transparent #E0E0E0;
}
/* ----------------------------------------
* Media sizing
* ----------------------------------------
*/
.noVNC_status_button {
font-size: 12px;
}
#noVNC_clipboard_text {
width: 500px;
}
#noVNC_logo {
font-size: 180px;
}
.noVNC-buttons-left {
padding-left: 10px;
}
.noVNC-buttons-right {
padding-right: 10px;
}
#noVNC_status {
z-index: 0;
position: absolute;
width: 100%;
margin-left: 0px;
}
#showExtraKeysButton { display: none; }
#toggleCtrlButton { display: inline; }
#toggleAltButton { display: inline; }
#sendTabButton { display: inline; }
#sendEscButton { display: inline; }
/* left-align the status text on lower resolutions */
@media screen and (max-width: 800px){
#noVNC_status {
z-index: 1;
position: relative;
width: auto;
float: left;
margin-left: 4px;
}
}
@media screen and (max-width: 640px){
#noVNC_clipboard_text {
width: 410px;
}
#noVNC_logo {
font-size: 150px;
}
.noVNC_status_button {
font-size: 10px;
}
.noVNC-buttons-left {
padding-left: 0px;
}
.noVNC-buttons-right {
padding-right: 0px;
}
/* collapse the extra keys on lower resolutions */
#showExtraKeysButton {
display: inline;
}
#toggleCtrlButton {
display: none;
position: absolute;
top: 30px;
left: 0px;
}
#toggleAltButton {
display: none;
position: absolute;
top: 65px;
left: 0px;
}
#sendTabButton {
display: none;
position: absolute;
top: 100px;
left: 0px;
}
#sendEscButton {
display: none;
position: absolute;
top: 135px;
left: 0px;
}
}
@media screen and (min-width: 321px) and (max-width: 480px) {
#noVNC_clipboard_text {
width: 250px;
}
#noVNC_logo {
font-size: 110px;
}
}
@media screen and (max-width: 320px) {
.noVNC_status_button {
font-size: 9px;
}
#noVNC_clipboard_text {
width: 220px;
}
#noVNC_logo {
font-size: 90px;
}
}
================================================
FILE: app/static/novnc/base64.js
================================================
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
// From: http://hg.mozilla.org/mozilla-central/raw-file/ec10630b1a54/js/src/devtools/jint/sunspider/string-base64.js
/*jslint white: false */
/*global console */
var Base64 = {
/* Convert data (an array of integers) to a Base64 string. */
toBase64Table : 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/='.split(''),
base64Pad : '=',
encode: function (data) {
"use strict";
var result = '';
var toBase64Table = Base64.toBase64Table;
var length = data.length;
var lengthpad = (length % 3);
// Convert every three bytes to 4 ascii characters.
for (var i = 0; i < (length - 2); i += 3) {
result += toBase64Table[data[i] >> 2];
result += toBase64Table[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
result += toBase64Table[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
result += toBase64Table[data[i + 2] & 0x3f];
}
// Convert the remaining 1 or 2 bytes, pad out to 4 characters.
var j = 0;
if (lengthpad === 2) {
j = length - lengthpad;
result += toBase64Table[data[j] >> 2];
result += toBase64Table[((data[j] & 0x03) << 4) + (data[j + 1] >> 4)];
result += toBase64Table[(data[j + 1] & 0x0f) << 2];
result += toBase64Table[64];
} else if (lengthpad === 1) {
j = length - lengthpad;
result += toBase64Table[data[j] >> 2];
result += toBase64Table[(data[j] & 0x03) << 4];
result += toBase64Table[64];
result += toBase64Table[64];
}
return result;
},
/* Convert Base64 data to a string */
/* jshint -W013 */
toBinaryTable : [
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,-1,
-1,-1,-1,-1, -1,-1,-1,-1, -1,-1,-1,62, -1,-1,-1,63,
52,53,54,55, 56,57,58,59, 60,61,-1,-1, -1, 0,-1,-1,
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10, 11,12,13,14,
15,16,17,18, 19,20,21,22, 23,24,25,-1, -1,-1,-1,-1,
-1,26,27,28, 29,30,31,32, 33,34,35,36, 37,38,39,40,
41,42,43,44, 45,46,47,48, 49,50,51,-1, -1,-1,-1,-1
],
/* jshint +W013 */
decode: function (data, offset) {
"use strict";
offset = typeof(offset) !== 'undefined' ? offset : 0;
var toBinaryTable = Base64.toBinaryTable;
var base64Pad = Base64.base64Pad;
var result, result_length;
var leftbits = 0; // number of bits decoded, but yet to be appended
var leftdata = 0; // bits decoded, but yet to be appended
var data_length = data.indexOf('=') - offset;
if (data_length < 0) { data_length = data.length - offset; }
/* Every four characters is 3 resulting numbers */
result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
result = new Array(result_length);
// Convert one by one.
for (var idx = 0, i = offset; i < data.length; i++) {
var c = toBinaryTable[data.charCodeAt(i) & 0x7f];
var padding = (data.charAt(i) === base64Pad);
// Skip illegal characters and whitespace
if (c === -1) {
console.error("Illegal character code " + data.charCodeAt(i) + " at position " + i);
continue;
}
// Collect data into leftdata, update bitcount
leftdata = (leftdata << 6) | c;
leftbits += 6;
// If we have 8 or more bits, append 8 bits to the result
if (leftbits >= 8) {
leftbits -= 8;
// Append if not padding.
if (!padding) {
result[idx++] = (leftdata >> leftbits) & 0xff;
}
leftdata &= (1 << leftbits) - 1;
}
}
// If there are any bits left, the base64 string was corrupted
if (leftbits) {
err = new Error('Corrupted base64 string');
err.name = 'Base64-Error';
throw err;
}
return result;
}
}; /* End of Base64 namespace */
================================================
FILE: app/static/novnc/black.css
================================================
/*
* noVNC black CSS
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
*/
#keyboardinput {
background-color:#000;
}
.noVNC_status_normal {
background: #4c4c4c; /* Old browsers */
background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
}
.noVNC_status_error {
background: #f04040; /* Old browsers */
background: -moz-linear-gradient(top, #f04040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f04040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
background: linear-gradient(top, #f04040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
}
.noVNC_status_warn {
background: #f0f040; /* Old browsers */
background: -moz-linear-gradient(top, #f0f040 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#f0f040), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
background: linear-gradient(top, #f0f040 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
}
.triangle-right {
border:2px solid #fff;
background:#000;
color:#fff;
}
.noVNC_status_button {
font-size: 12px;
vertical-align: middle;
border:1px solid #4c4c4c;
background: #4c4c4c; /* Old browsers */
background: -moz-linear-gradient(top, #4c4c4c 0%, #2c2c2c 50%, #000000 51%, #131313 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#4c4c4c), color-stop(50%,#2c2c2c), color-stop(51%,#000000), color-stop(100%,#131313)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* IE10+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#4c4c4c', endColorstr='#131313',GradientType=0 ); /* IE6-9 */
background: linear-gradient(top, #4c4c4c 0%,#2c2c2c 50%,#000000 51%,#131313 100%); /* W3C */
}
.noVNC_status_button_selected {
background: #9dd53a; /* Old browsers */
background: -moz-linear-gradient(top, #9dd53a 0%, #a1d54f 50%, #80c217 51%, #7cbc0a 100%); /* FF3.6+ */
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#9dd53a), color-stop(50%,#a1d54f), color-stop(51%,#80c217), color-stop(100%,#7cbc0a)); /* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Chrome10+,Safari5.1+ */
background: -o-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* Opera11.10+ */
background: -ms-linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* IE10+ */
filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#9dd53a', endColorstr='#7cbc0a',GradientType=0 ); /* IE6-9 */
background: linear-gradient(top, #9dd53a 0%,#a1d54f 50%,#80c217 51%,#7cbc0a 100%); /* W3C */
}
================================================
FILE: app/static/novnc/blue.css
================================================
/*
* noVNC blue CSS
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2013 Samuel Mannehed for Cendio AB
* noVNC is licensed under the MPL 2.0 (see LICENSE.txt)
* This file is licensed under the 2-Clause BSD license (see LICENSE.txt).
*/
.noVNC_status_normal {
background-color:#04073d;
background-image: -webkit-gradient(
linear,
left bottom,
left top,
color-stop(0.54, rgb(10,15,79)),
color-stop(0.5, rgb(4,7,61))
);
background-image: -moz-linear-gradient(
center bottom,
rgb(10,15,79) 54%,
rgb(4,7,61) 50%
);
}
.noVNC_status_error {
background-color:#f04040;
background-image: -webkit-gradient(
linear,
left bottom,
left top,
color-stop(0.54, rgb(240,64,64)),
color-stop(0.5, rgb(4,7,61))
);
background-image: -moz-linear-gradient(
center bottom,
rgb(4,7,61) 54%,
rgb(249,64,64) 50%
);
}
.noVNC_status_warn {
background-color:#f0f040;
background-image: -webkit-gradient(
linear,
left bottom,
left top,
color-stop(0.54, rgb(240,240,64)),
color-stop(0.5, rgb(4,7,61))
);
background-image: -moz-linear-gradient(
center bottom,
rgb(4,7,61) 54%,
rgb(240,240,64) 50%
);
}
.triangle-right {
border:2px solid #fff;
background:#04073d;
color:#fff;
}
#keyboardinput {
background-color:#04073d;
}
================================================
FILE: app/static/novnc/des.js
================================================
/*
* Ported from Flashlight VNC ActionScript implementation:
* http://www.wizhelp.com/flashlight-vnc/
*
* Full attribution follows:
*
* -------------------------------------------------------------------------
*
* This DES class has been extracted from package Acme.Crypto for use in VNC.
* The unnecessary odd parity code has been removed.
*
* These changes are:
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*
* DesCipher - the DES encryption method
*
* The meat of this code is by Dave Zimmerman <dzimm@widget.com>, and is:
*
* Copyright (c) 1996 Widget Workshop, Inc. All Rights Reserved.
*
* Permission to use, copy, modify, and distribute this software
* and its documentation for NON-COMMERCIAL or COMMERCIAL purposes and
* without fee is hereby granted, provided that this copyright notice is kept
* intact.
*
* WIDGET WORKSHOP MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY
* OF THE SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
* PARTICULAR PURPOSE, OR NON-INFRINGEMENT. WIDGET WORKSHOP SHALL NOT BE LIABLE
* FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR
* DISTRIBUTING THIS SOFTWARE OR ITS DERIVATIVES.
*
* THIS SOFTWARE IS NOT DESIGNED OR INTENDED FOR USE OR RESALE AS ON-LINE
* CONTROL EQUIPMENT IN HAZARDOUS ENVIRONMENTS REQUIRING FAIL-SAFE
* PERFORMANCE, SUCH AS IN THE OPERATION OF NUCLEAR FACILITIES, AIRCRAFT
* NAVIGATION OR COMMUNICATION SYSTEMS, AIR TRAFFIC CONTROL, DIRECT LIFE
* SUPPORT MACHINES, OR WEAPONS SYSTEMS, IN WHICH THE FAILURE OF THE
* SOFTWARE COULD LEAD DIRECTLY TO DEATH, PERSONAL INJURY, OR SEVERE
* PHYSICAL OR ENVIRONMENTAL DAMAGE ("HIGH RISK ACTIVITIES"). WIDGET WORKSHOP
* SPECIFICALLY DISCLAIMS ANY EXPRESS OR IMPLIED WARRANTY OF FITNESS FOR
* HIGH RISK ACTIVITIES.
*
*
* The rest is:
*
* Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* Visit the ACME Labs Java page for up-to-date versions of this and other
* fine Java utilities: http://www.acme.com/java/
*/
/* jslint white: false */
function DES(passwd) {
"use strict";
// Tables, permutations, S-boxes, etc.
// jshint -W013
var PC2 = [13,16,10,23, 0, 4, 2,27,14, 5,20, 9,22,18,11, 3,
25, 7,15, 6,26,19,12, 1,40,51,30,36,46,54,29,39,
50,44,32,47,43,48,38,55,33,52,45,41,49,35,28,31 ],
totrot = [ 1, 2, 4, 6, 8,10,12,14,15,17,19,21,23,25,27,28],
z = 0x0, a,b,c,d,e,f, SP1,SP2,SP3,SP4,SP5,SP6,SP7,SP8,
keys = [];
// jshint -W015
a=1<<16; b=1<<24; c=a|b; d=1<<2; e=1<<10; f=d|e;
SP1 = [c|e,z|z,a|z,c|f,c|d,a|f,z|d,a|z,z|e,c|e,c|f,z|e,b|f,c|d,b|z,z|d,
z|f,b|e,b|e,a|e,a|e,c|z,c|z,b|f,a|d,b|d,b|d,a|d,z|z,z|f,a|f,b|z,
a|z,c|f,z|d,c|z,c|e,b|z,b|z,z|e,c|d,a|z,a|e,b|d,z|e,z|d,b|f,a|f,
c|f,a|d,c|z,b|f,b|d,z|f,a|f,c|e,z|f,b|e,b|e,z|z,a|d,a|e,z|z,c|d];
a=1<<20; b=1<<31; c=a|b; d=1<<5; e=1<<15; f=d|e;
SP2 = [c|f,b|e,z|e,a|f,a|z,z|d,c|d,b|f,b|d,c|f,c|e,b|z,b|e,a|z,z|d,c|d,
a|e,a|d,b|f,z|z,b|z,z|e,a|f,c|z,a|d,b|d,z|z,a|e,z|f,c|e,c|z,z|f,
z|z,a|f,c|d,a|z,b|f,c|z,c|e,z|e,c|z,b|e,z|d,c|f,a|f,z|d,z|e,b|z,
z|f,c|e,a|z,b|d,a|d,b|f,b|d,a|d,a|e,z|z,b|e,z|f,b|z,c|d,c|f,a|e];
a=1<<17; b=1<<27; c=a|b; d=1<<3; e=1<<9; f=d|e;
SP3 = [z|f,c|e,z|z,c|d,b|e,z|z,a|f,b|e,a|d,b|d,b|d,a|z,c|f,a|d,c|z,z|f,
b|z,z|d,c|e,z|e,a|e,c|z,c|d,a|f,b|f,a|e,a|z,b|f,z|d,c|f,z|e,b|z,
c|e,b|z,a|d,z|f,a|z,c|e,b|e,z|z,z|e,a|d,c|f,b|e,b|d,z|e,z|z,c|d,
b|f,a|z,b|z,c|f,z|d,a|f,a|e,b|d,c|z,b|f,z|f,c|z,a|f,z|d,c|d,a|e];
a=1<<13; b=1<<23; c=a|b; d=1<<0; e=1<<7; f=d|e;
SP4 = [c|d,a|f,a|f,z|e,c|e,b|f,b|d,a|d,z|z,c|z,c|z,c|f,z|f,z|z,b|e,b|d,
z|d,a|z,b|z,c|d,z|e,b|z,a|d,a|e,b|f,z|d,a|e,b|e,a|z,c|e,c|f,z|f,
b|e,b|d,c|z,c|f,z|f,z|z,z|z,c|z,a|e,b|e,b|f,z|d,c|d,a|f,a|f,z|e,
c|f,z|f,z|d,a|z,b|d,a|d,c|e,b|f,a|d,a|e,b|z,c|d,z|e,b|z,a|z,c|e];
a=1<<25; b=1<<30; c=a|b; d=1<<8; e=1<<19; f=d|e;
SP5 = [z|d,a|f,a|e,c|d,z|e,z|d,b|z,a|e,b|f,z|e,a|d,b|f,c|d,c|e,z|f,b|z,
a|z,b|e,b|e,z|z,b|d,c|f,c|f,a|d,c|e,b|d,z|z,c|z,a|f,a|z,c|z,z|f,
z|e,c|d,z|d,a|z,b|z,a|e,c|d,b|f,a|d,b|z,c|e,a|f,b|f,z|d,a|z,c|e,
c|f,z|f,c|z,c|f,a|e,z|z,b|e,c|z,z|f,a|d,b|d,z|e,z|z,b|e,a|f,b|d];
a=1<<22; b=1<<29; c=a|b; d=1<<4; e=1<<14; f=d|e;
SP6 = [b|d,c|z,z|e,c|f,c|z,z|d,c|f,a|z,b|e,a|f,a|z,b|d,a|d,b|e,b|z,z|f,
z|z,a|d,b|f,z|e,a|e,b|f,z|d,c|d,c|d,z|z,a|f,c|e,z|f,a|e,c|e,b|z,
b|e,z|d,c|d,a|e,c|f,a|z,z|f,b|d,a|z,b|e,b|z,z|f,b|d,c|f,a|e,c|z,
a|f,c|e,z|z,c|d,z|d,z|e,c|z,a|f,z|e,a|d,b|f,z|z,c|e,b|z,a|d,b|f];
a=1<<21; b=1<<26; c=a|b; d=1<<1; e=1<<11; f=d|e;
SP7 = [a|z,c|d,b|f,z|z,z|e,b|f,a|f,c|e,c|f,a|z,z|z,b|d,z|d,b|z,c|d,z|f,
b|e,a|f,a|d,b|e,b|d,c|z,c|e,a|d,c|z,z|e,z|f,c|f,a|e,z|d,b|z,a|e,
b|z,a|e,a|z,b|f,b|f,c|d,c|d,z|d,a|d,b|z,b|e,a|z,c|e,z|f,a|f,c|e,
z|f,b|d,c|f,c|z,a|e,z|z,z|d,c|f,z|z,a|f,c|z,z|e,b|d,b|e,z|e,a|d];
a=1<<18; b=1<<28; c=a|b; d=1<<6; e=1<<12; f=d|e;
SP8 = [b|f,z|e,a|z,c|f,b|z,b|f,z|d,b|z,a|d,c|z,c|f,a|e,c|e,a|f,z|e,z|d,
c|z,b|d,b|e,z|f,a|e,a|d,c|d,c|e,z|f,z|z,z|z,c|d,b|d,b|e,a|f,a|z,
a|f,a|z,c|e,z|e,z|d,c|d,z|e,a|f,b|e,z|d,b|d,c|z,c|d,b|z,a|z,b|f,
z|z,c|f,a|d,b|d,c|z,b|e,b|f,z|z,c|f,a|e,a|e,z|f,z|f,a|d,b|z,c|e];
// jshint +W013,+W015
// Set the key.
function setKeys(keyBlock) {
var i, j, l, m, n, o, pc1m = [], pcr = [], kn = [],
raw0, raw1, rawi, KnLi;
for (j = 0, l = 56; j < 56; ++j, l -= 8) {
l += l < -5 ? 65 : l < -3 ? 31 : l < -1 ? 63 : l === 27 ? 35 : 0; // PC1
m = l & 0x7;
pc1m[j] = ((keyBlock[l >>> 3] & (1<<m)) !== 0) ? 1: 0;
}
for (i = 0; i < 16; ++i) {
m = i << 1;
n = m + 1;
kn[m] = kn[n] = 0;
for (o = 28; o < 59; o += 28) {
for (j = o - 28; j < o; ++j) {
l = j + totrot[i];
if (l < o) {
pcr[j] = pc1m[l];
} else {
pcr[j] = pc1m[l - 28];
}
}
}
for (j = 0; j < 24; ++j) {
if (pcr[PC2[j]] !== 0) {
kn[m] |= 1 << (23 - j);
}
if (pcr[PC2[j + 24]] !== 0) {
kn[n] |= 1 << (23 - j);
}
}
}
// cookey
for (i = 0, rawi = 0, KnLi = 0; i < 16; ++i) {
raw0 = kn[rawi++];
raw1 = kn[rawi++];
keys[KnLi] = (raw0 & 0x00fc0000) << 6;
keys[KnLi] |= (raw0 & 0x00000fc0) << 10;
keys[KnLi] |= (raw1 & 0x00fc0000) >>> 10;
keys[KnLi] |= (raw1 & 0x00000fc0) >>> 6;
++KnLi;
keys[KnLi] = (raw0 & 0x0003f000) << 12;
keys[KnLi] |= (raw0 & 0x0000003f) << 16;
keys[KnLi] |= (raw1 & 0x0003f000) >>> 4;
keys[KnLi] |= (raw1 & 0x0000003f);
++KnLi;
}
}
// Encrypt 8 bytes of text
function enc8(text) {
var i = 0, b = text.slice(), fval, keysi = 0,
l, r, x; // left, right, accumulator
// Squash 8 bytes to 2 ints
l = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
r = b[i++]<<24 | b[i++]<<16 | b[i++]<<8 | b[i++];
x = ((l >>> 4) ^ r) & 0x0f0f0f0f;
r ^= x;
l ^= (x << 4);
x = ((l >>> 16) ^ r) & 0x0000ffff;
r ^= x;
l ^= (x << 16);
x = ((r >>> 2) ^ l) & 0x33333333;
l ^= x;
r ^= (x << 2);
x = ((r >>> 8) ^ l) & 0x00ff00ff;
l ^= x;
r ^= (x << 8);
r = (r << 1) | ((r >>> 31) & 1);
x = (l ^ r) & 0xaaaaaaaa;
l ^= x;
r ^= x;
l = (l << 1) | ((l >>> 31) & 1);
for (i = 0; i < 8; ++i) {
x = (r << 28) | (r >>> 4);
x ^= keys[keysi++];
fval = SP7[x & 0x3f];
fval |= SP5[(x >>> 8) & 0x3f];
fval |= SP3[(x >>> 16) & 0x3f];
fval |= SP1[(x >>> 24) & 0x3f];
x = r ^ keys[keysi++];
fval |= SP8[x & 0x3f];
fval |= SP6[(x >>> 8) & 0x3f];
fval |= SP4[(x >>> 16) & 0x3f];
fval |= SP2[(x >>> 24) & 0x3f];
l ^= fval;
x = (l << 28) | (l >>> 4);
x ^= keys[keysi++];
fval = SP7[x & 0x3f];
fval |= SP5[(x >>> 8) & 0x3f];
fval |= SP3[(x >>> 16) & 0x3f];
fval |= SP1[(x >>> 24) & 0x3f];
x = l ^ keys[keysi++];
fval |= SP8[x & 0x0000003f];
fval |= SP6[(x >>> 8) & 0x3f];
fval |= SP4[(x >>> 16) & 0x3f];
fval |= SP2[(x >>> 24) & 0x3f];
r ^= fval;
}
r = (r << 31) | (r >>> 1);
x = (l ^ r) & 0xaaaaaaaa;
l ^= x;
r ^= x;
l = (l << 31) | (l >>> 1);
x = ((l >>> 8) ^ r) & 0x00ff00ff;
r ^= x;
l ^= (x << 8);
x = ((l >>> 2) ^ r) & 0x33333333;
r ^= x;
l ^= (x << 2);
x = ((r >>> 16) ^ l) & 0x0000ffff;
l ^= x;
r ^= (x << 16);
x = ((r >>> 4) ^ l) & 0x0f0f0f0f;
l ^= x;
r ^= (x << 4);
// Spread ints to bytes
x = [r, l];
for (i = 0; i < 8; i++) {
b[i] = (x[i>>>2] >>> (8 * (3 - (i % 4)))) % 256;
if (b[i] < 0) { b[i] += 256; } // unsigned
}
return b;
}
// Encrypt 16 bytes of text using passwd as key
function encrypt(t) {
return enc8(t.slice(0, 8)).concat(enc8(t.slice(8, 16)));
}
setKeys(passwd); // Setup keys
return {'encrypt': encrypt}; // Public interface
} // function DES
================================================
FILE: app/static/novnc/display.js
================================================
/*
* noVNC: HTML5 VNC client
* Copyright (C) 2012 Joel Martin
* Copyright (C) 2015 Samuel Mannehed for Cendio AB
* Licensed under MPL 2.0 (see LICENSE.txt)
*
* See README.md for usage and integration instructions.
*/
/*jslint browser: true, white: false */
/*global Util, Base64, changeCursor */
var Display;
(function () {
"use strict";
var SUPPORTS_IMAGEDATA_CONSTRUCTOR = false;
try {
new ImageData(new Uint8ClampedArray(1), 1, 1);
SUPPORTS_IMAGEDATA_CONSTRUCTOR = true;
} catch (ex) {
// ignore failure
}
Display = function (defaults) {
this._drawCtx = null;
this._c_forceCanvas = false;
this._renderQ = []; // queue drawing actions for in-oder rendering
// the full frame buffer (logical canvas) size
this._fb_width = 0;
this._fb_height = 0;
// the size limit of the viewport (start disabled)
this._maxWidth = 0;
this._maxHeight = 0;
// the visible "physical canvas" viewport
this._viewportLoc = { 'x': 0, 'y': 0, 'w': 0, 'h': 0 };
this._cleanRect = { 'x1': 0, 'y1': 0, 'x2': -1, 'y2': -1 };
this._prevDrawStyle = "";
this._tile = null;
this._tile16x16 = null;
this._tile_x = 0;
this._tile_y = 0;
Util.set_defaults(this, defaults, {
'true_color': true,
'colourMap': [],
'scale': 1.0,
'viewport': false,
'render_mode': ''
});
Util.Debug(">> Display.constructor");
if (!this._target) {
throw new Error("Target must be set");
}
if (typeof this._target === 'string') {
throw new Error('target must be a DOM element');
}
if (!this._target.getContext) {
throw new Error("no getContext method");
}
if (!this._drawCtx) {
this._drawCtx = this._target.getContext('2d');
}
Util.Debug("User Agent: " + navigator.userAgent);
if (Util.Engine.gecko) { Util.Debug("Browser: gecko " + Util.Engine.gecko); }
if (Util.Engine.webkit) { Util.Debug("Browser: webkit " + Util.Engine.webkit); }
if (Util.Engine.trident) { Util.Debug("Browser: trident " + Util.Engine.trident); }
if (Util.Engine.presto) { Util.Debug("Browser: presto " + Util.Engine.presto); }
this.clear();
// Check canvas features
if ('createImageData' in this._drawCtx) {
this._render_mode = 'canvas rendering';
} else {
throw new Error("Canvas does not support createImageData");
}
if (this._prefer_js === null) {
Util.Info("Prefering javascript operations");
this._prefer_js = true;
}
// Determine browser support for setting the cursor via data URI scheme
if (this._cursor_uri || this._cursor_uri === null ||
this._cursor_uri === undefined) {
this._cursor_uri = Util.browserSupportsCursorURIs();
}
Util.Debug("<< Display.constructor");
};
Display.prototype = {
// Public methods
viewportChangePos: function (deltaX, deltaY) {
var vp = this._viewportLoc;
deltaX = Math.floor(deltaX);
deltaY = Math.floor(deltaY);
if (!this._viewport) {
deltaX = -vp.w; // clamped later of out of bounds
deltaY = -vp.h;
}
var vx2 = vp.x + vp.w - 1;
var vy2 = vp.y + vp.h - 1;
// Position change
if (deltaX < 0 && vp.x + deltaX < 0) {
deltaX = -vp.x;
}
if (vx2 + deltaX >= this._fb_width) {
deltaX -= vx2 + deltaX - this._fb_width + 1;
}
if (vp.y + deltaY < 0) {
deltaY = -vp.y;
}
if (vy2 + deltaY >= this._fb_height) {
deltaY -= (vy2 + deltaY - this._fb_height + 1);
}
if (deltaX === 0 && deltaY === 0) {
return;
}
Util.Debug("viewportChange deltaX: " + deltaX + ", deltaY: " + deltaY);
vp.x += deltaX;
vx2 += deltaX;
vp.y += deltaY;
vy2 += deltaY;
// Update the clean rectangle
var cr = this._cleanRect;
if (vp.x > cr.x1) {
cr.x1 = vp.x;
}
if (vx2 < cr.x2) {
cr.x2 = vx2;
}
if (vp.y > cr.y1) {
cr.y1 = vp.y;
}
if (vy2 < cr.y2) {
cr.y2 = vy2;
}
var x1, w;
if (deltaX < 0) {
// Shift viewport left, redraw left section
x1 = 0;
w = -deltaX;
} else {
// Shift viewport right, redraw right section
x1 = vp.w - deltaX;
w = deltaX;
}
var y1, h;
if (deltaY < 0) {
// Shift viewport up, redraw top section
y1 = 0;
h = -deltaY;
} else {
// Shift viewport down, redraw bottom section
y1 = vp.h - deltaY;
h = deltaY;
}
var saveStyle = this._drawCtx.fillStyle;
var canvas = this._target;
this._drawCtx.fillStyle = "rgb(255,255,255)";
// Due to this bug among others [1] we need to disable the image-smoothing to
// avoid getting a blur effect when panning.
//
// 1. https://bugzilla.mozilla.org/show_bug.cgi?id=1194719
//
// We need to set these every time since all properties are reset
// when the the size is changed
if (this._drawCtx.mozImageSmoothingEnabled) {
this._drawCtx.mozImageSmoothingEnabled = false;
} else if (this._drawCtx.webkitImageSmoothingEnabled) {
this._drawCtx.webkitImageSmoothingEnabled = false;
} else if (this._drawCtx.msImageSmoothingEnabled) {
this._drawCtx.msImageSmoothingEnabled = false;
} else if (this._drawCtx.imageSmoothingEnabled) {
this._drawCtx.imageSmoothingEnabled = false;
}
// Copy the valid part of the viewport to the shifted location
this._drawCtx.drawImage(canvas, 0, 0, vp.w, vp.h, -deltaX, -deltaY, vp.w, vp.h);
if (deltaX !== 0) {
this._drawCtx.fillRect(x1, 0, w, vp.h);
}
if (deltaY !== 0) {
this._drawCtx.fillRect(0, y1, vp.w, h);
}
this._drawCtx.fillStyle = saveStyle;
},
viewportChangeSize: function(width, height) {
if (typeof(width) === "undefined" || typeof(height) === "undefined") {
Util.Debug("Setting viewport to full display region");
width = this._fb_width;
height = this._fb_height;
}
var vp = this._viewportLoc;
if (vp.w !== width || vp.h !== height) {
if (this._viewport) {
if (this._maxWidth !== 0 && width > this._maxWidth) {
width = this._maxWidth;
}
if (this._maxHeight !== 0 && height > this._maxHeight) {
height = this._maxHeight;
}
}
var cr = this._cleanRect;
if (width < vp.w && cr.x2 > vp.x + width - 1) {
cr.x2 = vp.x + width - 1;
}
if (height < vp.h && cr.y2 > vp.y + height - 1) {
cr.y2 = vp.y + height - 1;
}
vp.w = width;
vp.h = height;
var canvas = this._target;
if (canvas.width !== width || canvas.height !== height) {
// We have to save the canvas data since changing the size will clear it
var saveImg = null;
if (vp.w > 0 && vp.h > 0 && canvas.width > 0 && canvas.height > 0) {
var img_width = canvas.width < vp.w ? canvas.width : vp.w;
var img_height = canvas.height < vp.h ? canvas.height : vp.h;
saveImg = this._drawCtx.getImageData(0, 0, img_width, img_height);
}
if (canvas.width !== width) {
canvas.width = width;
canvas.style.width = width + 'px';
}
if (canvas.height !== height) {
canvas.height = height;
canvas.style.height = height + 'px';
}
if (saveImg) {
this._drawCtx.putImageData(saveImg, 0, 0);
}
}
}
},
// Return a map of clean and dirty areas of the viewport and reset the
// tracking of clean and dirty areas
//
// Returns: { 'cleanBox': { 'x': x, 'y': y, 'w': w, 'h': h},
// 'dirtyBoxes': [{ 'x': x, 'y': y, 'w': w, 'h': h }, ...] }
getCleanDirtyReset: function () {
var vp = this._viewportLoc;
var cr = this._cleanRect;
var cleanBox = { 'x': cr.x1, 'y': cr.y1,
'w': cr.x2 - cr.x1 + 1, 'h': cr.y2 - cr.y1 + 1 };
var dirtyBoxes = [];
if (cr.x1 >= cr.x2 || cr.y1 >= cr.y2) {
// Whole viewport is dirty
dirtyBoxes.push({ 'x': vp.x, 'y': vp.y, 'w': vp.w, 'h': vp.h });
} else {
// Redraw dirty regions
var vx2 = vp.x + vp.w - 1;
var vy2 = vp.y + vp.h - 1;
if (vp.x < cr.x1) {
// left side dirty region
dirtyBoxes.push({'x': vp.x, 'y': vp.y,
'w': cr.x1 - vp.x + 1, 'h': vp.h});
}
if (vx2 > cr.x2) {
// right side dirty region
dirtyBoxes.push({'x': cr.x2 + 1, 'y': vp.y,
'w': vx2 - cr.x2, 'h': vp.h});
}
if(vp.y < cr.y1) {
// top/middle dirty region
dirtyBoxes.push({'x': cr.x1, 'y': vp.y,
'w': cr.x2 - cr.x1 + 1, 'h': cr.y1 - vp.y});
}
if (vy2 > cr.y2) {
// bottom/middle dirty region
dirtyBoxes.push({'x': cr.x1, 'y': cr.y2 + 1,
'w': cr.x2 - cr.x1 + 1, 'h': vy2 - cr.y2});
}
}
this._cleanRect = {'x1': vp.x, 'y1': vp.y,
'x2': vp.x + vp.w - 1, 'y2': vp.y + vp.h - 1};
return {'cleanBox': cleanBox, 'dirtyBoxes': dirtyBoxes};
},
absX: function (x) {
return x + this._viewportLoc.x;
},
absY: function (y) {
return y + this._viewportLoc.y;
},
resize: function (width, height) {
this._prevDrawStyle = "";
this._fb_width = width;
this._fb_height = height;
this._rescale(this._scale);
this.viewportChangeSize();
},
clear: function () {
if (this._logo) {
this.resize(this._logo.width, this._logo.height);
this.blitStringImage(this._logo.data, 0, 0);
} else {
if (Util.Engine.trident === 6) {
// NB(directxman12): there's a bug in IE10 where we can fail to actually
// clear the canvas here because of the resize.
// Clearing the current viewport first fixes the issue
this._drawCtx.clearRect(0, 0, this._viewportLoc.w, this._viewportLoc.h);
}
this.resize(240, 20);
this._drawCtx.clearRect(0, 0, this._viewportLoc.w, this._viewportLoc.h);
}
this._renderQ = [];
},
fillRect: function (x, y, width, height, color, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) {
this.renderQ_push({
'type': 'fill',
'x': x,
'y': y,
'width': width,
'height': height,
'color': color
});
} else {
this._setFillColor(color);
this._drawCtx.fillRect(x - this._viewportLoc.x, y - this._viewportLoc.y, width, height);
}
},
copyImage: function (old_x, old_y, new_x, new_y, w, h, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) {
this.renderQ_push({
'type': 'copy',
'old_x': old_x,
'old_y': old_y,
'x': new_x,
'y': new_y,
'width': w,
'height': h,
});
} else {
var x1 = old_x - this._viewportLoc.x;
var y1 = old_y - this._viewportLoc.y;
var x2 = new_x - this._viewportLoc.x;
var y2 = new_y - this._viewportLoc.y;
this._drawCtx.drawImage(this._target, x1, y1, w, h, x2, y2, w, h);
}
},
// start updating a tile
startTile: function (x, y, width, height, color) {
this._tile_x = x;
this._tile_y = y;
if (width === 16 && height === 16) {
this._tile = this._tile16x16;
} else {
this._tile = this._drawCtx.createImageData(width, height);
}
if (this._prefer_js) {
var bgr;
if (this._true_color) {
bgr = color;
} else {
bgr = this._colourMap[color[0]];
}
var red = bgr[2];
var green = bgr[1];
var blue = bgr[0];
var data = this._tile.data;
for (var i = 0; i < width * height * 4; i += 4) {
data[i] = red;
data[i + 1] = green;
data[i + 2] = blue;
data[i + 3] = 255;
}
} else {
this.fillRect(x, y, width, height, color, true);
}
},
// update sub-rectangle of the current tile
subTile: function (x, y, w, h, color) {
if (this._prefer_js) {
var bgr;
if (this._true_color) {
bgr = color;
} else {
bgr = this._colourMap[color[0]];
}
var red = bgr[2];
var green = bgr[1];
var blue = bgr[0];
var xend = x + w;
var yend = y + h;
var data = this._tile.data;
var width = this._tile.width;
for (var j = y; j < yend; j++) {
for (var i = x; i < xend; i++) {
var p = (i + (j * width)) * 4;
data[p] = red;
data[p + 1] = green;
data[p + 2] = blue;
data[p + 3] = 255;
}
}
} else {
this.fillRect(this._tile_x + x, this._tile_y + y, w, h, color, true);
}
},
// draw the current tile to the screen
finishTile: function () {
if (this._prefer_js) {
this._drawCtx.putImageData(this._tile, this._tile_x - this._viewportLoc.x,
this._tile_y - this._viewportLoc.y);
}
// else: No-op -- already done by setSubTile
},
blitImage: function (x, y, width, height, arr, offset, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) {
this.renderQ_push({
'type': 'blit',
'data': arr,
'x': x,
'y': y,
'width': width,
'height': height,
});
} else if (this._true_color) {
this._bgrxImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset);
} else {
this._cmapImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset);
}
},
blitRgbImage: function (x, y , width, height, arr, offset, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) {
this.renderQ_push({
'type': 'blitRgb',
'data': arr,
'x': x,
'y': y,
'width': width,
'height': height,
});
} else if (this._true_color) {
this._rgbImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset);
} else {
// probably wrong?
this._cmapImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset);
}
},
blitRgbxImage: function (x, y, width, height, arr, offset, from_queue) {
if (this._renderQ.length !== 0 && !from_queue) {
// NB(directxman12): it's technically more performant here to use preallocated arrays, but it
// but it's a lot of extra work for not a lot of payoff -- if we're using the render queue,
// this probably isn't getting called *nearly* as much
var new_arr = new Uint8Array(width * height * 4);
new_arr.set(new Uint8Array(arr.buffer, 0, new_arr.length));
this.renderQ_push({
'type': 'blitRgbx',
'data': new_arr,
'x': x,
'y': y,
'width': width,
'height': height,
});
} else {
this._rgbxImageData(x, y, this._viewportLoc.x, this._viewportLoc.y, width, height, arr, offset);
}
},
blitStringImage: function (str, x, y) {
var img = new Image();
img.onload = function () {
this._drawCtx.drawImage(img, x - this._viewportLoc.x, y - this._viewportLoc.y);
}.bind(this);
img.src = str;
return img; // for debugging purposes
},
// wrap ctx.drawImage but relative to viewport
drawImage: function (img, x, y) {
this._drawCtx.drawImage(img, x - this._viewportLoc.x, y - this._viewportLoc.y);
},
renderQ_push: function (action) {
this._renderQ.push(action);
if (this._renderQ.length === 1) {
// If this can be rendered immediately it will be, otherwise
// the scanner will start polling the queue (every
// requestAnimationFrame interval)
this._scan_renderQ();
}
},
changeCursor: function (pixels, mask, hotx, hoty, w, h) {
if (this._cursor_uri === false) {
Util.Warn("changeCursor called but no cursor data URI support");
return;
}
if (this._true_color) {
Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h);
} else {
Display.changeCursor(this._target, pixels, mask, hotx, hoty, w, h, this._colourMap);
}
},
defaultCursor: function () {
this._target.style.cursor = "default";
},
disableLocalCursor: function () {
this._target.style.cursor = "none";
},
clippingDisplay: function () {
var vp = this._viewportLoc;
var fbClip = this._fb_width > vp.w || this._fb_height > vp.h;
var limitedVp = this._maxWidth !== 0 && this._maxHeight !== 0;
var clipping = false;
if (limitedVp) {
clipping = vp.w > this._maxWidth || vp.h > this._maxHeight;
}
return fbClip || (limitedVp && clipping);
},
// Overridden getters/setters
get_context: function () {
return this._drawCtx;
},
set_scale: function (scale) {
this._rescale(scale);
},
set_width: function (w) {
this._fb_width = w;
},
get_width: function () {
return this._fb_width;
},
set_height: function (h) {
this._fb_height = h;
},
get_height: function () {
return this._fb_height;
},
autoscale: function (containerWidth, containerHeight, downscaleOnly) {
var targetAspectRatio = containerWidth / containerHeight;
var fbAspectRatio = this._fb_width / this._fb_height;
var scaleRatio;
if (fbAspectRatio >= targetAspectRatio) {
scaleRatio = containerWidth / this._fb_width;
} else {
scaleRatio = containerHeight / this._fb_height;
}
var targetW, targetH;
if (scaleRatio > 1.0 && downscaleOnly) {
targetW = this._fb_width;
targetH = this._fb_height;
scaleRatio = 1.0;
} else if (fbAspectRatio >= targetAspectRatio) {
targetW = containerWidth;
targetH = Math.round(containerWidth / fbAspectRatio);
} else {
targetW = Math.round(containerHeight * fbAspectRatio);
targetH = containerHeight;
}
// NB(directxman12): If you set the width directly, or set the
// style width to a number, the canvas is cleared.
// However, if you set the style width to a string
// ('NNNpx'), the canvas is scaled without clearing.
this._target.style.width = targetW + 'px';
this._target.style.height = targetH + 'px';
this._scale = scaleRatio;
return scaleRatio; // so that the mouse, etc scale can be set
},
// Private Methods
_rescale: function (factor) {
this._scale = factor;
var w;
var h;
if (this._viewport &&
this._maxWidth !== 0 && this._maxHeight !== 0) {
w = Math.min(this._fb_width, this._maxWidth);
h = Math.min(this._fb_height, this._maxHeight);
} else {
w = this._fb_width;
h = this._fb_height;
}
this._target.style.width = Math.round(factor * w) + 'px';
this._target.style.height = Math.round(factor * h) + 'px';
},
_setFillColor: function (color) {
var bgr;
if (this._true_color) {
bgr = color;
} else {
bgr = this._colourMap[color];
}
var newStyle = 'rgb(' + bgr[2] + ',' + bgr[1] + ',' + bgr[0] + ')';
if (newStyle !== this._prevDrawStyle) {
this._drawCtx.fillStyle = newStyle;
this._prevDrawStyle = newStyle;
}
},
_rgbImageData: function (x, y, vx, vy, width, height, arr, offset) {
var img = this._drawCtx.createImageData(width, height);
var data = img.data;
for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 3) {
data[i] = arr[j];
data[i + 1] = arr[j + 1];
data[i + 2] = arr[j + 2];
data[i + 3] = 255; // Alpha
}
this._drawCtx.putImageData(img, x - vx, y - vy);
},
_bgrxImageData: function (x, y, vx, vy, width, height, arr, offset) {
var img = this._drawCtx.createImageData(width, height);
var data = img.data;
for (var i = 0, j = offset; i < width * height * 4; i += 4, j += 4) {
data[i] = arr[j + 2];
data[i + 1] = arr[j + 1];
data[i + 2] = arr[j];
data[i + 3] = 255; // Alpha
}
this._drawCtx.putImageData(img, x - vx, y - vy);
},
_rgbxImageData: function (x, y, vx, vy, width, height, arr, offset) {
// NB(directxman12): arr must be an Type Array view
var img;
if (SUPPORTS_IMAGEDATA_CONSTRUCTOR) {
img = new ImageData(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4), width, height);
} else {
img = this._drawCtx.createImageData(width, height);
img.data.set(new Uint8ClampedArray(arr.buffer, arr.byteOffset, width * height * 4));
}
this._drawCtx.putImageData(img, x - vx, y - vy);
},
_cmapImageData: function (x, y, vx, vy, width, height, arr, offset) {
var img = this._drawCtx.createImageData(width, height);
var data = img.data;
var cmap = this._colourMap;
for (var i = 0, j = offset; i < width * height * 4; i += 4, j++) {
var bgr = cmap[arr[j]];
data[i] = bgr[2];
data[i + 1] = bgr[1];
data[i + 2] = bgr[0];
data[i + 3] = 255; // Alpha
}
this._drawCtx.putImageData(img, x - vx, y - vy);
},
_scan_renderQ: function () {
var ready = true;
while (ready && this._renderQ.length > 0) {
var a = this._renderQ[0];
switch (a.type) {
case 'copy':
this.copyImage(a.old_x, a.old_y, a.x, a.y, a.width, a.height, true);
break;
case 'fill':
this.fillRect(a.x, a.y, a.width, a.height, a.color, true);
break;
case 'blit':
this.blitImage(a.x, a.y, a.width, a.height, a.data, 0, true);
break;
case 'blitRgb':
this.blitRgbImage(a.x, a.y, a.width, a.height, a.data, 0, true);
break;
case 'blitRgbx':
this.blitRgbxImage(a.x, a.y, a.width, a.height, a.data, 0, true);
break;
case 'img':
if (a.img.complete) {
this.drawImage(a.img, a.x, a.y);
} else {
// We need to wait for this image to 'load'
// to keep things in-order
ready = false;
}
break;
}
if (ready) {
this._renderQ.shift();
}
}
if (this._renderQ.length > 0) {
requestAnimFrame(this._scan_renderQ.bind(this));
}
},
};
Util.make_properties(Display, [
['target', 'wo', 'dom'], // Canvas element for rendering
['context', 'ro', 'raw'], // Canvas 2D context for rendering (read-only)
['logo', 'rw', 'raw'], // Logo to display when cleared: {"width": w, "height": h, "data": data}
['true_color', 'rw', 'bool'], // Use true-color pixel data
['colourMap', 'rw', 'arr'], // Colour map array (when not true-color)
['scale', 'rw', 'float'], // Display area scale factor 0.0 - 1.0
['viewport', 'rw', 'bool'], // Use viewport clipping
['width', 'rw', 'int'], // Display area width
['height', 'rw', 'int'], // Display area height
['maxWidth', 'rw', 'int'], // Viewport max width (0 if disabled)
['maxHeight', 'rw', 'int'], // Viewport max height (0 if disabled)
['render_mode', 'ro', 'str'], // Canvas rendering mode (read-only)
['prefer_js', 'rw', 'str'], // Prefer Javascript over canvas methods
['cursor_uri', 'rw', 'raw'] // Can we render cursor using data URI
]);
// Class Methods
Display.changeCursor = function (target, pixels, mask, hotx, hoty, w0, h0, cmap) {
var w = w0;
var h = h0;
if (h < w) {
h = w; // increase h to make it square
} else {
w = h; // increase w to make it square
}
var cur = [];
// Push multi-byte little-endian values
cur.push16le = function (num) {
this.push(num & 0xFF, (num >> 8) & 0xFF);
};
cur.push32le = function (num) {
this.push(num & 0xFF,
(num >> 8) & 0xFF,
(num >> 16) & 0xFF,
(num >> 24) & 0xFF);
};
var IHDRsz = 40;
var RGBsz = w * h * 4;
var XORsz = Math.ceil((w * h) / 8.0);
var ANDsz = Math.ceil((w * h) / 8.0);
cur.push16le(0); // 0: Reserved
cur.push16le(2); // 2: .CUR type
cur.push16le(1); // 4: Number of images, 1 for non-animated ico
// Cursor #1 header (ICONDIRENTRY)
cur.push(w); // 6: width
cur.push(h); // 7: height
cur.push(0); // 8: colors, 0 -> true-color
cur.push(0); // 9: reserved
cur.push16le(hotx); // 10: hotspot x coordinate
cur.push16le(hoty); // 12: hotspot y coordinate
cur.push32le(IHDRsz + RGBsz + XORsz + ANDsz);
// 14: cursor data byte size
cur.push32le(22); // 18: offset of cursor data in the file
// Cursor #1 InfoHeader (ICONIMAGE/BITMAPINFO)
cur.push32le(IHDRsz); // 22: InfoHeader size
cur.push32le(w); // 26: Cursor width
cur.push32le(h * 2); // 30: XOR+AND height
cur.push16le(1); // 34: number of planes
cur.push16le(32); // 36: bits per pixel
cur.push32le(0); // 38: Type of compression
cur.push32le(XORsz + ANDsz);
// 42: Size of Image
cur.push32le(0); // 46: reserved
cur.push32le(0); // 50: reserved
cur.push32le(0); // 54: reserved
cur.push32le(0); // 58: reserved
// 62: color data (RGBQUAD icColors[])
var y, x;
for (y = h - 1; y >= 0; y--) {
for (x = 0; x < w; x++) {
if (x >= w0 || y >= h0) {
cur.push(0); // blue
cur.push(0); // green
cur.push(0); // red
cur.push(0); // alpha
} else {
var idx = y * Math.ceil(w0 / 8) + Math.floor(x / 8);
var alpha = (mask[idx] << (x % 8)) & 0x80 ? 255 : 0;
if (cmap) {
idx = (w0 * y) + x;
var rgb = cmap[pixels[idx]];
cur.push(rgb[2]); // blue
cur.push(rgb[1]); // green
cur.push(rgb[0]); // red
cur.push(alpha); // alpha
} else {
idx = ((w0 * y) + x) * 4;
cur.push(pixels[idx + 2]); // blue
cur.push(pixels[idx + 1]); // green
cur.push(pixels[idx]); // red
cur.push(alpha); // alpha
}
}
}
}
// XOR/bitmask data (BYTE icXOR[])
// (ignored, just needs to be the right size)
for (y = 0; y < h; y++) {
for (x = 0; x < Math.ceil(w / 8); x++) {
cur.push(0);
}
}
// AND/bitmask data (BYTE icAND[])
// (ignored, just needs to be the right size)
for (y = 0; y < h; y++) {
for (x = 0; x < Math.ceil(w / 8); x++) {
cur.push(0);
}
}
var url = 'data:image/x-icon;base64,' + Base64.encode(cur);
target.style.cursor = 'url(' + url + ')' + hotx + ' ' + hoty + ', default';
};
})();
================================================
FILE: app/static/novnc/inflator.js
================================================
(function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.inflator = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';
var TYPED_OK = (typeof Uint8Array !== 'undefined') &&
(typeof Uint16Array !== 'undefined') &&
(typeof Int32Array !== 'undefined');
exports.assign = function (obj /*from1, from2, from3, ...*/) {
var sources = Array.prototype.slice.call(arguments, 1);
while (sources.length) {
var source = sources.shift();
if (!source) { continue; }
if (typeof source !== 'object') {
throw new TypeError(source + 'must be non-object');
}
for (var p in source) {
if (source.hasOwnProperty(p)) {
obj[p] = source[p];
}
}
}
return obj;
};
// reduce buffer size, avoiding mem copy
exports.shrinkBuf = function (buf, size) {
if (buf.length === size) { return buf; }
if (buf.subarray) { return buf.subarray(0, size); }
buf.length = size;
return buf;
};
var fnTyped = {
arraySet: function (dest, src, src_offs, len, dest_offs) {
if (src.subarray && dest.subarray) {
dest.set(src.subarray(src_offs, src_offs+len), dest_offs);
return;
}
// Fallback to ordinary array
for (var i=0; i<len; i++) {
dest[dest_offs + i] = src[src_offs + i];
}
},
// Join array of chunks to single array.
flattenChunks: function(chunks) {
var i, l, len, pos, chunk, result;
// calculate data length
len = 0;
for (i=0, l=chunks.length; i<l; i++) {
len += chunks[i].length;
}
// join chunks
result = new Uint8Array(len);
pos = 0;
for (i=0, l=chunks.length; i<l; i++) {
chunk = chunks[i];
result.set(chunk, pos);
pos += chunk.length;
}
return result;
}
};
var fnUntyped = {
arraySet: function (dest, src, src_offs, len, dest_offs) {
for (var i=0; i<len; i++) {
dest[dest_offs + i] = src[src_offs + i];
}
},
// Join array of chunks to single array.
flattenChunks: function(chunks) {
return [].concat.apply([], chunks);
}
};
// Enable/Disable typed arrays use, for testing
//
exports.setTyped = function (on) {
if (on) {
exports.Buf8 = Uint8Array;
exports.Buf16 = Uint16Array;
exports.Buf32 = Int32Array;
exports.assign(exports, fnTyped);
} else {
exports.Buf8 = Array;
exports.Buf16 = Array;
exports.Buf32 = Array;
exports.assign(exports, fnUntyped);
}
};
exports.setTyped(TYPED_OK);
},{}],2:[function(require,module,exports){
'use strict';
// Note: adler32 takes 12% for level 0 and 2% for level 6.
// It doesn't worth to make additional optimizationa as in original.
// Small size is preferable.
function adler32(adler, buf, len, pos) {
var s1 = (adler & 0xffff) |0,
s2 = ((adler >>> 16) & 0xffff) |0,
n = 0;
while (len !== 0) {
// Set limit ~ twice less than 5552, to keep
// s2 in 31-bits, because we force signed ints.
// in other case %= will fail.
n = len > 2000 ? 2000 : len;
len -= n;
do {
s1 = (s1 + buf[pos++]) |0;
s2 = (s2 + s1) |0;
} while (--n);
s1 %= 65521;
s2 %= 65521;
}
return (s1 | (s2 << 16)) |0;
}
module.exports = adler32;
},{}],3:[function(require,module,exports){
'use strict';
// Note: we can't get significant speed boost here.
// So write code to minimize size - no pregenerated tables
// and array tools dependencies.
// Use ordinary array, since untyped makes no boost here
function makeTable() {
var c, table = [];
for (var n =0; n < 256; n++) {
c = n;
for (var k =0; k < 8; k++) {
c = ((c&1) ? (0xEDB88320 ^ (c >>> 1)) : (c >>> 1));
}
table[n] = c;
}
return table;
}
// Create table on load. Just 255 signed longs. Not a problem.
var crcTable = makeTable();
function crc32(crc, buf, len, pos) {
var t = crcTable,
end = pos + len;
crc = crc ^ (-1);
for (var i = pos; i < end; i++) {
crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
}
return (crc ^ (-1)); // >>> 0;
}
module.exports = crc32;
},{}],4:[function(require,module,exports){
'use strict';
// See state defs from inflate.js
var BAD = 30; /* got a data error -- remain here until reset */
var TYPE = 12; /* i: waiting for type bits, including last-flag bit */
/*
Decode literal, length, and distance codes and write out the resulting
literal and match bytes until either not enough input or output is
available, an end-of-block is encountered, or a data error is encountered.
When large enough input and output buffers are supplied to inflate(), for
example, a 16K input buffer and a 64K output buffer, more than 95% of the
inflate execution time is spent in this routine.
Entry assumptions:
state.mode === LEN
strm.avail_in >= 6
strm.avail_out >= 258
start >= strm.avail_out
state.bits < 8
On return, state.mode is one of:
LEN -- ran out of enough output space or enough available input
TYPE -- reached end of block code, inflate() to interpret next block
BAD -- error in block data
Notes:
- The maximum input bits used by a length/distance pair is 15 bits for the
length code, 5 bits for the length extra, 15 bits for the distance code,
and 13 bits for the distance extra. This totals 48 bits, or six bytes.
Therefore if strm.avail_in >= 6, then there is enough input to avoid
checking for available input while decoding.
- The maximum bytes that a single length/distance pair can output is 258
bytes, which is the maximum length that can be coded. inflate_fast()
requires strm.avail_out >= 258 for each loop to avoid checking for
output space.
*/
module.exports = function inflate_fast(strm, start) {
var state;
var _in; /* local strm.input */
var last; /* have enough input while in < last */
var _out; /* local strm.output */
var beg; /* inflate()'s initial strm.output */
var end; /* while out < end, enough space available */
//#ifdef INFLATE_STRICT
var dmax; /* maximum distance from zlib header */
//#endif
var wsize; /* window size or zero if not using window */
var whave; /* valid bytes in the window */
var wnext; /* window write index */
var window; /* allocated sliding window, if wsize != 0 */
var hold; /* local strm.hold */
var bits; /* local strm.bits */
var lcode; /* local strm.lencode */
var dcode; /* local strm.distcode */
var lmask; /* mask for first level of length codes */
var dmask; /* mask for first level of distance codes */
var here; /* retrieved table entry */
var op; /* code bits, operation, extra bits, or */
/* window position, window bytes to copy */
var len; /* match length, unused bytes */
var dist; /* match distance */
var from; /* where to copy match from */
var from_source;
var input, output; // JS specific, because we have no pointers
/* copy state to local variables */
state = strm.state;
//here = state.here;
_in = strm.next_in;
input = strm.input;
last = _in + (strm.avail_in - 5);
_out = strm.next_out;
output = strm.output;
beg = _out - (start - strm.avail_out);
end = _out + (strm.avail_out - 257);
//#ifdef INFLATE_STRICT
dmax = state.dmax;
//#endif
wsize = state.wsize;
whave = state.whave;
wnext = state.wnext;
window = state.window;
hold = state.hold;
bits = state.bits;
lcode = state.lencode;
dcode = state.distcode;
lmask = (1 << state.lenbits) - 1;
dmask = (1 << state.distbits) - 1;
/* decode literals and length/distances until end-of-block or not enough
input data or output space */
top:
do {
if (bits < 15) {
hold += input[_in++] << bits;
bits += 8;
hold += input[_in++] << bits;
bits += 8;
}
here = lcode[hold & lmask];
dolen:
for (;;) { // Goto emulation
op = here >>> 24/*here.bits*/;
hold >>>= op;
bits -= op;
op = (here >>> 16) & 0xff/*here.op*/;
if (op === 0) { /* literal */
//Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
// "inflate: literal '%c'\n" :
// "inflate: literal 0x%02x\n", here.val));
output[_out++] = here & 0xffff/*here.val*/;
}
else if (op & 16) { /* length base */
len = here & 0xffff/*here.val*/;
op &= 15; /* number of extra bits */
if (op) {
if (bits < op) {
hold += input[_in++] << bits;
bits += 8;
}
len += hold & ((1 << op) - 1);
hold >>>= op;
bits -= op;
}
//Tracevv((stderr, "inflate: length %u\n", len));
if (bits < 15) {
hold += input[_in++] << bits;
bits += 8;
hold += input[_in++] << bits;
bits += 8;
}
here = dcode[hold & dmask];
dodist:
for (;;) { // goto emulation
op = here >>> 24/*here.bits*/;
hold >>>= op;
bits -= op;
op = (here >>> 16) & 0xff/*here.op*/;
if (op & 16) { /* distance base */
dist = here & 0xffff/*here.val*/;
op &= 15; /* number of extra bits */
if (bits < op) {
hold += input[_in++] << bits;
bits += 8;
if (bits < op) {
hold += input[_in++] << bits;
bits += 8;
}
}
dist += hold & ((1 << op) - 1);
//#ifdef INFLATE_STRICT
if (dist > dmax) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break top;
}
//#endif
hold >>>= op;
bits -= op;
//Tracevv((stderr, "inflate: distance %u\n", dist));
op = _out - beg; /* max distance in output */
if (dist > op) { /* see if copy from window */
op = dist - op; /* distance back in window */
if (op > whave) {
if (state.sane) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break top;
}
// (!) This block is disabled in zlib defailts,
// don't enable it for binary compatibility
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
// if (len <= op - whave) {
// do {
// output[_out++] = 0;
// } while (--len);
// continue top;
// }
// len -= op - whave;
// do {
// output[_out++] = 0;
// } while (--op > whave);
// if (op === 0) {
// from = _out - dist;
// do {
// output[_out++] = output[from++];
// } while (--len);
// continue top;
// }
//#endif
}
from = 0; // window index
from_source = window;
if (wnext === 0) { /* very common case */
from += wsize - op;
if (op < len) { /* some from window */
len -= op;
do {
output[_out++] = window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
}
}
else if (wnext < op) { /* wrap around window */
from += wsize + wnext - op;
op -= wnext;
if (op < len) { /* some from end of window */
len -= op;
do {
output[_out++] = window[from++];
} while (--op);
from = 0;
if (wnext < len) { /* some from start of window */
op = wnext;
len -= op;
do {
output[_out++] = window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
}
}
}
else { /* contiguous in window */
from += wnext - op;
if (op < len) { /* some from window */
len -= op;
do {
output[_out++] = window[from++];
} while (--op);
from = _out - dist; /* rest from output */
from_source = output;
}
}
while (len > 2) {
output[_out++] = from_source[from++];
output[_out++] = from_source[from++];
output[_out++] = from_source[from++];
len -= 3;
}
if (len) {
output[_out++] = from_source[from++];
if (len > 1) {
output[_out++] = from_source[from++];
}
}
}
else {
from = _out - dist; /* copy direct from output */
do { /* minimum length is three */
output[_out++] = output[from++];
output[_out++] = output[from++];
output[_out++] = output[from++];
len -= 3;
} while (len > 2);
if (len) {
output[_out++] = output[from++];
if (len > 1) {
output[_out++] = output[from++];
}
}
}
}
else if ((op & 64) === 0) { /* 2nd level distance code */
here = dcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
continue dodist;
}
else {
strm.msg = 'invalid distance code';
state.mode = BAD;
break top;
}
break; // need to emulate goto via "continue"
}
}
else if ((op & 64) === 0) { /* 2nd level length code */
here = lcode[(here & 0xffff)/*here.val*/ + (hold & ((1 << op) - 1))];
continue dolen;
}
else if (op & 32) { /* end-of-block */
//Tracevv((stderr, "inflate: end of block\n"));
state.mode = TYPE;
break top;
}
else {
strm.msg = 'invalid literal/length code';
state.mode = BAD;
break top;
}
break; // need to emulate goto via "continue"
}
} while (_in < last && _out < end);
/* return unused bytes (on entry, bits < 8, so in won't go too far back) */
len = bits >> 3;
_in -= len;
bits -= len << 3;
hold &= (1 << bits) - 1;
/* update state and return */
strm.next_in = _in;
strm.next_out = _out;
strm.avail_in = (_in < last ? 5 + (last - _in) : 5 - (_in - last));
strm.avail_out = (_out < end ? 257 + (end - _out) : 257 - (_out - end));
state.hold = hold;
state.bits = bits;
return;
};
},{}],5:[function(require,module,exports){
'use strict';
var utils = require('../utils/common');
var adler32 = require('./adler32');
var crc32 = require('./crc32');
var inflate_fast = require('./inffast');
var inflate_table = require('./inftrees');
var CODES = 0;
var LENS = 1;
var DISTS = 2;
/* Public constants ==========================================================*/
/* ===========================================================================*/
/* Allowed flush values; see deflate() and inflate() below for details */
//var Z_NO_FLUSH = 0;
//var Z_PARTIAL_FLUSH = 1;
//var Z_SYNC_FLUSH = 2;
//var Z_FULL_FLUSH = 3;
var Z_FINISH = 4;
var Z_BLOCK = 5;
var Z_TREES = 6;
/* Return codes for the compression/decompression functions. Negative values
* are errors, positive values are used for special but normal events.
*/
var Z_OK = 0;
var Z_STREAM_END = 1;
var Z_NEED_DICT = 2;
//var Z_ERRNO = -1;
var Z_STREAM_ERROR = -2;
var Z_DATA_ERROR = -3;
var Z_MEM_ERROR = -4;
var Z_BUF_ERROR = -5;
//var Z_VERSION_ERROR = -6;
/* The deflate compression method */
var Z_DEFLATED = 8;
/* STATES ====================================================================*/
/* ===========================================================================*/
var HEAD = 1; /* i: waiting for magic header */
var FLAGS = 2; /* i: waiting for method and flags (gzip) */
var TIME = 3; /* i: waiting for modification time (gzip) */
var OS = 4; /* i: waiting for extra flags and operating system (gzip) */
var EXLEN = 5; /* i: waiting for extra length (gzip) */
var EXTRA = 6; /* i: waiting for extra bytes (gzip) */
var NAME = 7; /* i: waiting for end of file name (gzip) */
var COMMENT = 8; /* i: waiting for end of comment (gzip) */
var HCRC = 9; /* i: waiting for header crc (gzip) */
var DICTID = 10; /* i: waiting for dictionary check value */
var DICT = 11; /* waiting for inflateSetDictionary() call */
var TYPE = 12; /* i: waiting for type bits, including last-flag bit */
var TYPEDO = 13; /* i: same, but skip check to exit inflate on new block */
var STORED = 14; /* i: waiting for stored size (length and complement) */
var COPY_ = 15; /* i/o: same as COPY below, but only first time in */
var COPY = 16; /* i/o: waiting for input or output to copy stored block */
var TABLE = 17; /* i: waiting for dynamic block table lengths */
var LENLENS = 18; /* i: waiting for code length code lengths */
var CODELENS = 19; /* i: waiting for length/lit and distance code lengths */
var LEN_ = 20; /* i: same as LEN below, but only first time in */
var LEN = 21; /* i: waiting for length/lit/eob code */
var LENEXT = 22; /* i: waiting for length extra bits */
var DIST = 23; /* i: waiting for distance code */
var DISTEXT = 24; /* i: waiting for distance extra bits */
var MATCH = 25; /* o: waiting for output space to copy string */
var LIT = 26; /* o: waiting for output space to write literal */
var CHECK = 27; /* i: waiting for 32-bit check value */
var LENGTH = 28; /* i: waiting for 32-bit length (gzip) */
var DONE = 29; /* finished check, done -- remain here until reset */
var BAD = 30; /* got a data error -- remain here until reset */
var MEM = 31; /* got an inflate() memory error -- remain here until reset */
var SYNC = 32; /* looking for synchronization bytes to restart inflate() */
/* ===========================================================================*/
var ENOUGH_LENS = 852;
var ENOUGH_DISTS = 592;
//var ENOUGH = (ENOUGH_LENS+ENOUGH_DISTS);
var MAX_WBITS = 15;
/* 32K LZ77 window */
var DEF_WBITS = MAX_WBITS;
function ZSWAP32(q) {
return (((q >>> 24) & 0xff) +
((q >>> 8) & 0xff00) +
((q & 0xff00) << 8) +
((q & 0xff) << 24));
}
function InflateState() {
this.mode = 0; /* current inflate mode */
this.last = false; /* true if processing last block */
this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */
this.havedict = false; /* true if dictionary provided */
this.flags = 0; /* gzip header method and flags (0 if zlib) */
this.dmax = 0; /* zlib header max distance (INFLATE_STRICT) */
this.check = 0; /* protected copy of check value */
this.total = 0; /* protected copy of output count */
// TODO: may be {}
this.head = null; /* where to save gzip header information */
/* sliding window */
this.wbits = 0; /* log base 2 of requested window size */
this.wsize = 0; /* window size or zero if not using window */
this.whave = 0; /* valid bytes in the window */
this.wnext = 0; /* window write index */
this.window = null; /* allocated sliding window, if needed */
/* bit accumulator */
this.hold = 0; /* input bit accumulator */
this.bits = 0; /* number of bits in "in" */
/* for string and stored block copying */
this.length = 0; /* literal or length of data to copy */
this.offset = 0; /* distance back to copy string from */
/* for table and code decoding */
this.extra = 0; /* extra bits needed */
/* fixed and dynamic code tables */
this.lencode = null; /* starting table for length/literal codes */
this.distcode = null; /* starting table for distance codes */
this.lenbits = 0; /* index bits for lencode */
this.distbits = 0; /* index bits for distcode */
/* dynamic table building */
this.ncode = 0; /* number of code length code lengths */
this.nlen = 0; /* number of length code lengths */
this.ndist = 0; /* number of distance code lengths */
this.have = 0; /* number of code lengths in lens[] */
this.next = null; /* next available space in codes[] */
this.lens = new utils.Buf16(320); /* temporary storage for code lengths */
this.work = new utils.Buf16(288); /* work area for code table building */
/*
because we don't have pointers in js, we use lencode and distcode directly
as buffers so we don't need codes
*/
//this.codes = new utils.Buf32(ENOUGH); /* space for code tables */
this.lendyn = null; /* dynamic table for length/literal codes (JS specific) */
this.distdyn = null; /* dynamic table for distance codes (JS specific) */
this.sane = 0; /* if false, allow invalid distance too far */
this.back = 0; /* bits back of last unprocessed length/lit */
this.was = 0; /* initial length of match */
}
function inflateResetKeep(strm) {
var state;
if (!strm || !strm.state) { return Z_STREAM_ERROR; }
state = strm.state;
strm.total_in = strm.total_out = state.total = 0;
strm.msg = ''; /*Z_NULL*/
if (state.wrap) { /* to support ill-conceived Java test suite */
strm.adler = state.wrap & 1;
}
state.mode = HEAD;
state.last = 0;
state.havedict = 0;
state.dmax = 32768;
state.head = null/*Z_NULL*/;
state.hold = 0;
state.bits = 0;
//state.lencode = state.distcode = state.next = state.codes;
state.lencode = state.lendyn = new utils.Buf32(ENOUGH_LENS);
state.distcode = state.distdyn = new utils.Buf32(ENOUGH_DISTS);
state.sane = 1;
state.back = -1;
//Tracev((stderr, "inflate: reset\n"));
return Z_OK;
}
function inflateReset(strm) {
var state;
if (!strm || !strm.state) { return Z_STREAM_ERROR; }
state = strm.state;
state.wsize = 0;
state.whave = 0;
state.wnext = 0;
return inflateResetKeep(strm);
}
function inflateReset2(strm, windowBits) {
var wrap;
var state;
/* get the state */
if (!strm || !strm.state) { return Z_STREAM_ERROR; }
state = strm.state;
/* extract wrap request from windowBits parameter */
if (windowBits < 0) {
wrap = 0;
windowBits = -windowBits;
}
else {
wrap = (windowBits >> 4) + 1;
if (windowBits < 48) {
windowBits &= 15;
}
}
/* set number of window bits, free window if different */
if (windowBits && (windowBits < 8 || windowBits > 15)) {
return Z_STREAM_ERROR;
}
if (state.window !== null && state.wbits !== windowBits) {
state.window = null;
}
/* update state and reset the rest of it */
state.wrap = wrap;
state.wbits = windowBits;
return inflateReset(strm);
}
function inflateInit2(strm, windowBits) {
var ret;
var state;
if (!strm) { return Z_STREAM_ERROR; }
//strm.msg = Z_NULL; /* in case we return an error */
state = new InflateState();
//if (state === Z_NULL) return Z_MEM_ERROR;
//Tracev((stderr, "inflate: allocated\n"));
strm.state = state;
state.window = null/*Z_NULL*/;
ret = inflateReset2(strm, windowBits);
if (ret !== Z_OK) {
strm.state = null/*Z_NULL*/;
}
return ret;
}
function inflateInit(strm) {
return inflateInit2(strm, DEF_WBITS);
}
/*
Return state with length and distance decoding tables and index sizes set to
fixed code decoding. Normally this returns fixed tables from inffixed.h.
If BUILDFIXED is defined, then instead this routine builds the tables the
first time it's called, and returns those tables the first time and
thereafter. This reduces the size of the code by about 2K bytes, in
exchange for a little execution time. However, BUILDFIXED should not be
used for threaded applications, since the rewriting of the tables and virgin
may not be thread-safe.
*/
var virgin = true;
var lenfix, distfix; // We have no pointers in JS, so keep tables separate
function fixedtables(state) {
/* build fixed huffman tables if first call (may not be thread safe) */
if (virgin) {
var sym;
lenfix = new utils.Buf32(512);
distfix = new utils.Buf32(32);
/* literal/length table */
sym = 0;
while (sym < 144) { state.lens[sym++] = 8; }
while (sym < 256) { state.lens[sym++] = 9; }
while (sym < 280) { state.lens[sym++] = 7; }
while (sym < 288) { state.lens[sym++] = 8; }
inflate_table(LENS, state.lens, 0, 288, lenfix, 0, state.work, {bits: 9});
/* distance table */
sym = 0;
while (sym < 32) { state.lens[sym++] = 5; }
inflate_table(DISTS, state.lens, 0, 32, distfix, 0, state.work, {bits: 5});
/* do this just once */
virgin = false;
}
state.lencode = lenfix;
state.lenbits = 9;
state.distcode = distfix;
state.distbits = 5;
}
/*
Update the window with the last wsize (normally 32K) bytes written before
returning. If window does not exist yet, create it. This is only called
when a window is already in use, or when output has been written during this
inflate call, but the end of the deflate stream has not been reached yet.
It is also called to create a window for dictionary data when a dictionary
is loaded.
Providing output buffers larger than 32K to inflate() should provide a speed
advantage, since only the last 32K of output is copied to the sliding window
upon return from inflate(), and since all distances after the first 32K of
output will fall in the output data, making match copies simpler and faster.
The advantage may be dependent on the size of the processor's data caches.
*/
function updatewindow(strm, src, end, copy) {
var dist;
var state = strm.state;
/* if it hasn't been done already, allocate space for the window */
if (state.window === null) {
state.wsize = 1 << state.wbits;
state.wnext = 0;
state.whave = 0;
state.window = new utils.Buf8(state.wsize);
}
/* copy state->wsize or less output bytes into the circular window */
if (copy >= state.wsize) {
utils.arraySet(state.window,src, end - state.wsize, state.wsize, 0);
state.wnext = 0;
state.whave = state.wsize;
}
else {
dist = state.wsize - state.wnext;
if (dist > copy) {
dist = copy;
}
//zmemcpy(state->window + state->wnext, end - copy, dist);
utils.arraySet(state.window,src, end - copy, dist, state.wnext);
copy -= dist;
if (copy) {
//zmemcpy(state->window, end - copy, copy);
utils.arraySet(state.window,src, end - copy, copy, 0);
state.wnext = copy;
state.whave = state.wsize;
}
else {
state.wnext += dist;
if (state.wnext === state.wsize) { state.wnext = 0; }
if (state.whave < state.wsize) { state.whave += dist; }
}
}
return 0;
}
function inflate(strm, flush) {
var state;
var input, output; // input/output buffers
var next; /* next input INDEX */
var put; /* next output INDEX */
var have, left; /* available input and output */
var hold; /* bit buffer */
var bits; /* bits in bit buffer */
var _in, _out; /* save starting available input and output */
var copy; /* number of stored or match bytes to copy */
var from; /* where to copy match bytes from */
var from_source;
var here = 0; /* current decoding table entry */
var here_bits, here_op, here_val; // paked "here" denormalized (JS specific)
//var last; /* parent table entry */
var last_bits, last_op, last_val; // paked "last" denormalized (JS specific)
var len; /* length to copy for repeats, bits to drop */
var ret; /* return code */
var hbuf = new utils.Buf8(4); /* buffer for gzip header crc calculation */
var opts;
var n; // temporary var for NEED_BITS
var order = /* permutation of code lengths */
[16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15];
if (!strm || !strm.state || !strm.output ||
(!strm.input && strm.avail_in !== 0)) {
return Z_STREAM_ERROR;
}
state = strm.state;
if (state.mode === TYPE) { state.mode = TYPEDO; } /* skip check */
//--- LOAD() ---
put = strm.next_out;
output = strm.output;
left = strm.avail_out;
next = strm.next_in;
input = strm.input;
have = strm.avail_in;
hold = state.hold;
bits = state.bits;
//---
_in = have;
_out = left;
ret = Z_OK;
inf_leave: // goto emulation
for (;;) {
switch (state.mode) {
case HEAD:
if (state.wrap === 0) {
state.mode = TYPEDO;
break;
}
//=== NEEDBITS(16);
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if ((state.wrap & 2) && hold === 0x8b1f) { /* gzip header */
state.check = 0/*crc32(0L, Z_NULL, 0)*/;
//=== CRC2(state.check, hold);
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
state.check = crc32(state.check, hbuf, 2, 0);
//===//
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = FLAGS;
break;
}
state.flags = 0; /* expect zlib header */
if (state.head) {
state.head.done = false;
}
if (!(state.wrap & 1) || /* check if zlib header allowed */
(((hold & 0xff)/*BITS(8)*/ << 8) + (hold >> 8)) % 31) {
strm.msg = 'incorrect header check';
state.mode = BAD;
break;
}
if ((hold & 0x0f)/*BITS(4)*/ !== Z_DEFLATED) {
strm.msg = 'unknown compression method';
state.mode = BAD;
break;
}
//--- DROPBITS(4) ---//
hold >>>= 4;
bits -= 4;
//---//
len = (hold & 0x0f)/*BITS(4)*/ + 8;
if (state.wbits === 0) {
state.wbits = len;
}
else if (len > state.wbits) {
strm.msg = 'invalid window size';
state.mode = BAD;
break;
}
state.dmax = 1 << len;
//Tracev((stderr, "inflate: zlib header ok\n"));
strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
state.mode = hold & 0x200 ? DICTID : TYPE;
//=== INITBITS();
hold = 0;
bits = 0;
//===//
break;
case FLAGS:
//=== NEEDBITS(16); */
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.flags = hold;
if ((state.flags & 0xff) !== Z_DEFLATED) {
strm.msg = 'unknown compression method';
state.mode = BAD;
break;
}
if (state.flags & 0xe000) {
strm.msg = 'unknown header flags set';
state.mode = BAD;
break;
}
if (state.head) {
state.head.text = ((hold >> 8) & 1);
}
if (state.flags & 0x0200) {
//=== CRC2(state.check, hold);
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
state.check = crc32(state.check, hbuf, 2, 0);
//===//
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = TIME;
/* falls through */
case TIME:
//=== NEEDBITS(32); */
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if (state.head) {
state.head.time = hold;
}
if (state.flags & 0x0200) {
//=== CRC4(state.check, hold)
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
hbuf[2] = (hold >>> 16) & 0xff;
hbuf[3] = (hold >>> 24) & 0xff;
state.check = crc32(state.check, hbuf, 4, 0);
//===
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = OS;
/* falls through */
case OS:
//=== NEEDBITS(16); */
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if (state.head) {
state.head.xflags = (hold & 0xff);
state.head.os = (hold >> 8);
}
if (state.flags & 0x0200) {
//=== CRC2(state.check, hold);
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
state.check = crc32(state.check, hbuf, 2, 0);
//===//
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = EXLEN;
/* falls through */
case EXLEN:
if (state.flags & 0x0400) {
//=== NEEDBITS(16); */
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.length = hold;
if (state.head) {
state.head.extra_len = hold;
}
if (state.flags & 0x0200) {
//=== CRC2(state.check, hold);
hbuf[0] = hold & 0xff;
hbuf[1] = (hold >>> 8) & 0xff;
state.check = crc32(state.check, hbuf, 2, 0);
//===//
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
}
else if (state.head) {
state.head.extra = null/*Z_NULL*/;
}
state.mode = EXTRA;
/* falls through */
case EXTRA:
if (state.flags & 0x0400) {
copy = state.length;
if (copy > have) { copy = have; }
if (copy) {
if (state.head) {
len = state.head.extra_len - state.length;
if (!state.head.extra) {
// Use untyped array for more conveniend processing later
state.head.extra = new Array(state.head.extra_len);
}
utils.arraySet(
state.head.extra,
input,
next,
// extra field is limited to 65536 bytes
// - no need for additional size check
copy,
/*len + copy > state.head.extra_max - len ? state.head.extra_max : copy,*/
len
);
//zmemcpy(state.head.extra + len, next,
// len + copy > state.head.extra_max ?
// state.head.extra_max - len : copy);
}
if (state.flags & 0x0200) {
state.check = crc32(state.check, input, copy, next);
}
have -= copy;
next += copy;
state.length -= copy;
}
if (state.length) { break inf_leave; }
}
state.length = 0;
state.mode = NAME;
/* falls through */
case NAME:
if (state.flags & 0x0800) {
if (have === 0) { break inf_leave; }
copy = 0;
do {
// TODO: 2 or 1 bytes?
len = input[next + copy++];
/* use constant limit because in js we should not preallocate memory */
if (state.head && len &&
(state.length < 65536 /*state.head.name_max*/)) {
state.head.name += String.fromCharCode(len);
}
} while (len && copy < have);
if (state.flags & 0x0200) {
state.check = crc32(state.check, input, copy, next);
}
have -= copy;
next += copy;
if (len) { break inf_leave; }
}
else if (state.head) {
state.head.name = null;
}
state.length = 0;
state.mode = COMMENT;
/* falls through */
case COMMENT:
if (state.flags & 0x1000) {
if (have === 0) { break inf_leave; }
copy = 0;
do {
len = input[next + copy++];
/* use constant limit because in js we should not preallocate memory */
if (state.head && len &&
(state.length < 65536 /*state.head.comm_max*/)) {
state.head.comment += String.fromCharCode(len);
}
} while (len && copy < have);
if (state.flags & 0x0200) {
state.check = crc32(state.check, input, copy, next);
}
have -= copy;
next += copy;
if (len) { break inf_leave; }
}
else if (state.head) {
state.head.comment = null;
}
state.mode = HCRC;
/* falls through */
case HCRC:
if (state.flags & 0x0200) {
//=== NEEDBITS(16); */
while (bits < 16) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if (hold !== (state.check & 0xffff)) {
strm.msg = 'header crc mismatch';
state.mode = BAD;
break;
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
}
if (state.head) {
state.head.hcrc = ((state.flags >> 9) & 1);
state.head.done = true;
}
strm.adler = state.check = 0 /*crc32(0L, Z_NULL, 0)*/;
state.mode = TYPE;
break;
case DICTID:
//=== NEEDBITS(32); */
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
strm.adler = state.check = ZSWAP32(hold);
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = DICT;
/* falls through */
case DICT:
if (state.havedict === 0) {
//--- RESTORE() ---
strm.next_out = put;
strm.avail_out = left;
strm.next_in = next;
strm.avail_in = have;
state.hold = hold;
state.bits = bits;
//---
return Z_NEED_DICT;
}
strm.adler = state.check = 1/*adler32(0L, Z_NULL, 0)*/;
state.mode = TYPE;
/* falls through */
case TYPE:
if (flush === Z_BLOCK || flush === Z_TREES) { break inf_leave; }
/* falls through */
case TYPEDO:
if (state.last) {
//--- BYTEBITS() ---//
hold >>>= bits & 7;
bits -= bits & 7;
//---//
state.mode = CHECK;
break;
}
//=== NEEDBITS(3); */
while (bits < 3) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.last = (hold & 0x01)/*BITS(1)*/;
//--- DROPBITS(1) ---//
hold >>>= 1;
bits -= 1;
//---//
switch ((hold & 0x03)/*BITS(2)*/) {
case 0: /* stored block */
//Tracev((stderr, "inflate: stored block%s\n",
// state.last ? " (last)" : ""));
state.mode = STORED;
break;
case 1: /* fixed block */
fixedtables(state);
//Tracev((stderr, "inflate: fixed codes block%s\n",
// state.last ? " (last)" : ""));
state.mode = LEN_; /* decode codes */
if (flush === Z_TREES) {
//--- DROPBITS(2) ---//
hold >>>= 2;
bits -= 2;
//---//
break inf_leave;
}
break;
case 2: /* dynamic block */
//Tracev((stderr, "inflate: dynamic codes block%s\n",
// state.last ? " (last)" : ""));
state.mode = TABLE;
break;
case 3:
strm.msg = 'invalid block type';
state.mode = BAD;
}
//--- DROPBITS(2) ---//
hold >>>= 2;
bits -= 2;
//---//
break;
case STORED:
//--- BYTEBITS() ---// /* go to byte boundary */
hold >>>= bits & 7;
bits -= bits & 7;
//---//
//=== NEEDBITS(32); */
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if ((hold & 0xffff) !== ((hold >>> 16) ^ 0xffff)) {
strm.msg = 'invalid stored block lengths';
state.mode = BAD;
break;
}
state.length = hold & 0xffff;
//Tracev((stderr, "inflate: stored length %u\n",
// state.length));
//=== INITBITS();
hold = 0;
bits = 0;
//===//
state.mode = COPY_;
if (flush === Z_TREES) { break inf_leave; }
/* falls through */
case COPY_:
state.mode = COPY;
/* falls through */
case COPY:
copy = state.length;
if (copy) {
if (copy > have) { copy = have; }
if (copy > left) { copy = left; }
if (copy === 0) { break inf_leave; }
//--- zmemcpy(put, next, copy); ---
utils.arraySet(output, input, next, copy, put);
//---//
have -= copy;
next += copy;
left -= copy;
put += copy;
state.length -= copy;
break;
}
//Tracev((stderr, "inflate: stored end\n"));
state.mode = TYPE;
break;
case TABLE:
//=== NEEDBITS(14); */
while (bits < 14) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.nlen = (hold & 0x1f)/*BITS(5)*/ + 257;
//--- DROPBITS(5) ---//
hold >>>= 5;
bits -= 5;
//---//
state.ndist = (hold & 0x1f)/*BITS(5)*/ + 1;
//--- DROPBITS(5) ---//
hold >>>= 5;
bits -= 5;
//---//
state.ncode = (hold & 0x0f)/*BITS(4)*/ + 4;
//--- DROPBITS(4) ---//
hold >>>= 4;
bits -= 4;
//---//
//#ifndef PKZIP_BUG_WORKAROUND
if (state.nlen > 286 || state.ndist > 30) {
strm.msg = 'too many length or distance symbols';
state.mode = BAD;
break;
}
//#endif
//Tracev((stderr, "inflate: table sizes ok\n"));
state.have = 0;
state.mode = LENLENS;
/* falls through */
case LENLENS:
while (state.have < state.ncode) {
//=== NEEDBITS(3);
while (bits < 3) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.lens[order[state.have++]] = (hold & 0x07);//BITS(3);
//--- DROPBITS(3) ---//
hold >>>= 3;
bits -= 3;
//---//
}
while (state.have < 19) {
state.lens[order[state.have++]] = 0;
}
// We have separate tables & no pointers. 2 commented lines below not needed.
//state.next = state.codes;
//state.lencode = state.next;
// Switch to use dynamic table
state.lencode = state.lendyn;
state.lenbits = 7;
opts = {bits: state.lenbits};
ret = inflate_table(CODES, state.lens, 0, 19, state.lencode, 0, state.work, opts);
state.lenbits = opts.bits;
if (ret) {
strm.msg = 'invalid code lengths set';
state.mode = BAD;
break;
}
//Tracev((stderr, "inflate: code lengths ok\n"));
state.have = 0;
state.mode = CODELENS;
/* falls through */
case CODELENS:
while (state.have < state.nlen + state.ndist) {
for (;;) {
here = state.lencode[hold & ((1 << state.lenbits) - 1)];/*BITS(state.lenbits)*/
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if ((here_bits) <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
if (here_val < 16) {
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
state.lens[state.have++] = here_val;
}
else {
if (here_val === 16) {
//=== NEEDBITS(here.bits + 2);
n = here_bits + 2;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
if (state.have === 0) {
strm.msg = 'invalid bit length repeat';
state.mode = BAD;
break;
}
len = state.lens[state.have - 1];
copy = 3 + (hold & 0x03);//BITS(2);
//--- DROPBITS(2) ---//
hold >>>= 2;
bits -= 2;
//---//
}
else if (here_val === 17) {
//=== NEEDBITS(here.bits + 3);
n = here_bits + 3;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
len = 0;
copy = 3 + (hold & 0x07);//BITS(3);
//--- DROPBITS(3) ---//
hold >>>= 3;
bits -= 3;
//---//
}
else {
//=== NEEDBITS(here.bits + 7);
n = here_bits + 7;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
len = 0;
copy = 11 + (hold & 0x7f);//BITS(7);
//--- DROPBITS(7) ---//
hold >>>= 7;
bits -= 7;
//---//
}
if (state.have + copy > state.nlen + state.ndist) {
strm.msg = 'invalid bit length repeat';
state.mode = BAD;
break;
}
while (copy--) {
state.lens[state.have++] = len;
}
}
}
/* handle error breaks in while */
if (state.mode === BAD) { break; }
/* check for end-of-block code (better have one) */
if (state.lens[256] === 0) {
strm.msg = 'invalid code -- missing end-of-block';
state.mode = BAD;
break;
}
/* build code tables -- note: do not change the lenbits or distbits
values here (9 and 6) without reading the comments in inftrees.h
concerning the ENOUGH constants, which depend on those values */
state.lenbits = 9;
opts = {bits: state.lenbits};
ret = inflate_table(LENS, state.lens, 0, state.nlen, state.lencode, 0, state.work, opts);
// We have separate tables & no pointers. 2 commented lines below not needed.
// state.next_index = opts.table_index;
state.lenbits = opts.bits;
// state.lencode = state.next;
if (ret) {
strm.msg = 'invalid literal/lengths set';
state.mode = BAD;
break;
}
state.distbits = 6;
//state.distcode.copy(state.codes);
// Switch to use dynamic table
state.distcode = state.distdyn;
opts = {bits: state.distbits};
ret = inflate_table(DISTS, state.lens, state.nlen, state.ndist, state.distcode, 0, state.work, opts);
// We have separate tables & no pointers. 2 commented lines below not needed.
// state.next_index = opts.table_index;
state.distbits = opts.bits;
// state.distcode = state.next;
if (ret) {
strm.msg = 'invalid distances set';
state.mode = BAD;
break;
}
//Tracev((stderr, 'inflate: codes ok\n'));
state.mode = LEN_;
if (flush === Z_TREES) { break inf_leave; }
/* falls through */
case LEN_:
state.mode = LEN;
/* falls through */
case LEN:
if (have >= 6 && left >= 258) {
//--- RESTORE() ---
strm.next_out = put;
strm.avail_out = left;
strm.next_in = next;
strm.avail_in = have;
state.hold = hold;
state.bits = bits;
//---
inflate_fast(strm, _out);
//--- LOAD() ---
put = strm.next_out;
output = strm.output;
left = strm.avail_out;
next = strm.next_in;
input = strm.input;
have = strm.avail_in;
hold = state.hold;
bits = state.bits;
//---
if (state.mode === TYPE) {
state.back = -1;
}
break;
}
state.back = 0;
for (;;) {
here = state.lencode[hold & ((1 << state.lenbits) -1)]; /*BITS(state.lenbits)*/
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if (here_bits <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
if (here_op && (here_op & 0xf0) === 0) {
last_bits = here_bits;
last_op = here_op;
last_val = here_val;
for (;;) {
here = state.lencode[last_val +
((hold & ((1 << (last_bits + last_op)) -1))/*BITS(last.bits + last.op)*/ >> last_bits)];
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if ((last_bits + here_bits) <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
//--- DROPBITS(last.bits) ---//
hold >>>= last_bits;
bits -= last_bits;
//---//
state.back += last_bits;
}
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
state.back += here_bits;
state.length = here_val;
if (here_op === 0) {
//Tracevv((stderr, here.val >= 0x20 && here.val < 0x7f ?
// "inflate: literal '%c'\n" :
// "inflate: literal 0x%02x\n", here.val));
state.mode = LIT;
break;
}
if (here_op & 32) {
//Tracevv((stderr, "inflate: end of block\n"));
state.back = -1;
state.mode = TYPE;
break;
}
if (here_op & 64) {
strm.msg = 'invalid literal/length code';
state.mode = BAD;
break;
}
state.extra = here_op & 15;
state.mode = LENEXT;
/* falls through */
case LENEXT:
if (state.extra) {
//=== NEEDBITS(state.extra);
n = state.extra;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.length += hold & ((1 << state.extra) -1)/*BITS(state.extra)*/;
//--- DROPBITS(state.extra) ---//
hold >>>= state.extra;
bits -= state.extra;
//---//
state.back += state.extra;
}
//Tracevv((stderr, "inflate: length %u\n", state.length));
state.was = state.length;
state.mode = DIST;
/* falls through */
case DIST:
for (;;) {
here = state.distcode[hold & ((1 << state.distbits) -1)];/*BITS(state.distbits)*/
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if ((here_bits) <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
if ((here_op & 0xf0) === 0) {
last_bits = here_bits;
last_op = here_op;
last_val = here_val;
for (;;) {
here = state.distcode[last_val +
((hold & ((1 << (last_bits + last_op)) -1))/*BITS(last.bits + last.op)*/ >> last_bits)];
here_bits = here >>> 24;
here_op = (here >>> 16) & 0xff;
here_val = here & 0xffff;
if ((last_bits + here_bits) <= bits) { break; }
//--- PULLBYTE() ---//
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
//---//
}
//--- DROPBITS(last.bits) ---//
hold >>>= last_bits;
bits -= last_bits;
//---//
state.back += last_bits;
}
//--- DROPBITS(here.bits) ---//
hold >>>= here_bits;
bits -= here_bits;
//---//
state.back += here_bits;
if (here_op & 64) {
strm.msg = 'invalid distance code';
state.mode = BAD;
break;
}
state.offset = here_val;
state.extra = (here_op) & 15;
state.mode = DISTEXT;
/* falls through */
case DISTEXT:
if (state.extra) {
//=== NEEDBITS(state.extra);
n = state.extra;
while (bits < n) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
state.offset += hold & ((1 << state.extra) -1)/*BITS(state.extra)*/;
//--- DROPBITS(state.extra) ---//
hold >>>= state.extra;
bits -= state.extra;
//---//
state.back += state.extra;
}
//#ifdef INFLATE_STRICT
if (state.offset > state.dmax) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break;
}
//#endif
//Tracevv((stderr, "inflate: distance %u\n", state.offset));
state.mode = MATCH;
/* falls through */
case MATCH:
if (left === 0) { break inf_leave; }
copy = _out - left;
if (state.offset > copy) { /* copy from window */
copy = state.offset - copy;
if (copy > state.whave) {
if (state.sane) {
strm.msg = 'invalid distance too far back';
state.mode = BAD;
break;
}
// (!) This block is disabled in zlib defailts,
// don't enable it for binary compatibility
//#ifdef INFLATE_ALLOW_INVALID_DISTANCE_TOOFAR_ARRR
// Trace((stderr, "inflate.c too far\n"));
// copy -= state.whave;
// if (copy > state.length) { copy = state.length; }
// if (copy > left) { copy = left; }
// left -= copy;
// state.length -= copy;
// do {
// output[put++] = 0;
// } while (--copy);
// if (state.length === 0) { state.mode = LEN; }
// break;
//#endif
}
if (copy > state.wnext) {
copy -= state.wnext;
from = state.wsize - copy;
}
else {
from = state.wnext - copy;
}
if (copy > state.length) { copy = state.length; }
from_source = state.window;
}
else { /* copy from output */
from_source = output;
from = put - state.offset;
copy = state.length;
}
if (copy > left) { copy = left; }
left -= copy;
state.length -= copy;
do {
output[put++] = from_source[from++];
} while (--copy);
if (state.length === 0) { state.mode = LEN; }
break;
case LIT:
if (left === 0) { break inf_leave; }
output[put++] = state.length;
left--;
state.mode = LEN;
break;
case CHECK:
if (state.wrap) {
//=== NEEDBITS(32);
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
// Use '|' insdead of '+' to make sure that result is signed
hold |= input[next++] << bits;
bits += 8;
}
//===//
_out -= left;
strm.total_out += _out;
state.total += _out;
if (_out) {
strm.adler = state.check =
/*UPDATE(state.check, put - _out, _out);*/
(state.flags ? crc32(state.check, output, _out, put - _out) : adler32(state.check, output, _out, put - _out));
}
_out = left;
// NB: crc32 stored as signed 32-bit int, ZSWAP32 returns signed too
if ((state.flags ? hold : ZSWAP32(hold)) !== state.check) {
strm.msg = 'incorrect data check';
state.mode = BAD;
break;
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
//Tracev((stderr, "inflate: check matches trailer\n"));
}
state.mode = LENGTH;
/* falls through */
case LENGTH:
if (state.wrap && state.flags) {
//=== NEEDBITS(32);
while (bits < 32) {
if (have === 0) { break inf_leave; }
have--;
hold += input[next++] << bits;
bits += 8;
}
//===//
if (hold !== (state.total & 0xffffffff)) {
strm.msg = 'incorrect length check';
state.mode = BAD;
break;
}
//=== INITBITS();
hold = 0;
bits = 0;
//===//
//Tracev((stderr, "inflate: length matches trailer\n"));
}
state.mode = DONE;
/* falls through */
case DONE:
ret = Z_STREAM_END;
break inf_leave;
case BAD:
ret = Z_DATA_ERROR;
break inf_leave;
case MEM:
return Z_MEM_ERROR;
case SYNC:
/* falls through */
default:
return Z
gitextract_on4keuxv/ ├── .gitattributes ├── .gitignore ├── LICENSE.txt ├── README.md ├── app/ │ ├── Dockerfile │ ├── browser_app.py │ ├── config.yaml │ ├── main.py │ ├── requirements.txt │ ├── run_browser │ ├── static/ │ │ ├── dropdown.css │ │ ├── main.css │ │ ├── main.js │ │ ├── normalize.css │ │ ├── novnc/ │ │ │ ├── base.css │ │ │ ├── base64.js │ │ │ ├── black.css │ │ │ ├── blue.css │ │ │ ├── des.js │ │ │ ├── display.js │ │ │ ├── inflator.js │ │ │ ├── input.js │ │ │ ├── jsunzip.js │ │ │ ├── keyboard.js │ │ │ ├── keysym.js │ │ │ ├── keysymdef.js │ │ │ ├── logo.js │ │ │ ├── playback.js │ │ │ ├── rfb.js │ │ │ ├── ui.js │ │ │ ├── util.js │ │ │ ├── websock.js │ │ │ └── webutil.js │ │ ├── shared.js │ │ ├── skeleton.css │ │ └── timemap.js │ ├── templates/ │ │ ├── archives-list.html │ │ ├── browser-select.html │ │ ├── index.html │ │ ├── replay.html │ │ └── tracking.html │ └── uwsgi.ini ├── archives.yaml ├── browsers/ │ ├── base-basilisk2-browser/ │ │ ├── Dockerfile │ │ ├── basilisk_ii_prefs │ │ ├── performa.rom │ │ └── quadra650.rom │ ├── base-browser/ │ │ ├── Dockerfile │ │ ├── entry_point.sh │ │ └── requirements.txt │ ├── base-chromium/ │ │ ├── Dockerfile │ │ ├── jwmrc │ │ └── run.sh │ ├── base-sheepshaver/ │ │ ├── Dockerfile │ │ ├── NetscapePreferences │ │ ├── SheepShaver │ │ ├── newworld86.rom │ │ ├── oldworld.rom │ │ ├── run.sh │ │ └── sheepshaver_prefs │ ├── base-wine-browser/ │ │ ├── Dockerfile │ │ └── proxy.reg │ ├── build-browsers.sh │ ├── build-me.sh │ ├── chrome/ │ │ ├── Dockerfile │ │ ├── jwmrc │ │ └── run.sh │ ├── chromium10/ │ │ └── Dockerfile │ ├── chromium5/ │ │ └── Dockerfile │ ├── firefox/ │ │ ├── Dockerfile │ │ ├── ffprofile/ │ │ │ └── user.js │ │ ├── jwmrc │ │ └── run.sh │ ├── ie4/ │ │ ├── Dockerfile │ │ └── run.sh │ ├── ie4.01-mac/ │ │ ├── Dockerfile │ │ └── run.sh │ ├── ie5.1-mac/ │ │ └── Dockerfile │ ├── ie5.5/ │ │ ├── Dockerfile │ │ ├── proxy.reg │ │ └── run.sh │ ├── lynx/ │ │ ├── Dockerfile │ │ └── run.sh │ ├── mosaic/ │ │ ├── Dockerfile │ │ ├── fvwm2rc │ │ ├── proxy │ │ └── run.sh │ ├── netscape/ │ │ ├── Dockerfile │ │ ├── fvwm2rc │ │ ├── install.sh │ │ ├── preferences.js │ │ └── run.sh │ ├── netscape-mac-3.04/ │ │ ├── Dockerfile │ │ └── run.sh │ ├── netscape-mac-4.08/ │ │ ├── Dockerfile │ │ ├── NetscapePreferences │ │ └── run.sh │ ├── netscape4.8-mac/ │ │ └── Dockerfile │ ├── netscape4.8-win/ │ │ ├── Dockerfile │ │ ├── prefs.js │ │ └── run.sh │ ├── safari3/ │ │ ├── Dockerfile │ │ └── run.sh │ ├── safari5/ │ │ ├── Dockerfile │ │ └── run.sh │ └── www/ │ ├── Dockerfile │ ├── previous.cfg │ ├── proxy.py │ ├── run.sh │ └── tars.iso.dmg ├── docker-compose.yml ├── nginx/ │ ├── Dockerfile │ └── nginx.conf ├── pull-containers.sh ├── push-containers.sh ├── pywb/ │ ├── Dockerfile │ ├── archivereplayview.py │ ├── ca/ │ │ └── .gitignore │ ├── config.yaml │ ├── mementoquery.py │ ├── redisclient.py │ ├── requirements.txt │ ├── templates/ │ │ ├── blank.html │ │ ├── head_insert.html │ │ └── not_found.html │ └── uwsgi.ini └── run-local.sh
SYMBOL INDEX (141 symbols across 15 files)
FILE: app/browser_app.py
function set_timestamp (line 51) | def set_timestamp(timestamp):
function pingsock (line 79) | def pingsock(ws):
function receiver (line 131) | def receiver(ws):
function mark_for_removal (line 152) | def mark_for_removal():
function shutdown (line 168) | def shutdown():
function get_update (line 175) | def get_update():
function homepage (line 244) | def homepage():
function proxy (line 263) | def proxy():
function do_init (line 268) | def do_init():
function enable_cors (line 313) | def enable_cors():
FILE: app/main.py
class DockerController (line 25) | class DockerController(object):
method _load_config (line 26) | def _load_config(self):
method __init__ (line 31) | def __init__(self):
method _get_host_port (line 95) | def _get_host_port(self, info, port, default_host):
method timed_new_container (line 104) | def timed_new_container(self, browser, env, host, client_id):
method new_container (line 120) | def new_container(self, browser_id, env=None, default_host=None):
method remove_container (line 167) | def remove_container(self, short_id, ip=None):
method remove_expired (line 182) | def remove_expired(self):
method check_nodes (line 196) | def check_nodes(self):
method add_new_client (line 215) | def add_new_client(self):
method am_i_next (line 222) | def am_i_next(self, enc_id):
method throttle (line 265) | def throttle(self):
method do_init (line 292) | def do_init(self, browser, url, ts, host, client_id):
method get_randompage (line 306) | def get_randompage(self):
method get_random_browser (line 315) | def get_random_browser(self):
function server_static (line 328) | def server_static(filepath):
function init_container (line 333) | def init_container():
function index (line 353) | def index():
function route_load_url (line 359) | def route_load_url(path='', url='', ts=''):
function randompage (line 391) | def randompage():
function init_cleanup_timer (line 403) | def init_cleanup_timer(dc, expire_time):
FILE: app/static/main.js
function init_container (line 26) | function init_container() {
function do_init (line 80) | function do_init() {
function lose_focus (line 87) | function lose_focus() {
function grab_focus (line 93) | function grab_focus() {
function update_replay_state (line 106) | function update_replay_state() {
function establish_ping_sock (line 112) | function establish_ping_sock()
function format_date (line 139) | function format_date(date) {
function handle_data_update (line 143) | function handle_data_update(data) {
function set_time_left (line 209) | function set_time_left(time_left) {
function UIresize (line 225) | function UIresize() {
function FBUComplete (line 235) | function FBUComplete(rfb, fbu) {
function onVNCCopyCut (line 240) | function onVNCCopyCut(rfb, text)
function do_vnc (line 245) | function do_vnc() {
function updateState (line 280) | function updateState(rfb, state, oldstate, msg) {
function update_countdown (line 365) | function update_countdown() {
FILE: app/static/novnc/des.js
function DES (line 80) | function DES(passwd) {
FILE: app/static/novnc/inflator.js
function s (line 1) | function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&re...
function adler32 (line 112) | function adler32(adler, buf, len, pos) {
function makeTable (line 148) | function makeTable() {
function crc32 (line 166) | function crc32(crc, buf, len, pos) {
function ZSWAP32 (line 604) | function ZSWAP32(q) {
function InflateState (line 612) | function InflateState() {
function inflateResetKeep (line 670) | function inflateResetKeep(strm) {
function inflateReset (line 697) | function inflateReset(strm) {
function inflateReset2 (line 709) | function inflateReset2(strm, windowBits) {
function inflateInit2 (line 743) | function inflateInit2(strm, windowBits) {
function inflateInit (line 763) | function inflateInit(strm) {
function fixedtables (line 782) | function fixedtables(state) {
function updatewindow (line 830) | function updatewindow(strm, src, end, copy) {
function inflate (line 872) | function inflate(strm, flush) {
function inflateEnd (line 1964) | function inflateEnd(strm) {
function inflateGetHeader (line 1978) | function inflateGetHeader(strm, head) {
function ZStream (line 2347) | function ZStream() {
FILE: app/static/novnc/jsunzip.js
function JSUnzip (line 34) | function JSUnzip() {
function TINF (line 205) | function TINF() {
FILE: app/static/novnc/keyboard.js
function substituteCodepoint (line 4) | function substituteCodepoint(cp) {
function isMac (line 20) | function isMac() {
function isWindows (line 23) | function isWindows() {
function isLinux (line 26) | function isLinux() {
function hasShortcutModifier (line 31) | function hasShortcutModifier(charModifier, currentModifiers) {
function hasCharModifier (line 54) | function hasCharModifier(charModifier, currentModifiers) {
function ModifierSync (line 67) | function ModifierSync(charModifier) {
function getKey (line 153) | function getKey(evt){
function getKeysym (line 167) | function getKeysym(evt){
function keysymFromKeyCode (line 200) | function keysymFromKeyCode(keycode, shiftPressed) {
function nonCharacterKey (line 235) | function nonCharacterKey(evt) {
function KeyEventDecoder (line 295) | function KeyEventDecoder(modifierState, next) {
function VerifyCharModifier (line 382) | function VerifyCharModifier(next) {
function TrackKeyState (line 439) | function TrackKeyState(next) {
function EscapeModifiers (line 523) | function EscapeModifiers(next) {
FILE: app/static/novnc/keysymdef.js
function lookup (line 10) | function lookup(k) { return k ? {keysym: k, keyname: keynames ? keynames...
FILE: app/static/novnc/websock.js
function Websock (line 39) | function Websock() {
FILE: app/static/shared.js
function hide_menu (line 26) | function hide_menu()
function show_browser_menu (line 70) | function show_browser_menu()
function show_datetime_menu (line 90) | function show_datetime_menu()
function parse_ts (line 108) | function parse_ts(ts)
function set_ts (line 115) | function set_ts(ts)
function set_dt (line 142) | function set_dt(date)
function load_timemap (line 155) | function load_timemap(url) {
function init_sparkline (line 179) | function init_sparkline(data) {
FILE: app/static/timemap.js
function Sparkline (line 4) | function Sparkline(target, data, options)
FILE: browsers/www/proxy.py
function do_default (line 15) | def do_default():
function do_proxy (line 20) | def do_proxy(dt='', url=''):
FILE: pywb/archivereplayview.py
class ReplayHandler (line 29) | class ReplayHandler(WBHandler):
method _init_replay_view (line 30) | def _init_replay_view(self, config):
class MementoHandler (line 35) | class MementoHandler(ReplayHandler):
method _init_replay_view (line 36) | def _init_replay_view(self, config):
method handle_query (line 39) | def handle_query(self, wbrequest, cdx_lines, output):
class UpstreamArchiveLoader (line 67) | class UpstreamArchiveLoader(object):
method __init__ (line 68) | def __init__(self, config):
method unrewrite_header (line 79) | def unrewrite_header(self, response, name):
method _do_req (line 88) | def _do_req(self, urls, host, cdx, env, skip_hosts):
method __call__ (line 133) | def __call__(self, cdx, skip_hosts, cdx_loader, wbrequest):
method _get_urls_to_try (line 185) | def _get_urls_to_try(self, cdx, skip_hosts, wbrequest):
class MementoUpstreamArchiveLoader (line 198) | class MementoUpstreamArchiveLoader(UpstreamArchiveLoader):
method __init__ (line 199) | def __init__(self, config):
method load_archive_info_json (line 206) | def load_archive_info_json(self, url):
method load_archive_info_xml (line 237) | def load_archive_info_xml(self, url):
method find_archive_info (line 272) | def find_archive_info(self, uri):
method _get_urls_to_try (line 281) | def _get_urls_to_try(self, cdx, skip_hosts, wbrequest):
FILE: pywb/mementoquery.py
function datetime_to_secs (line 26) | def datetime_to_secs(dt):
class MementoJsonApi (line 35) | class MementoJsonApi(object):
method __init__ (line 36) | def __init__(self, paths):
method timegate_query (line 41) | def timegate_query(self, timestamp, url):
method timemap_query (line 59) | def timemap_query(self, url, closest='1'):
method parse_mem_value (line 88) | def parse_mem_value(self, m):
class MementoTimemapQuery (line 99) | class MementoTimemapQuery(object):
method __init__ (line 100) | def __init__(self, api_loader, url, closest='1'):
method __iter__ (line 105) | def __iter__(self):
method next (line 112) | def next(self):
class MementoClosestQuery (line 117) | class MementoClosestQuery(object):
method __init__ (line 122) | def __init__(self, api_loader, url, timestamp):
method _get_mem_info (line 128) | def _get_mem_info(self, mementos, name, closest_ts=None):
method __iter__ (line 142) | def __iter__(self):
method next (line 159) | def next(self):
method set_next_closest (line 180) | def set_next_closest(self, curr):
class MementoIndexServer (line 192) | class MementoIndexServer(object):
method __init__ (line 193) | def __init__(self, paths, **kwargs):
method load_cdx (line 197) | def load_cdx(self, **params):
method sort_archives (line 246) | def sort_archives(self, archive_list):
method memento_to_cdx (line 271) | def memento_to_cdx(self, url, mem_iter, limit, skip_exclude=True):
method cdx_to_text (line 323) | def cdx_to_text(self, cdx_list):
function test_memento_to_cdx (line 332) | def test_memento_to_cdx(url, mem):
function main (line 338) | def main():
FILE: pywb/redisclient.py
class RedisClient (line 9) | class RedisClient(object):
method __init__ (line 10) | def __init__(self):
method init_redis (line 13) | def init_redis(self, config={}):
method load_cdx_cache_iter (line 31) | def load_cdx_cache_iter(self, url, ts):
method save_cdx_cache_iter (line 47) | def save_cdx_cache_iter(self, cdx_list, url, ts):
method get_url_key_p (line 55) | def get_url_key_p(ts, url):
Condensed preview — 127 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (3,444K chars).
[
{
"path": ".gitattributes",
"chars": 45,
"preview": "*.tar.gz filter=lfs diff=lfs merge=lfs -text\n"
},
{
"path": ".gitignore",
"chars": 412,
"preview": "*.py[cod]\n\n# C extensions\n*.so\n\n# Packages\n*.egg\n*.egg-info\ndist\nbuild\neggs\n.eggs\nparts\nbin\nvar\nsdist\ndevelop-eggs\n.inst"
},
{
"path": "LICENSE.txt",
"chars": 894,
"preview": "Netcapsule is Copyright, 2015 Ilya Kreymer\n\nNetcapsule source code is subject to the terms of the Mozilla Public\nLicense"
},
{
"path": "README.md",
"chars": 10606,
"preview": "# OldWeb.today / Netcapsule #\n\n## Browse old websites the old way ##\n\n**This system is now deployed on http://oldweb.tod"
},
{
"path": "app/Dockerfile",
"chars": 271,
"preview": "FROM python:2.7\nMAINTAINER Ilya Kreymer <ikreymer at gmail.com>\n\nWORKDIR /app\n\nADD requirements.txt /app/\n\nRUN pip insta"
},
{
"path": "app/browser_app.py",
"chars": 7516,
"preview": "from gevent import monkey, spawn, Timeout, sleep\nmonkey.patch_all()\n\n\nfrom bottle import route, default_app, run, reques"
},
{
"path": "app/config.yaml",
"chars": 4421,
"preview": "api_version: '1.21'\nvnc_port: 6080\ncmd_port: 6082\n\nmax_containers: 100\n\ninit_container_expire_secs: 30\nfull_container_ex"
},
{
"path": "app/main.py",
"chars": 12809,
"preview": "from docker.client import Client\nfrom docker.utils import kwargs_from_env\n\nfrom bottle import route, run, template, requ"
},
{
"path": "app/requirements.txt",
"chars": 50,
"preview": "docker-py\nbottle\ngevent\nuwsgi\njinja2\nredis\npyyaml\n"
},
{
"path": "app/run_browser",
"chars": 53,
"preview": "#!/bin/bash\n\nwhile 'true'\ndo\n \"$@\"\n sleep 0.2\ndone\n"
},
{
"path": "app/static/dropdown.css",
"chars": 7318,
"preview": "/* dropdown menus */\n\n.dropdown {\n width: 303px;\n height: 58px;\n background-color: white;\n cursor: pointer;\n"
},
{
"path": "app/static/main.css",
"chars": 6242,
"preview": "html, body {\n height: 100%;\n margin: 0px;\n padding: 0px;\n border: 0px;\n background-color: #f0f0f0;\n fo"
},
{
"path": "app/static/main.js",
"chars": 12685,
"preview": "window.INCLUDE_URI = \"/static/novnc/\";\n\nvar cmd_host = undefined;\nvar vnc_host = undefined;\n\nvar connected = false;\nvar "
},
{
"path": "app/static/normalize.css",
"chars": 7797,
"preview": "/*! normalize.css v3.0.2 | MIT License | git.io/normalize */\n\n/**\n * 1. Set default font family to sans-serif.\n * 2. Pre"
},
{
"path": "app/static/novnc/base.css",
"chars": 11266,
"preview": "/*\n * noVNC base CSS\n * Copyright (C) 2012 Joel Martin\n * Copyright (C) 2013 Samuel Mannehed for Cendio AB\n * noVNC is l"
},
{
"path": "app/static/novnc/base64.js",
"chars": 4416,
"preview": "/* This Source Code Form is subject to the terms of the Mozilla Public\n * License, v. 2.0. If a copy of the MPL was not "
},
{
"path": "app/static/novnc/black.css",
"chars": 4613,
"preview": "/*\n * noVNC black CSS\n * Copyright (C) 2012 Joel Martin\n * Copyright (C) 2013 Samuel Mannehed for Cendio AB\n * noVNC is "
},
{
"path": "app/static/novnc/blue.css",
"chars": 1332,
"preview": "/*\n * noVNC blue CSS\n * Copyright (C) 2012 Joel Martin\n * Copyright (C) 2013 Samuel Mannehed for Cendio AB\n * noVNC is l"
},
{
"path": "app/static/novnc/des.js",
"chars": 11456,
"preview": "/*\n * Ported from Flashlight VNC ActionScript implementation:\n * http://www.wizhelp.com/flashlight-vnc/\n *\n * Full a"
},
{
"path": "app/static/novnc/display.js",
"chars": 33117,
"preview": "/*\n * noVNC: HTML5 VNC client\n * Copyright (C) 2012 Joel Martin\n * Copyright (C) 2015 Samuel Mannehed for Cendio AB\n * L"
},
{
"path": "app/static/novnc/inflator.js",
"chars": 76404,
"preview": "(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"func"
},
{
"path": "app/static/novnc/input.js",
"chars": 13944,
"preview": "/*\n * noVNC: HTML5 VNC client\n * Copyright (C) 2012 Joel Martin\n * Copyright (C) 2013 Samuel Mannehed for Cendio AB\n * L"
},
{
"path": "app/static/novnc/jsunzip.js",
"chars": 19367,
"preview": "/*\n * JSUnzip\n *\n * Copyright (c) 2011 by Erik Moller\n * All Rights Reserved\n *\n * This software is provided 'as-is', wi"
},
{
"path": "app/static/novnc/keyboard.js",
"chars": 20840,
"preview": "var kbdUtil = (function() {\n \"use strict\";\n\n function substituteCodepoint(cp) {\n // Any Unicode code points"
},
{
"path": "app/static/novnc/keysym.js",
"chars": 22527,
"preview": "var XK_VoidSymbol = 0xffffff, /* Void symbol */\n\nXK_BackSpace = 0xff08, /* Back space, "
},
{
"path": "app/static/novnc/keysymdef.js",
"chars": 21512,
"preview": "// This file describes mappings from Unicode codepoints to the keysym values\n// (and optionally, key names) expected by "
},
{
"path": "app/static/novnc/logo.js",
"chars": 27610,
"preview": "noVNC_logo = {\"width\": 640, \"height\": 435, \"data\": \"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAoAAAAGzCAYAAAC/y6a9AA"
},
{
"path": "app/static/novnc/playback.js",
"chars": 3204,
"preview": "/*\n * noVNC: HTML5 VNC client\n * Copyright (C) 2012 Joel Martin\n * Licensed under MPL 2.0 (see LICENSE.txt)\n */\n\n\"use st"
},
{
"path": "app/static/novnc/rfb.js",
"chars": 85309,
"preview": "/*\n * noVNC: HTML5 VNC client\n * Copyright (C) 2012 Joel Martin\n * Copyright (C) 2013 Samuel Mannehed for Cendio AB\n * L"
},
{
"path": "app/static/novnc/ui.js",
"chars": 46512,
"preview": "/*\n * noVNC: HTML5 VNC client\n * Copyright (C) 2012 Joel Martin\n * Copyright (C) 2015 Samuel Mannehed for Cendio AB\n * L"
},
{
"path": "app/static/novnc/util.js",
"chars": 20063,
"preview": "/*\n * noVNC: HTML5 VNC client\n * Copyright (C) 2012 Joel Martin\n * Licensed under MPL 2.0 (see LICENSE.txt)\n *\n * See RE"
},
{
"path": "app/static/novnc/websock.js",
"chars": 14301,
"preview": "/*\n * Websock: high-performance binary WebSockets\n * Copyright (C) 2012 Joel Martin\n * Licensed under MPL 2.0 (see LICEN"
},
{
"path": "app/static/novnc/webutil.js",
"chars": 6471,
"preview": "/*\n * noVNC: HTML5 VNC client\n * Copyright (C) 2012 Joel Martin\n * Copyright (C) 2013 NTT corp.\n * Licensed under MPL 2."
},
{
"path": "app/static/shared.js",
"chars": 5698,
"preview": "var sparkline = undefined;\nvar sparkline_url = undefined;\nvar sparkline_loading = false;\n\n\n// Dropdowns\n$(function() {\n\n"
},
{
"path": "app/static/skeleton.css",
"chars": 9952,
"preview": "/*\n* Skeleton V2.0.4\n* Copyright 2014, Dave Gamache\n* www.getskeleton.com\n* Free to use under the MIT license.\n* http://"
},
{
"path": "app/static/timemap.js",
"chars": 7128,
"preview": "\nvar current_date, requested_date;\n\nfunction Sparkline(target, data, options)\n{ \n // set up a date parsing functio"
},
{
"path": "app/templates/archives-list.html",
"chars": 1962,
"preview": "<ul id=\"statsHosts\">\n <li data-id=\"rhizome\">\n <a target=\"_blank\" href=\"http://webenact.rhizome.org/\">Rhizome W"
},
{
"path": "app/templates/browser-select.html",
"chars": 5342,
"preview": "<div id=\"browser-selector\" class=\"selector-menu\">\n <input type=\"hidden\" name=\"browser-input\" value=\"\">\n <table>\n "
},
{
"path": "app/templates/index.html",
"chars": 7182,
"preview": "<!doctype html>\n<head>\n <meta charset=\"utf-8\">\n <title>oldweb.today</title>\n {% include 'tracking.html' %}\n "
},
{
"path": "app/templates/replay.html",
"chars": 4668,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n <head>\n <title>oldweb.today</title>\n {% include 'tracking.html' %}\n "
},
{
"path": "app/templates/tracking.html",
"chars": 439,
"preview": " <script>\n (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){\n (i[r].q=i[r].q||[]"
},
{
"path": "app/uwsgi.ini",
"chars": 320,
"preview": "[uwsgi]\nif-not-env = PORT\nhttp-socket = :9020\nsocket = :9021\nendif =\n\nmaster = true\nbuffer-size = 65536\ndie-on-term = tr"
},
{
"path": "archives.yaml",
"chars": 3440,
"preview": "# Wayback Based\n- id: rhizome\n name: Rhizome Webenact\n timegate: http://webenact.rhizome.org/all/\n timemap: http://we"
},
{
"path": "browsers/base-basilisk2-browser/Dockerfile",
"chars": 342,
"preview": "FROM netcapsule/base-browser\n\nRUN sudo dpkg --add-architecture i386 \\\n && echo \"deb http://httpredir.debian.org/debia"
},
{
"path": "browsers/base-basilisk2-browser/basilisk_ii_prefs",
"chars": 487,
"preview": "disk /app/hd\nextfs /app/share/\nscreen win/1024/768\nseriala \nserialb \nether slirp\nudptunnel false\nudpport 6066\nrom /app/p"
},
{
"path": "browsers/base-browser/Dockerfile",
"chars": 1409,
"preview": "FROM debian:jessie\n\nENV TS 1996\nENV URL about:blank\n\nENV SCREEN_WIDTH 1360\nENV SCREEN_HEIGHT 1020\nENV SCREEN_DEPTH 16\nEN"
},
{
"path": "browsers/base-browser/entry_point.sh",
"chars": 1228,
"preview": "#!/bin/bash\nexport GEOMETRY=\"$SCREEN_WIDTH\"\"x\"\"$SCREEN_HEIGHT\"\"x\"\"$SCREEN_DEPTH\"\n\nmkdir -p ~/.vnc \nx11vnc -storepasswd s"
},
{
"path": "browsers/base-browser/requirements.txt",
"chars": 46,
"preview": "requests\nbottle\nredis\ngevent\nbottle-websocket\n"
},
{
"path": "browsers/base-chromium/Dockerfile",
"chars": 554,
"preview": "FROM netcapsule/base-browser\n\nRUN apt-get update && apt-get install -y libpango1.0-0 libfreetype6 libnss3-1d libnspr4-0d"
},
{
"path": "browsers/base-chromium/jwmrc",
"chars": 625,
"preview": "<?xml version=\"1.0\"?>\n<JWM>\n <Group>\n <Name>.*</Name>\n <Option>maximized</Option>\n <Option>border</Opti"
},
{
"path": "browsers/base-chromium/run.sh",
"chars": 595,
"preview": "#!/bin/bash\n\n#fluxbox -display $DISPLAY -log /tmp/fluxbox.log &\njwm -display $DISPLAY &\n\nhttp_proxy=\"http://netcapsule_p"
},
{
"path": "browsers/base-sheepshaver/Dockerfile",
"chars": 729,
"preview": "FROM netcapsule/base-browser\n\n# Install some tools required for creating the image\nRUN apt-get update \\\n && apt-get i"
},
{
"path": "browsers/base-sheepshaver/NetscapePreferences",
"chars": 599,
"preview": "// Netscape User Preferences\r// This is a generated file! Do not edit.\r\ruser_pref(\"browser.startup.agreed_to_licence\", "
},
{
"path": "browsers/base-sheepshaver/newworld86.rom",
"chars": 1153378,
"preview": "<CHRP-BOOT>\r<COMPATIBLE>\riMac,1 PowerMac1,1 PowerBook1,1\r</COMPATIBLE>\r<DESCRIPTION>\rMacROM for NewWorld.\r</DESCRIPTION>"
},
{
"path": "browsers/base-sheepshaver/run.sh",
"chars": 573,
"preview": "#!/bin/bash\n\nsudo chown browser:browser -R /app\n\nmkdir ./share\n\nPYWB_IP=$(grep netcapsule_pywb_1 /etc/hosts | cut -f 1 |"
},
{
"path": "browsers/base-sheepshaver/sheepshaver_prefs",
"chars": 452,
"preview": "screen win/1024/768\nwindowmodes 0\nscreenmodes 0\nseriala /dev/cu.Bluetooth-Modem\nserialb /dev/null\nbootdrive 0\nbootdriver"
},
{
"path": "browsers/base-wine-browser/Dockerfile",
"chars": 1234,
"preview": "FROM netcapsule/base-browser\n\n# Adapter from suchja/wine\nENV WINE_MONO_VERSION 0.0.8\nUSER root\n\n# Install some tools req"
},
{
"path": "browsers/base-wine-browser/proxy.reg",
"chars": 244,
"preview": "Regedit4\n\n[HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings]\n\"MigrateProxy\"=dword:00000001\n"
},
{
"path": "browsers/build-browsers.sh",
"chars": 239,
"preview": "#!/bin/bash\nDIR=$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pwd )\n\nset -e\n\nfor dir in $DIR/*/\ndo\n dir=${dir%*/}\n n"
},
{
"path": "browsers/build-me.sh",
"chars": 73,
"preview": "#!/bin/bash\n\nname=$(basename $PWD)\n\ndocker build -t \"netcapsule/$name\" .\n"
},
{
"path": "browsers/chrome/Dockerfile",
"chars": 534,
"preview": "FROM netcapsule/base-browser\n\nRUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add "
},
{
"path": "browsers/chrome/jwmrc",
"chars": 625,
"preview": "<?xml version=\"1.0\"?>\n<JWM>\n <Group>\n <Name>.*</Name>\n <Option>maximized</Option>\n <Option>border</Opti"
},
{
"path": "browsers/chrome/run.sh",
"chars": 555,
"preview": "#!/bin/bash\n\n#fluxbox -display $DISPLAY -log /tmp/fluxbox.log &\njwm -display $DISPLAY &\n\nhttp_proxy=\"http://netcapsule_p"
},
{
"path": "browsers/chromium10/Dockerfile",
"chars": 719,
"preview": "FROM netcapsule/base-chromium\n\n# To get a different version, use \"Position Lookup\" here: [https://omahaproxy.appspot.com"
},
{
"path": "browsers/chromium5/Dockerfile",
"chars": 719,
"preview": "FROM netcapsule/base-chromium\n\n# To get a different version, use \"Position Lookup\" here: [https://omahaproxy.appspot.com"
},
{
"path": "browsers/firefox/Dockerfile",
"chars": 670,
"preview": "FROM netcapsule/base-browser\n\nENV FF_VERSION 40.0.3\n\nRUN apt-get update && apt-get install -y \\\n libgtk2.0-0 libasoun"
},
{
"path": "browsers/firefox/ffprofile/user.js",
"chars": 648,
"preview": "user_pref(\"network.proxy.http\", \"netcapsule_pywb_1\");\nuser_pref(\"network.proxy.http_port\", 8080);\nuser_pref(\"network.pro"
},
{
"path": "browsers/firefox/jwmrc",
"chars": 595,
"preview": "<?xml version=\"1.0\"?>\n<JWM>\n <Group>\n <Name>.*</Name>\n <Option>maximized</Option>\n </Group>\n <WindowStyl"
},
{
"path": "browsers/firefox/run.sh",
"chars": 676,
"preview": "#!/bin/bash\n\n#fluxbox -display $DISPLAY -log /tmp/fluxbox.log &\njwm -display $DISPLAY &\n\nsudo chown browser:browser /hom"
},
{
"path": "browsers/ie4/Dockerfile",
"chars": 199,
"preview": "FROM netcapsule/base-wine-browser\n\nUSER browser\nWORKDIR /home/browser\n\nADD ie4wine.tar.gz /home/browser/\n\nCOPY run.sh /a"
},
{
"path": "browsers/ie4/run.sh",
"chars": 242,
"preview": "#!/bin/bash\nexport WINEPREFIX=\"/home/browser/ie4\"\n\nsed -i s/DIMENSION/$SCREEN_WIDTH\"x\"$SCREEN_HEIGHT/g /home/browser/ie4"
},
{
"path": "browsers/ie4.01-mac/Dockerfile",
"chars": 225,
"preview": "FROM netcapsule/base-basilisk2-browser\n\nWORKDIR /app\n\nCOPY run.sh /app/run.sh\nRUN sudo chmod a+x /app/run.sh\n\n# not actu"
},
{
"path": "browsers/ie4.01-mac/run.sh",
"chars": 351,
"preview": "#!/bin/bash\n\nsudo chown browser:browser -R /app\n\nmkdir /app/share\n\nPYWB_IP=$(grep netcapsule_pywb_1 /etc/hosts | cut -f "
},
{
"path": "browsers/ie5.1-mac/Dockerfile",
"chars": 103,
"preview": "FROM netcapsule/base-sheepshaver\n\nENV RUN_BROWSER ie5.1\n\nCMD /app/entry_point.sh /home/browser/run.sh\n\n"
},
{
"path": "browsers/ie5.5/Dockerfile",
"chars": 240,
"preview": "FROM netcapsule/base-wine-browser\n\nUSER browser\nWORKDIR /home/browser\n\nADD ie55wine.tar.gz /home/browser/\n\nCOPY proxy.re"
},
{
"path": "browsers/ie5.5/proxy.reg",
"chars": 251,
"preview": "Regedit4\n\n[HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings]\n\"MigrateProxy\"=dword:00000001\n"
},
{
"path": "browsers/ie5.5/run.sh",
"chars": 244,
"preview": "#!/bin/bash\n\nexport WINEPREFIX=\"/home/browser/ie55\"\n\nsed -i s/DIMENSION/$SCREEN_WIDTH\"x\"$SCREEN_HEIGHT/g /home/browser/i"
},
{
"path": "browsers/lynx/Dockerfile",
"chars": 232,
"preview": "FROM netcapsule/base-browser\n\nRUN apt-get update && apt-get install -y \\\n lynx-cur \\\n && rm -rf /var/lib/apt/lists"
},
{
"path": "browsers/lynx/run.sh",
"chars": 195,
"preview": "#!/bin/bash\n\n#fvwm -d $DISPLAY &\n\nsudo chmod a+x /usr/bin/xterm\n\nrun_browser xterm -maximized -w 0 -bd black -fg white -"
},
{
"path": "browsers/mosaic/Dockerfile",
"chars": 553,
"preview": "FROM netcapsule/base-browser\n\nRUN apt-get update && apt-get install -y \\\n build-essential libmotif-dev libjpeg62-turb"
},
{
"path": "browsers/mosaic/fvwm2rc",
"chars": 654,
"preview": "# default Styles:\n\nDestroyModuleConfig FE-StartMaximised: *\n*FE-StartMaximised: Cmd Function\n*FE-StartMaximised: add_win"
},
{
"path": "browsers/mosaic/proxy",
"chars": 33,
"preview": "http netcapsule_pywb_1 8080 http\n"
},
{
"path": "browsers/mosaic/run.sh",
"chars": 175,
"preview": "#!/bin/bash\n\nunset http_proxy\nunset https_proxy\n#fluxbox -display $DISPLAY -log /tmp/fluxbox.log &\nfvwm -d $DISPLAY &\n\nc"
},
{
"path": "browsers/netscape/Dockerfile",
"chars": 539,
"preview": "FROM netcapsule/base-browser\n\nRUN sudo dpkg --add-architecture i386\nRUN apt-get update && apt-get install -y \\\n libc6"
},
{
"path": "browsers/netscape/fvwm2rc",
"chars": 654,
"preview": "# default Styles:\n\nDestroyModuleConfig FE-StartMaximised: *\n*FE-StartMaximised: Cmd Function\n*FE-StartMaximised: add_win"
},
{
"path": "browsers/netscape/install.sh",
"chars": 1339,
"preview": "#!/bin/bash\n\nsudo chown browser:browser /download\ncd /download\n\nwget http://ftp.netscape.com/pub/communicator/english/4."
},
{
"path": "browsers/netscape/preferences.js",
"chars": 3423,
"preview": "// Netscape User Preferences\n// This is a generated file! Do not edit.\n\nuser_pref(\"bookmarks.outliner_geometry\", \"100x("
},
{
"path": "browsers/netscape/run.sh",
"chars": 659,
"preview": "#!/bin/bash\n\n#fluxbox -display $DISPLAY -log /tmp/fluxbox.log &\nfvwm -d $DISPLAY &\n\nexport XKEYSYMDB=XKeysymDB\nexport LD"
},
{
"path": "browsers/netscape-mac-3.04/Dockerfile",
"chars": 225,
"preview": "FROM netcapsule/base-basilisk2-browser\n\nWORKDIR /app\n\nCOPY run.sh /app/run.sh\nRUN sudo chmod a+x /app/run.sh\n\n# not actu"
},
{
"path": "browsers/netscape-mac-3.04/run.sh",
"chars": 215,
"preview": "#!/bin/bash\n\nsudo chown browser:browser -R /app\n\nmkdir /app/share\n\nPYWB_IP=$(grep netcapsule_pywb_1 /etc/hosts | cut -f "
},
{
"path": "browsers/netscape-mac-4.08/Dockerfile",
"chars": 257,
"preview": "FROM netcapsule/base-basilisk2-browser\n\nWORKDIR /app\n\nCOPY run.sh /app/run.sh\nRUN sudo chmod a+x /app/run.sh\n\nCOPY Netsc"
},
{
"path": "browsers/netscape-mac-4.08/NetscapePreferences",
"chars": 6255,
"preview": "// Netscape User Preferences\r// This is a generated file! Do not edit.\r\ruser_pref(\"browser.startup.agreed_to_licence\", "
},
{
"path": "browsers/netscape-mac-4.08/run.sh",
"chars": 469,
"preview": "#!/bin/bash\n\nsudo chown browser:browser -R /app\n\nmkdir /app/share\n\nPYWB_IP=$(grep netcapsule_pywb_1 /etc/hosts | cut -f "
},
{
"path": "browsers/netscape4.8-mac/Dockerfile",
"chars": 109,
"preview": "FROM netcapsule/base-sheepshaver\n\nENV RUN_BROWSER netscape4.8\n\nCMD /app/entry_point.sh /home/browser/run.sh\n\n"
},
{
"path": "browsers/netscape4.8-win/Dockerfile",
"chars": 234,
"preview": "FROM netcapsule/base-wine-browser\n\nUSER browser\nWORKDIR /home/browser\n\nADD ns48.tar.gz /home/browser/\n\nCOPY prefs.js /ho"
},
{
"path": "browsers/netscape4.8-win/prefs.js",
"chars": 410,
"preview": "user_pref(\"network.proxy.http\", \"netcapsule_pywb_1\");\nuser_pref(\"network.proxy.http_port\", 8080);\nuser_pref(\"network.pro"
},
{
"path": "browsers/netscape4.8-win/run.sh",
"chars": 581,
"preview": "#!/bin/bash\n\nexport WINEPREFIX=\"/home/browser/ns48\"\n\nPYWB_IP=$(grep netcapsule_pywb_1 /etc/hosts | cut -f 1 | head -n 1)"
},
{
"path": "browsers/safari3/Dockerfile",
"chars": 199,
"preview": "FROM netcapsule/base-wine-browser\n\nUSER browser\nWORKDIR /home/browser\n\nADD safari3.tar.gz /home/browser/\n\nCOPY run.sh /a"
},
{
"path": "browsers/safari3/run.sh",
"chars": 282,
"preview": "#!/bin/bash\n\nsudo chown -R browser /home/browser/safari3\n\nexport WINEPREFIX=\"/home/browser/safari3\"\n\nsed -i s/DIMENSION/"
},
{
"path": "browsers/safari5/Dockerfile",
"chars": 185,
"preview": "FROM netcapsule/base-wine-browser\n\nWORKDIR /home/browser\n\nADD safari.tar.gz /home/browser/\n\nCOPY run.sh /app/run.sh\nRUN "
},
{
"path": "browsers/safari5/run.sh",
"chars": 235,
"preview": "#!/bin/bash\n\nexport WINEPREFIX=\"/home/browser/safari\"\n\nsed -i s/DIMENSION/$SCREEN_WIDTH\"x\"$SCREEN_HEIGHT/g /home/browser"
},
{
"path": "browsers/www/Dockerfile",
"chars": 722,
"preview": "FROM netcapsule/base-browser\n\nRUN sudo dpkg --add-architecture i386 &&\\\n apt-get update && apt-get install -qqy subve"
},
{
"path": "browsers/www/previous.cfg",
"chars": 4012,
"preview": "[Log]\nsLogFileName = stderr\nsTraceFileName = stderr\nnTextLogLevel = 1\nnAlertDlgLogLevel = 1\nbConfirmQuit = TRUE\n\n[Config"
},
{
"path": "browsers/www/proxy.py",
"chars": 2111,
"preview": "from bottle import request, route, HTTPResponse, run\nfrom argparse import ArgumentParser\n\nimport requests\n\nproxies = 'lo"
},
{
"path": "browsers/www/run.sh",
"chars": 404,
"preview": "#!/bin/bash\n\ncd /home/browser/\n\nsudo chown browser NS33_2GB.dd\n\nsudo chown browser ./.previous/previous.cfg\nsudo chown b"
},
{
"path": "docker-compose.yml",
"chars": 1253,
"preview": "pywb:\n build: ./pywb\n restart: always\n environment:\n - ARCHIVE_JSON=/archives.json\n\n volumes:\n "
},
{
"path": "nginx/Dockerfile",
"chars": 147,
"preview": "FROM nginx:latest\n\nRUN usermod -u 1000 www-data\n#RUN usermod -G staff www-data\n#RUN chown -Rf www-data /cache\nADD nginx."
},
{
"path": "nginx/nginx.conf",
"chars": 2640,
"preview": "user www-data;\nworker_processes 16;\npid /run/nginx.pid;\n\nevents {\n worker_connections 4096;\n}\n\nhttp {\n ##\n # Ba"
},
{
"path": "pull-containers.sh",
"chars": 338,
"preview": "#!/bin/bash\n\n# Pull all containers used by Netcapsule from Dockerhub\n\n# Latest pywb\n#docker pull ikreymer/pywb:dev\n\n\nDIR"
},
{
"path": "push-containers.sh",
"chars": 265,
"preview": "#!/bin/bash\n\n# Push containers to dockerhub\nDIR=$( cd \"$( dirname \"${BASH_SOURCE[0]}\" )\" && pwd )\n\n# Browsers\nfor dir in"
},
{
"path": "pywb/Dockerfile",
"chars": 380,
"preview": "FROM python:2.7\nMAINTAINER Ilya Kreymer <ikreymer at gmail.com>\n\nWORKDIR /webarchive\n\nCOPY requirements.txt /webarchive/"
},
{
"path": "pywb/archivereplayview.py",
"chars": 10866,
"preview": "from pywb.webapp.handlers import WBHandler\nfrom pywb.utils.statusandheaders import StatusAndHeaders\nfrom pywb.webapp.rep"
},
{
"path": "pywb/ca/.gitignore",
"chars": 71,
"preview": "# Ignore everything in this directory\n*\n# Except this file\n!.gitignore\n"
},
{
"path": "pywb/config.yaml",
"chars": 1754,
"preview": "\ncollections:\n\n # For Single Archive Collections: Specify CDX Path, url template and name\n single_archive:\n "
},
{
"path": "pywb/mementoquery.py",
"chars": 11237,
"preview": "import requests\nimport sys\nimport calendar\nimport datetime\nimport itertools\nimport urllib\nimport json\n\nfrom collections "
},
{
"path": "pywb/redisclient.py",
"chars": 1699,
"preview": "import redis\nimport os\nimport yaml\n\nfrom pywb.utils.canonicalize import canonicalize\n\n\n#================================"
},
{
"path": "pywb/requirements.txt",
"chars": 44,
"preview": "certauth\nyoutube-dl\nboto\nuwsgi\ngevent\nredis\n"
},
{
"path": "pywb/templates/blank.html",
"chars": 0,
"preview": ""
},
{
"path": "pywb/templates/head_insert.html",
"chars": 310,
"preview": "<SCRIPT language=\"JavaScript\">\n\nfunction beacon() {\n if (window == window.top) {\n document.writeln('<IMG SRC=\""
},
{
"path": "pywb/templates/not_found.html",
"chars": 92,
"preview": "<h2>Url Not Found</h2>\n\nSorry, the url <b>{{ url }}</b> could not be found in the archives.\n"
},
{
"path": "pywb/uwsgi.ini",
"chars": 373,
"preview": "[uwsgi]\nif-not-env = PORT\nhttp-socket = :8080\nendif =\n\nmaster = true\nbuffer-size = 65536\ndie-on-term = true\n\nif-env = VI"
},
{
"path": "run-local.sh",
"chars": 339,
"preview": "#export ARCHIVE_JSON=http://webenact.rhizome.org/collinfo.json\nexport ARCHIVE_JSON=./archives.gen.json\nexport RANDOM_URL"
}
]
// ... and 5 more files (download for full content)
About this extraction
This page contains the full source code of the ikreymer/netcapsule GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 127 files (1.7 MB), approximately 1.2M tokens, and a symbol index with 141 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.