First release, remove not used lines from gitignore

This commit is contained in:
HelloZeroNet 2015-01-12 02:03:45 +01:00
parent c0bfb3b062
commit d28e1cb4a6
85 changed files with 7205 additions and 50 deletions

286
src/Ui/UiRequest.py Normal file
View file

@ -0,0 +1,286 @@
import time, re, os, mimetypes, json
from Config import config
from Site import SiteManager
from Ui.UiWebsocket import UiWebsocket
status_texts = {
200: "200 OK",
400: "400 Bad Request",
403: "403 Forbidden",
404: "404 Not Found",
}
class UiRequest:
def __init__(self, server = None):
if server:
self.server = server
self.log = server.log
self.get = {} # Get parameters
self.env = {} # Enviroment settings
self.start_response = None # Start response function
# Call the request handler function base on path
def route(self, path):
if config.ui_restrict and self.env['REMOTE_ADDR'] != config.ui_restrict: # Restict Ui access by ip
return self.error403()
if path == "/":
return self.actionIndex()
elif path == "/favicon.ico":
return self.actionFile("src/Ui/media/img/favicon.ico")
# Media
elif path.startswith("/uimedia/"):
return self.actionUiMedia(path)
elif path.startswith("/media"):
return self.actionSiteMedia(path)
# Websocket
elif path == "/Websocket":
return self.actionWebsocket()
# Debug
elif path == "/Debug" and config.debug:
return self.actionDebug()
elif path == "/Console" and config.debug:
return self.actionConsole()
# Test
elif path == "/Test/Websocket":
return self.actionFile("Data/temp/ws_test.html")
elif path == "/Test/Stream":
return self.actionTestStream()
# Site media wrapper
else:
return self.actionWrapper(path)
# Get mime by filename
def getContentType(self, file_name):
content_type = mimetypes.guess_type(file_name)[0]
if not content_type:
if file_name.endswith("json"): # Correct json header
content_type = "application/json"
else:
content_type = "application/octet-stream"
return content_type
# Send response headers
def sendHeader(self, status=200, content_type="text/html; charset=utf-8", extra_headers=[]):
headers = []
headers.append(("Version", "HTTP/1.1"))
headers.append(("Access-Control-Allow-Origin", "*")) # Allow json access
headers.append(("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept")) # Allow json access
headers.append(("Cache-Control", "no-cache, no-store, private, must-revalidate, max-age=0")) # No caching at all
#headers.append(("Cache-Control", "public, max-age=604800")) # Cache 1 week
headers.append(("Content-Type", content_type))
for extra_header in extra_headers:
headers.append(extra_header)
self.start_response(status_texts[status], headers)
# Renders a template
def render(self, template_path, *args, **kwargs):
#template = SimpleTemplate(open(template_path), lookup=[os.path.dirname(template_path)])
#yield str(template.render(*args, **kwargs).encode("utf8"))
template = open(template_path).read()
yield template.format(**kwargs)
# - Actions -
# Redirect to an url
def actionRedirect(self, url):
self.start_response('301 Redirect', [('Location', url)])
yield "Location changed: %s" % url
def actionIndex(self):
return self.actionRedirect("/"+config.homepage)
# Render a file from media with iframe site wrapper
def actionWrapper(self, path):
if self.env.get("HTTP_X_REQUESTED_WITH"): return self.error403() # No ajax allowed on wrapper
match = re.match("/(?P<site>[A-Za-z0-9]+)(?P<inner_path>/.*|$)", path)
if match:
inner_path = match.group("inner_path").lstrip("/")
if not inner_path: inner_path = "index.html" # If inner path defaults to index.html
site = self.server.sites.get(match.group("site"))
if site and site.content and not site.bad_files: # Its downloaded
title = site.content["title"]
else:
title = "Loading %s..." % match.group("site")
site = SiteManager.need(match.group("site")) # Start download site
if not site: self.error404()
self.sendHeader(extra_headers=[("X-Frame-Options", "DENY")])
return self.render("src/Ui/template/wrapper.html",
inner_path=inner_path,
address=match.group("site"),
title=title,
auth_key=site.settings["auth_key"],
permissions=json.dumps(site.settings["permissions"]),
show_loadingscreen=json.dumps(not os.path.isfile(site.getPath(inner_path))),
homepage=config.homepage
)
else: # Bad url
return self.error404(path)
# Serve a media for site
def actionSiteMedia(self, path):
match = re.match("/media/(?P<site>[A-Za-z0-9]+)/(?P<inner_path>.*)", path)
referer = self.env.get("HTTP_REFERER")
if referer: # Only allow same site to receive media
referer = re.sub("http://.*?/", "/", referer) # Remove server address
referer = referer.replace("/media", "") # Media
if not referer.startswith("/"+match.group("site")): return self.error403() # Referer not starts same address as requested path
if match: # Looks like a valid path
file_path = "data/%s/%s" % (match.group("site"), match.group("inner_path"))
allowed_dir = os.path.abspath("data/%s" % match.group("site")) # Only files within data/sitehash allowed
if ".." in file_path or not os.path.dirname(os.path.abspath(file_path)).startswith(allowed_dir): # File not in allowed path
return self.error403()
else:
if config.debug and file_path.split("/")[-1].startswith("all."): # When debugging merge *.css to all.css and *.js to all.js
site = self.server.sites.get(match.group("site"))
if site.settings["own"]:
from Debug import DebugMedia
DebugMedia.merge(file_path)
if os.path.isfile(file_path): # File exits
return self.actionFile(file_path)
else: # File not exits, try to download
site = SiteManager.need(match.group("site"), all_file=False)
self.sendHeader(content_type=self.getContentType(file_path)) # ?? Get Exception without this
result = site.needFile(match.group("inner_path")) # Wait until file downloads
return self.actionFile(file_path)
else: # Bad url
return self.error404(path)
# Serve a media for ui
def actionUiMedia(self, path):
match = re.match("/uimedia/(?P<inner_path>.*)", path)
if match: # Looks like a valid path
file_path = "src/Ui/media/%s" % match.group("inner_path")
allowed_dir = os.path.abspath("src/Ui/media") # Only files within data/sitehash allowed
if ".." in file_path or not os.path.dirname(os.path.abspath(file_path)).startswith(allowed_dir): # File not in allowed path
return self.error403()
else:
if config.debug and match.group("inner_path").startswith("all."): # When debugging merge *.css to all.css and *.js to all.js
from Debug import DebugMedia
DebugMedia.merge(file_path)
return self.actionFile(file_path)
else: # Bad url
return self.error400()
# Stream a file to client
def actionFile(self, file_path, block_size = 64*1024):
if os.path.isfile(file_path):
# Try to figure out content type by extension
content_type = self.getContentType(file_path)
self.sendHeader(content_type = content_type) # TODO: Dont allow external access: extra_headers=[("Content-Security-Policy", "default-src 'unsafe-inline' data: http://localhost:43110 ws://localhost:43110")]
if self.env["REQUEST_METHOD"] != "OPTIONS":
file = open(file_path, "rb")
while 1:
try:
block = file.read(block_size)
if block:
yield block
else:
raise StopIteration
except StopIteration:
file.close()
break
else: # File not exits
yield self.error404(file_path)
# On websocket connection
def actionWebsocket(self):
ws = self.env.get("wsgi.websocket")
if ws:
auth_key = self.get["auth_key"]
# Find site by auth_key
site = None
for site_check in self.server.sites.values():
if site_check.settings["auth_key"] == auth_key: site = site_check
if site: # Correct auth key
ui_websocket = UiWebsocket(ws, site, self.server)
site.websockets.append(ui_websocket) # Add to site websockets to allow notify on events
ui_websocket.start()
for site_check in self.server.sites.values(): # Remove websocket from every site (admin sites allowed to join other sites event channels)
if ui_websocket in site_check.websockets:
site_check.websockets.remove(ui_websocket)
return "Bye."
else: # No site found by auth key
self.log.error("Auth key not found: %s" % auth_key)
return self.error403()
else:
start_response("400 Bad Request", [])
return "Not a websocket!"
# Debug last error
def actionDebug(self):
# Raise last error from DebugHook
import sys
last_error = sys.modules["src.main"].DebugHook.last_error
if last_error:
raise last_error[0], last_error[1], last_error[2]
else:
self.sendHeader()
yield "No error! :)"
# Just raise an error to get console
def actionConsole(self):
raise Exception("Here is your console")
# - Tests -
def actionTestStream(self):
self.sendHeader()
yield " "*1080 # Overflow browser's buffer
yield "He"
time.sleep(1)
yield "llo!"
yield "Running websockets: %s" % len(self.server.websockets)
self.server.sendMessage("Hello!")
# - Errors -
# Send bad request error
def error400(self):
self.sendHeader(400)
return "Bad Request"
# You are not allowed to access this
def error403(self):
self.sendHeader(403)
return "Forbidden"
# Send file not found error
def error404(self, path = None):
self.sendHeader(404)
return "Not Found: %s" % path
# - Reload for eaiser developing -
def reload(self):
import imp
global UiWebsocket
UiWebsocket = imp.load_source("UiWebsocket", "src/Ui/UiWebsocket.py").UiWebsocket

93
src/Ui/UiServer.py Normal file
View file

@ -0,0 +1,93 @@
from gevent import monkey; monkey.patch_all(thread = False)
import logging, time, cgi, string, random
from gevent.pywsgi import WSGIServer
from gevent.pywsgi import WSGIHandler
from lib.geventwebsocket.handler import WebSocketHandler
from Ui import UiRequest
from Site import SiteManager
from Config import config
# Skip websocket handler if not necessary
class UiWSGIHandler(WSGIHandler):
def __init__(self, *args, **kwargs):
super(UiWSGIHandler, self).__init__(*args, **kwargs)
self.ws_handler = WebSocketHandler(*args, **kwargs)
def run_application(self):
if "HTTP_UPGRADE" in self.environ: # Websocket request
self.ws_handler.__dict__ = self.__dict__ # Match class variables
self.ws_handler.run_application()
else: # Standard HTTP request
#print self.application.__class__.__name__
return super(UiWSGIHandler, self).run_application()
class UiServer:
def __init__(self):
self.ip = config.ui_ip
self.port = config.ui_port
if self.ip == "*": self.ip = "" # Bind all
#self.sidebar_websockets = [] # Sidebar websocket connections
#self.auth_key = ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(12)) # Global admin auth key
self.sites = SiteManager.list()
self.log = logging.getLogger(__name__)
self.ui_request = UiRequest(self)
# Handle WSGI request
def handleRequest(self, env, start_response):
path = env["PATH_INFO"]
self.ui_request.env = env
self.ui_request.start_response = start_response
if env.get("QUERY_STRING"):
self.ui_request.get = dict(cgi.parse_qsl(env['QUERY_STRING']))
else:
self.ui_request.get = {}
return self.ui_request.route(path)
# Send a message to all connected client
def sendMessage(self, message):
sent = 0
for ws in self.websockets:
try:
ws.send(message)
sent += 1
except Exception, err:
self.log.error("addMessage error: %s" % err)
self.server.websockets.remove(ws)
return sent
# Reload the UiRequest class to prevent restarts in debug mode
def reload(self):
import imp
self.ui_request = imp.load_source("UiRequest", "src/Ui/UiRequest.py").UiRequest(self)
self.ui_request.reload()
# Bind and run the server
def start(self):
handler = self.handleRequest
if config.debug:
# Auto reload UiRequest on change
from Debug import DebugReloader
DebugReloader(self.reload)
# Werkzeug Debugger
try:
from werkzeug.debug import DebuggedApplication
handler = DebuggedApplication(self.handleRequest, evalex=True)
except Exception, err:
self.log.info("%s: For debugging please download Werkzeug (http://werkzeug.pocoo.org/)" % err)
from Debug import DebugReloader
self.log.write = lambda msg: self.log.debug(msg.strip()) # For Wsgi access.log
self.log.info("--------------------------------------")
self.log.info("Web interface: http://%s:%s/" % (config.ui_ip, config.ui_port))
self.log.info("--------------------------------------")
WSGIServer((self.ip, self.port), handler, handler_class=UiWSGIHandler, log=self.log).serve_forever()

217
src/Ui/UiWebsocket.py Normal file
View file

@ -0,0 +1,217 @@
import json, gevent, time, sys, hashlib
from Config import config
from Site import SiteManager
class UiWebsocket:
def __init__(self, ws, site, server):
self.ws = ws
self.site = site
self.server = server
self.next_message_id = 1
self.waiting_cb = {} # Waiting for callback. Key: message_id, Value: function pointer
self.channels = [] # Channels joined to
# Start listener loop
def start(self):
ws = self.ws
if self.site.address == config.homepage and not self.site.page_requested: # Add open fileserver port message or closed port error to homepage at first request after start
if config.ip_external:
self.site.notifications.append(["done", "Congratulation, your port <b>"+str(config.fileserver_port)+"</b> is opened. <br>You are full member of ZeroNet network!", 10000])
elif config.ip_external == False:
self.site.notifications.append(["error", "Your network connection is restricted. Please, open <b>"+str(config.fileserver_port)+"</b> port <br>on your router to become full member of ZeroNet network.", 0])
self.site.page_requested = True # Dont add connection notification anymore
for notification in self.site.notifications: # Send pending notification messages
self.cmd("notification", notification)
self.site.notifications = []
while True:
try:
message = ws.receive()
if message:
self.handleRequest(message)
except Exception, err:
if err.message != 'Connection is already closed':
if config.debug: # Allow websocket errors to appear on /Debug
import sys
sys.modules["src.main"].DebugHook.handleError()
self.site.log.error("WebSocket error: %s" % err)
return "Bye."
# Event in a channel
def event(self, channel, *params):
if channel in self.channels: # We are joined to channel
if channel == "siteChanged":
site = params[0] # Triggerer site
site_info = self.siteInfo(site)
if len(params) > 1 and params[1]: # Extra data
site_info.update(params[1])
self.cmd("setSiteInfo", site_info)
# Send response to client (to = message.id)
def response(self, to, result):
self.send({"cmd": "response", "to": to, "result": result})
# Send a command
def cmd(self, cmd, params={}, cb = None):
self.send({"cmd": cmd, "params": params}, cb)
# Encode to json and send message
def send(self, message, cb = None):
message["id"] = self.next_message_id # Add message id to allow response
self.next_message_id += 1
self.ws.send(json.dumps(message))
if cb: # Callback after client responsed
self.waiting_cb[message["id"]] = cb
# Handle incoming messages
def handleRequest(self, data):
req = json.loads(data)
cmd = req["cmd"]
permissions = self.site.settings["permissions"]
if cmd == "response":
self.actionResponse(req)
elif cmd == "ping":
self.actionPing(req["id"])
elif cmd == "channelJoin":
self.actionChannelJoin(req["id"], req["params"])
elif cmd == "siteInfo":
self.actionSiteInfo(req["id"], req["params"])
elif cmd == "serverInfo":
self.actionServerInfo(req["id"], req["params"])
elif cmd == "siteUpdate":
self.actionSiteUpdate(req["id"], req["params"])
# Admin commands
elif cmd == "sitePause" and "ADMIN" in permissions:
self.actionSitePause(req["id"], req["params"])
elif cmd == "siteResume" and "ADMIN" in permissions:
self.actionSiteResume(req["id"], req["params"])
elif cmd == "siteList" and "ADMIN" in permissions:
self.actionSiteList(req["id"], req["params"])
elif cmd == "channelJoinAllsite" and "ADMIN" in permissions:
self.actionChannelJoinAllsite(req["id"], req["params"])
# Unknown command
else:
self.response(req["id"], "Unknown command: %s" % cmd)
# - Actions -
# Do callback on response {"cmd": "response", "to": message_id, "result": result}
def actionResponse(self, req):
if req["to"] in self.waiting_cb:
self.waiting_cb(req["result"]) # Call callback function
else:
self.site.log.error("Websocket callback not found: %s" % req)
# Send a simple pong answer
def actionPing(self, to):
self.response(to, "pong")
# Format site info
def siteInfo(self, site):
ret = {
"auth_id": self.site.settings["auth_key"][0:10],
"auth_id_md5": hashlib.md5(self.site.settings["auth_key"][0:10]).hexdigest(),
"address": site.address,
"settings": site.settings,
"content_updated": site.content_updated,
"bad_files": site.bad_files.keys(),
"last_downloads": site.last_downloads,
"peers": len(site.peers),
"tasks": [task["inner_path"] for task in site.worker_manager.tasks],
"content": site.content
}
if site.settings["serving"] and site.content: ret["peers"] += 1 # Add myself if serving
return ret
# Send site details
def actionSiteInfo(self, to, params):
ret = self.siteInfo(self.site)
self.response(to, ret)
# Join to an event channel
def actionChannelJoin(self, to, params):
if params["channel"] not in self.channels:
self.channels.append(params["channel"])
# Server variables
def actionServerInfo(self, to, params):
ret = {
"ip_external": config.ip_external,
"platform": sys.platform,
"fileserver_ip": config.fileserver_ip,
"fileserver_port": config.fileserver_port,
"ui_ip": config.ui_ip,
"ui_port": config.ui_port,
"debug": config.debug
}
self.response(to, ret)
# - Admin actions -
# List all site info
def actionSiteList(self, to, params):
ret = []
SiteManager.load() # Reload sites
for site in self.server.sites.values():
if not site.content: continue # Broken site
ret.append(self.siteInfo(site))
self.response(to, ret)
# Join to an event channel on all sites
def actionChannelJoinAllsite(self, to, params):
if params["channel"] not in self.channels: # Add channel to channels
self.channels.append(params["channel"])
for site in self.server.sites.values(): # Add websocket to every channel
if self not in site.websockets:
site.websockets.append(self)
# Update site content.json
def actionSiteUpdate(self, to, params):
address = params.get("address")
site = self.server.sites.get(address)
if site and (site.address == self.site.address or "ADMIN" in self.site.settings["permissions"]):
gevent.spawn(site.update)
else:
self.response(to, {"error": "Unknown site: %s" % address})
# Pause site serving
def actionSitePause(self, to, params):
address = params.get("address")
site = self.server.sites.get(address)
if site:
site.settings["serving"] = False
site.saveSettings()
site.updateWebsocket()
else:
self.response(to, {"error": "Unknown site: %s" % address})
# Resume site serving
def actionSiteResume(self, to, params):
address = params.get("address")
site = self.server.sites.get(address)
if site:
site.settings["serving"] = True
site.saveSettings()
gevent.spawn(site.update)
time.sleep(0.001) # Wait for update thread starting
site.updateWebsocket()
else:
self.response(to, {"error": "Unknown site: %s" % address})

3
src/Ui/__init__.py Normal file
View file

@ -0,0 +1,3 @@
from UiServer import UiServer
from UiRequest import UiRequest
from UiWebsocket import UiWebsocket

View file

@ -0,0 +1,40 @@
class Loading
constructor: ->
if window.show_loadingscreen then @showScreen()
showScreen: ->
$(".loadingscreen").css("display", "block").addClassLater("ready")
@screen_visible = true
@printLine "&nbsp;&nbsp;&nbsp;Connecting..."
# We dont need loadingscreen anymore
hideScreen: ->
if @screen_visible # Hide with animate
$(".loadingscreen").addClass("done").removeLater(2000)
else # Not visible, just remove
$(".loadingscreen").remove()
@screen_visible = false
# Append text to last line of loadingscreen
print: (text, type="normal") ->
if not @screen_visible then return false
$(".loadingscreen .console .cursor").remove() # Remove previous cursor
last_line = $(".loadingscreen .console .console-line:last-child")
if type == "error" then text = "<span class='console-error'>#{text}</span>"
last_line.html(last_line.html()+text)
# Add line to loading screen
printLine: (text, type="normal") ->
if not @screen_visible then return false
$(".loadingscreen .console .cursor").remove() # Remove previous cursor
if type == "error" then text = "<span class='console-error'>#{text}</span>" else text = text+"<span class='cursor'> </span>"
$(".loadingscreen .console").append("<div class='console-line'>#{text}</div>")
window.Loading = Loading

View file

@ -0,0 +1,68 @@
class Notifications
constructor: (@elem) ->
@
test: ->
setTimeout (=>
@add("connection", "error", "Connection lost to <b>UiServer</b> on <b>localhost</b>!")
@add("message-Anyone", "info", "New from <b>Anyone</b>.")
), 1000
setTimeout (=>
@add("connection", "done", "<b>UiServer</b> connection recovered.", 5000)
), 3000
add: (id, type, body, timeout=0) ->
@log id, type, body, timeout
# Close notifications with same id
for elem in $(".notification-#{id}")
@close $(elem)
# Create element
elem = $(".notification.template", @elem).clone().removeClass("template")
elem.addClass("notification-#{type}").addClass("notification-#{id}")
# Update text
if type == "error"
$(".notification-icon", elem).html("!")
else if type == "done"
$(".notification-icon", elem).html("<div class='icon-success'></div>")
else
$(".notification-icon", elem).html("i")
$(".body", elem).html(body)
elem.appendTo(@elem)
# Timeout
if timeout
$(".close", elem).remove() # No need of close button
setTimeout (=>
@close elem
), timeout
# Animate
width = elem.outerWidth()
if not timeout then width += 20 # Add space for close button
elem.css({"width": "50px", "transform": "scale(0.01)"})
elem.animate({"scale": 1}, 800, "easeOutElastic")
elem.animate({"width": width}, 700, "easeInOutCubic")
# Close button
$(".close", elem).on "click", =>
@close elem
return false
@
close: (elem) ->
elem.stop().animate {"width": 0, "opacity": 0}, 700, "easeInOutCubic"
elem.slideUp 300, (-> elem.remove())
log: (args...) ->
console.log "[Notifications]", args...
window.Notifications = Notifications

View file

@ -0,0 +1,33 @@
class Sidebar
constructor: ->
@initFixbutton()
initFixbutton: ->
$(".fixbutton-bg").on "mouseover", ->
$(@).stop().animate({"scale": 0.7}, 800, "easeOutElastic")
$(".fixbutton-burger").stop().animate({"opacity": 1.5, "left": 0}, 800, "easeOutElastic")
$(".fixbutton-text").stop().animate({"opacity": 0, "left": 20}, 300, "easeOutCubic")
$(".fixbutton-bg").on "mouseout", ->
$(@).stop().animate({"scale": 0.6}, 300, "easeOutCubic")
$(".fixbutton-burger").stop().animate({"opacity": 0, "left": -20}, 300, "easeOutCubic")
$(".fixbutton-text").stop().animate({"opacity": 1, "left": 0}, 300, "easeOutBack")
###$(".fixbutton-bg").on "click", ->
return false
###
$(".fixbutton-bg").on "mousedown", ->
$(".fixbutton-burger").stop().animate({"scale": 0.7, "left": 0}, 300, "easeOutCubic")
#$("#inner-iframe").toggleClass("back")
#$(".wrapper-iframe").stop().animate({"scale": 0.9}, 600, "easeOutCubic")
#$("body").addClass("back")
$(".fixbutton-bg").on "mouseup", ->
$(".fixbutton-burger").stop().animate({"scale": 1, "left": 0}, 600, "easeOutElastic")
window.Sidebar = Sidebar

152
src/Ui/media/Wrapper.coffee Normal file
View file

@ -0,0 +1,152 @@
class Wrapper
constructor: (ws_url) ->
@log "Created!"
@loading = new Loading()
@notifications = new Notifications($(".notifications"))
@sidebar = new Sidebar()
window.addEventListener("message", @onMessageInner, false)
@inner = document.getElementById("inner-iframe").contentWindow
@ws = new ZeroWebsocket(ws_url)
@ws.next_message_id = 1000000 # Avoid messageid collision :)
@ws.onOpen = @onOpenWebsocket
@ws.onClose = @onCloseWebsocket
@ws.onMessage = @onMessageWebsocket
@ws.connect()
@ws_error = null # Ws error message
@site_info = null # Hold latest site info
@inner_loaded = false # If iframe loaded or not
@inner_ready = false # Inner frame ready to receive messages
@wrapperWsInited = false # Wrapper notified on websocket open
@site_error = null # Latest failed file download
window.onload = @onLoad # On iframe loaded
@
# Incoming message from UiServer websocket
onMessageWebsocket: (e) =>
message = JSON.parse(e.data)
cmd = message.cmd
if cmd == "response"
if @ws.waiting_cb[message.to]? # We are waiting for response
@ws.waiting_cb[message.to](message.result)
else
@sendInner message # Pass message to inner frame
else if cmd == "notification" # Display notification
@notifications.add("notification-#{message.id}", message.params[0], message.params[1], message.params[2])
else if cmd == "setSiteInfo"
@sendInner message # Pass to inner frame
if message.params.address == window.address # Current page
@setSiteInfo message.params
else
@sendInner message # Pass message to inner frame
# Incoming message from inner frame
onMessageInner: (e) =>
message = e.data
cmd = message.cmd
if cmd == "innerReady"
@inner_ready = true
@log "innerReady", @ws.ws.readyState, @wrapperWsInited
if @ws.ws.readyState == 1 and not @wrapperWsInited # If ws already opened
@sendInner {"cmd": "wrapperOpenedWebsocket"}
@wrapperWsInited = true
else if cmd == "wrapperNotification"
@notifications.add("notification-#{message.id}", message.params[0], message.params[1], message.params[2])
else # Send to websocket
@ws.send(message) # Pass message to websocket
onOpenWebsocket: (e) =>
@ws.cmd "channelJoin", {"channel": "siteChanged"} # Get info on modifications
@log "onOpenWebsocket", @inner_ready, @wrapperWsInited
if not @wrapperWsInited and @inner_ready
@sendInner {"cmd": "wrapperOpenedWebsocket"} # Send to inner frame
@wrapperWsInited = true
if @inner_loaded # Update site info
@reloadSiteInfo()
# If inner frame not loaded for 2 sec show peer informations on loading screen by loading site info
setTimeout (=>
if not @site_info then @reloadSiteInfo()
), 2000
if @ws_error
@notifications.add("connection", "done", "Connection with <b>UiServer Websocket</b> recovered.", 6000)
@ws_error = null
onCloseWebsocket: (e) =>
@wrapperWsInited = false
setTimeout (=> # Wait a bit, maybe its page closing
@sendInner {"cmd": "wrapperClosedWebsocket"} # Send to inner frame
if e.code == 1000 # Server error please reload page
@ws_error = @notifications.add("connection", "error", "UiServer Websocket error, please reload the page.")
else if not @ws_error
@ws_error = @notifications.add("connection", "error", "Connection with <b>UiServer Websocket</b> was lost. Reconnecting...")
), 500
# Iframe loaded
onLoad: (e) =>
@log "onLoad", e
@inner_loaded = true
if not @inner_ready then @sendInner {"cmd": "wrapperReady"} # Inner frame loaded before wrapper
if not @site_error then @loading.hideScreen() # Hide loading screen
if @ws.ws.readyState == 1 and not @site_info # Ws opened
@reloadSiteInfo()
# Send message to innerframe
sendInner: (message) ->
@inner.postMessage(message, '*')
# Get site info from UiServer
reloadSiteInfo: ->
@ws.cmd "siteInfo", {}, (site_info) =>
@setSiteInfo site_info
window.document.title = site_info.content.title+" - ZeroNet"
@log "Setting title to", window.document.title
# Got setSiteInfo from websocket UiServer
setSiteInfo: (site_info) ->
if site_info.event? # If loading screen visible add event to it
# File started downloading
if site_info.event[0] == "file_added" and site_info.bad_files.length
@loading.printLine("#{site_info.bad_files.length} files needs to be downloaded")
# File finished downloading
else if site_info.event[0] == "file_done"
@loading.printLine("#{site_info.event[1]} downloaded")
if site_info.event[1] == window.inner_path # File downloaded we currently on
@loading.hideScreen()
if not $(".loadingscreen").length # Loading screen already removed (loaded +2sec)
@notifications.add("modified", "info", "New version of this page has just released.<br>Reload to see the modified content.")
# File failed downloading
else if site_info.event[0] == "file_failed"
@site_error = site_info.event[1]
@loading.printLine("#{site_info.event[1]} download failed", "error")
# New peers found
else if site_info.event[0] == "peers_added"
@loading.printLine("Peers found: #{site_info.peers}")
if @loading.screen_visible and not @site_info # First site info display current peers
if site_info.peers > 1
@loading.printLine "Peers found: #{site_info.peers}"
else
@site_error = "No peers found"
@loading.printLine "No peers found"
@site_info = site_info
log: (args...) ->
console.log "[Wrapper]", args...
ws_url = "ws://#{window.location.hostname}:#{window.location.port}/Websocket?auth_key=#{window.auth_key}"
window.wrapper = new Wrapper(ws_url)

102
src/Ui/media/Wrapper.css Normal file
View file

@ -0,0 +1,102 @@
body { margin: 0px; padding: 0px; height: 100%; background-color: #D2CECD; overflow: hidden }
body.back { background-color: #090909 }
a { color: black }
.template { display: none !important }
#inner-iframe { width: 100%; height: 100%; position: absolute; border: 0px; transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out }
#inner-iframe.back { transform: scale(0.95) translate(-300px, 0px); opacity: 0.4 }
/* Fixbutton */
.fixbutton {
position: absolute; right: 35px; top: 15px; width: 40px; z-index: 999;
text-align: center; color: white; font-family: Consolas; font-size: 25px; line-height: 40px;
}
.fixbutton-bg {
border-radius: 80px; background-color: rgba(180, 180, 180, 0.5); cursor: pointer;
display: block; width: 80px; height: 80px; transition: background-color 0.2s, box-shadow 0.5s; transform: scale(0.6); margin-left: -20px; margin-top: -20px; /* 2x size to prevent blur on anim */
/*box-shadow: inset 105px 260px 0px -200px rgba(0,0,0,0.1);*/ /* box-shadow: inset -75px 183px 0px -200px rgba(0,0,0,0.1); */
}
.fixbutton-text { pointer-events: none; position: absolute; z-index: 999; width: 40px; backface-visibility: hidden; perspective: 1000px }
.fixbutton-burger { pointer-events: none; position: absolute; z-index: 999; width: 40px; opacity: 0; left: -20px }
.fixbutton-bg:hover { background-color: #AF3BFF }
/* Notification */
.notifications { position: absolute; top: 0px; right: 85px; display: inline-block; z-index: 999; white-space: nowrap }
.notification {
position: relative; float: right; clear: both; margin: 10px; height: 50px; box-sizing: border-box; overflow: hidden; backface-visibility: hidden; perspective: 1000px;
background-color: white; color: #4F4F4F; font-family: 'Helvetica Neue', 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px
}
.notification-icon {
display: block; width: 50px; height: 50px; position: absolute; float: left; z-index: 1;
text-align: center; background-color: #e74c3c; line-height: 45px; vertical-align: bottom; font-size: 40px; color: white;
}
.notification .body { max-width: 420px; padding-left: 68px; padding-right: 17px; height: 50px; vertical-align: middle; display: table-cell }
.notification.visible { max-width: 350px }
.notification .close { position: absolute; top: 0px; right: 0px; font-size: 19px; line-height: 13px; color: #DDD; padding: 7px; text-decoration: none }
.notification .close:hover { color: black }
.notification .close:active, .notification .close:focus { color: #AF3BFF }
/* Notification types */
.notification-info .notification-icon { font-size: 22px; font-weight: bold; background-color: #2980b9; line-height: 48px }
.notification-done .notification-icon { font-size: 22px; background-color: #27ae60 }
/* Icons (based on http://nicolasgallagher.com/pure-css-gui-icons/demo/) */
.icon-success { left:6px; width:5px; height:12px; border-width:0 5px 5px 0; border-style:solid; border-color:white; margin-left: 20px; margin-top: 15px; transform:rotate(45deg) }
/* Loading screen */
.loadingscreen { width: 100%; height: 100%; position: absolute; background-color: #EEE; z-index: 1; overflow: hidden; display: none }
.loading-text { text-align: center; vertical-align: middle; top: 50%; position: absolute; margin-top: 39px; width: 100% }
/* Console */
.console { line-height: 24px; font-family: monospace; font-size: 14px; color: #ADADAD; text-transform: uppercase; opacity: 0; transform: translateY(-20px); }
.console-line:last-child { color: #6C6767 }
.console .cursor {
background-color: #999; color: #999; animation: pulse 1.5s infinite ease-in-out; margin-right: -9px;
display: inline-block; width: 9px; height: 19px; vertical-align: -4px;
}
.console .console-error { color: #e74c3c; font-weight: bold; animation: pulse 2s infinite linear }
/* Flipper loading anim */
.flipper-container { width: 40px; height: 40px; position: absolute; top: 0%; left: 50%; transform: translate3d(-50%, -50%, 0); perspective: 1200; opacity: 0 }
.flipper { position: relative; display: block; height: inherit; width: inherit; animation: flip 1.2s infinite ease-in-out; -webkit-transform-style: preserve-3d; }
.flipper .front, .flipper .back {
position: absolute; top: 0; left: 0; backface-visibility: hidden; /*transform-style: preserve-3d;*/ display: block;
background-color: #d50000; height: 100%; width: 100%; /*outline: 1px solid transparent; /* FF AA fix */
}
.flipper .back { background-color: white; z-index: 800; transform: rotateY(-180deg) }
/* Loading ready */
.loadingscreen.ready .console { opacity: 1; transform: translateY(0px); transition: all 0.3s }
.loadingscreen.ready .flipper-container { top: 50%; opacity: 1; transition: all 1s cubic-bezier(1, 0, 0, 1); }
/* Loading done */
.loadingscreen.done { height: 0%; transition: all 1s cubic-bezier(0.6, -0.28, 0.735, 0.045); }
.loadingscreen.done .console { transform: translateY(300px); opacity: 0; transition: all 1.5s }
.loadingscreen.done .flipper-container { opacity: 0; transition: all 1.5s }
/* Animations */
@keyframes flip {
0% { transform: perspective(120px) rotateX(0deg) rotateY(0deg); }
50% { transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) }
100% { transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); }
}
@keyframes pulse {
0% { opacity: 0 }
5% { opacity: 1 }
30% { opacity: 1 }
70% { opacity: 0 }
100% { opacity: 0 }
}

133
src/Ui/media/all.css Normal file
View file

@ -0,0 +1,133 @@
/* ---- src/Ui/media/Wrapper.css ---- */
body { margin: 0px; padding: 0px; height: 100%; background-color: #D2CECD; overflow: hidden }
body.back { background-color: #090909 }
a { color: black }
.template { display: none !important }
#inner-iframe { width: 100%; height: 100%; position: absolute; border: 0px; -webkit-transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out ; -moz-transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out ; -o-transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out ; -ms-transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out ; transition: all 0.8s cubic-bezier(0.68, -0.55, 0.265, 1.55), opacity 0.8s ease-in-out }
#inner-iframe.back { -webkit-transform: scale(0.95) translate(-300px, 0px); -moz-transform: scale(0.95) translate(-300px, 0px); -o-transform: scale(0.95) translate(-300px, 0px); -ms-transform: scale(0.95) translate(-300px, 0px); transform: scale(0.95) translate(-300px, 0px) ; opacity: 0.4 }
/* Fixbutton */
.fixbutton {
position: absolute; right: 35px; top: 15px; width: 40px; z-index: 999;
text-align: center; color: white; font-family: Consolas; font-size: 25px; line-height: 40px;
}
.fixbutton-bg {
-webkit-border-radius: 80px; -moz-border-radius: 80px; -o-border-radius: 80px; -ms-border-radius: 80px; border-radius: 80px ; background-color: rgba(180, 180, 180, 0.5); cursor: pointer;
display: block; width: 80px; height: 80px; -webkit-transition: background-color 0.2s, box-shadow 0.5s; -moz-transition: background-color 0.2s, box-shadow 0.5s; -o-transition: background-color 0.2s, box-shadow 0.5s; -ms-transition: background-color 0.2s, box-shadow 0.5s; transition: background-color 0.2s, box-shadow 0.5s ; -webkit-transform: scale(0.6); -moz-transform: scale(0.6); -o-transform: scale(0.6); -ms-transform: scale(0.6); transform: scale(0.6) ; margin-left: -20px; margin-top: -20px; /* 2x size to prevent blur on anim */
/*box-shadow: inset 105px 260px 0px -200px rgba(0,0,0,0.1);*/ /* -webkit-box-shadow: inset -75px 183px 0px -200px rgba(0,0,0,0.1); -moz-box-shadow: inset -75px 183px 0px -200px rgba(0,0,0,0.1); -o-box-shadow: inset -75px 183px 0px -200px rgba(0,0,0,0.1); -ms-box-shadow: inset -75px 183px 0px -200px rgba(0,0,0,0.1); box-shadow: inset -75px 183px 0px -200px rgba(0,0,0,0.1) ; */
}
.fixbutton-text { pointer-events: none; position: absolute; z-index: 999; width: 40px; backface-visibility: hidden; -webkit-perspective: 1000px ; -moz-perspective: 1000px ; -o-perspective: 1000px ; -ms-perspective: 1000px ; perspective: 1000px }
.fixbutton-burger { pointer-events: none; position: absolute; z-index: 999; width: 40px; opacity: 0; left: -20px }
.fixbutton-bg:hover { background-color: #AF3BFF }
/* Notification */
.notifications { position: absolute; top: 0px; right: 85px; display: inline-block; z-index: 999; white-space: nowrap }
.notification {
position: relative; float: right; clear: both; margin: 10px; height: 50px; -webkit-box-sizing: border-box; -moz-box-sizing: border-box; -o-box-sizing: border-box; -ms-box-sizing: border-box; box-sizing: border-box ; overflow: hidden; backface-visibility: hidden; -webkit-perspective: 1000px; -moz-perspective: 1000px; -o-perspective: 1000px; -ms-perspective: 1000px; perspective: 1000px ;
background-color: white; color: #4F4F4F; font-family: 'Helvetica Neue', 'Segoe UI', Helvetica, Arial, sans-serif; font-size: 14px; line-height: 20px
}
.notification-icon {
display: block; width: 50px; height: 50px; position: absolute; float: left; z-index: 1;
text-align: center; background-color: #e74c3c; line-height: 45px; vertical-align: bottom; font-size: 40px; color: white;
}
.notification .body { max-width: 420px; padding-left: 68px; padding-right: 17px; height: 50px; vertical-align: middle; display: table-cell }
.notification.visible { max-width: 350px }
.notification .close { position: absolute; top: 0px; right: 0px; font-size: 19px; line-height: 13px; color: #DDD; padding: 7px; text-decoration: none }
.notification .close:hover { color: black }
.notification .close:active, .notification .close:focus { color: #AF3BFF }
/* Notification types */
.notification-info .notification-icon { font-size: 22px; font-weight: bold; background-color: #2980b9; line-height: 48px }
.notification-done .notification-icon { font-size: 22px; background-color: #27ae60 }
/* Icons (based on http://nicolasgallagher.com/pure-css-gui-icons/demo/) */
.icon-success { left:6px; width:5px; height:12px; border-width:0 5px 5px 0; border-style:solid; border-color:white; margin-left: 20px; margin-top: 15px; transform:rotate(45deg) }
/* Loading screen */
.loadingscreen { width: 100%; height: 100%; position: absolute; background-color: #EEE; z-index: 1; overflow: hidden; display: none }
.loading-text { text-align: center; vertical-align: middle; top: 50%; position: absolute; margin-top: 39px; width: 100% }
/* Console */
.console { line-height: 24px; font-family: monospace; font-size: 14px; color: #ADADAD; text-transform: uppercase; opacity: 0; -webkit-transform: translateY(-20px); -moz-transform: translateY(-20px); -o-transform: translateY(-20px); -ms-transform: translateY(-20px); transform: translateY(-20px) ; }
.console-line:last-child { color: #6C6767 }
.console .cursor {
background-color: #999; color: #999; -webkit-animation: pulse 1.5s infinite ease-in-out; -moz-animation: pulse 1.5s infinite ease-in-out; -o-animation: pulse 1.5s infinite ease-in-out; -ms-animation: pulse 1.5s infinite ease-in-out; animation: pulse 1.5s infinite ease-in-out ; margin-right: -9px;
display: inline-block; width: 9px; height: 19px; vertical-align: -4px;
}
.console .console-error { color: #e74c3c; font-weight: bold; -webkit-animation: pulse 2s infinite linear ; -moz-animation: pulse 2s infinite linear ; -o-animation: pulse 2s infinite linear ; -ms-animation: pulse 2s infinite linear ; animation: pulse 2s infinite linear }
/* Flipper loading anim */
.flipper-container { width: 40px; height: 40px; position: absolute; top: 0%; left: 50%; -webkit-transform: translate3d(-50%, -50%, 0); -moz-transform: translate3d(-50%, -50%, 0); -o-transform: translate3d(-50%, -50%, 0); -ms-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0) ; -webkit-perspective: 1200; -moz-perspective: 1200; -o-perspective: 1200; -ms-perspective: 1200; perspective: 1200 ; opacity: 0 }
.flipper { position: relative; display: block; height: inherit; width: inherit; -webkit-animation: flip 1.2s infinite ease-in-out; -moz-animation: flip 1.2s infinite ease-in-out; -o-animation: flip 1.2s infinite ease-in-out; -ms-animation: flip 1.2s infinite ease-in-out; animation: flip 1.2s infinite ease-in-out ; -webkit-transform-style: preserve-3d; }
.flipper .front, .flipper .back {
position: absolute; top: 0; left: 0; backface-visibility: hidden; /*transform-style: preserve-3d;*/ display: block;
background-color: #d50000; height: 100%; width: 100%; /*outline: 1px solid transparent; /* FF AA fix */
}
.flipper .back { background-color: white; z-index: 800; -webkit-transform: rotateY(-180deg) ; -moz-transform: rotateY(-180deg) ; -o-transform: rotateY(-180deg) ; -ms-transform: rotateY(-180deg) ; transform: rotateY(-180deg) }
/* Loading ready */
.loadingscreen.ready .console { opacity: 1; -webkit-transform: translateY(0px); -moz-transform: translateY(0px); -o-transform: translateY(0px); -ms-transform: translateY(0px); transform: translateY(0px) ; -webkit-transition: all 0.3s ; -moz-transition: all 0.3s ; -o-transition: all 0.3s ; -ms-transition: all 0.3s ; transition: all 0.3s }
.loadingscreen.ready .flipper-container { top: 50%; opacity: 1; -webkit-transition: all 1s cubic-bezier(1, 0, 0, 1); -moz-transition: all 1s cubic-bezier(1, 0, 0, 1); -o-transition: all 1s cubic-bezier(1, 0, 0, 1); -ms-transition: all 1s cubic-bezier(1, 0, 0, 1); transition: all 1s cubic-bezier(1, 0, 0, 1) ; }
/* Loading done */
.loadingscreen.done { height: 0%; -webkit-transition: all 1s cubic-bezier(0.6, -0.28, 0.735, 0.045); -moz-transition: all 1s cubic-bezier(0.6, -0.28, 0.735, 0.045); -o-transition: all 1s cubic-bezier(0.6, -0.28, 0.735, 0.045); -ms-transition: all 1s cubic-bezier(0.6, -0.28, 0.735, 0.045); transition: all 1s cubic-bezier(0.6, -0.28, 0.735, 0.045) ; }
.loadingscreen.done .console { -webkit-transform: translateY(300px); -moz-transform: translateY(300px); -o-transform: translateY(300px); -ms-transform: translateY(300px); transform: translateY(300px) ; opacity: 0; -webkit-transition: all 1.5s ; -moz-transition: all 1.5s ; -o-transition: all 1.5s ; -ms-transition: all 1.5s ; transition: all 1.5s }
.loadingscreen.done .flipper-container { opacity: 0; -webkit-transition: all 1.5s ; -moz-transition: all 1.5s ; -o-transition: all 1.5s ; -ms-transition: all 1.5s ; transition: all 1.5s }
/* Animations */
@keyframes flip {
0% { -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg); -moz-transform: perspective(120px) rotateX(0deg) rotateY(0deg); -o-transform: perspective(120px) rotateX(0deg) rotateY(0deg); -ms-transform: perspective(120px) rotateX(0deg) rotateY(0deg); transform: perspective(120px) rotateX(0deg) rotateY(0deg) ; }
50% { -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) ; -moz-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) ; -o-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) ; -ms-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) ; transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) }
100% { -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); -moz-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); -o-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); -ms-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg) ; }
}
@-webkit-keyframes flip {
0% { -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg); -moz-transform: perspective(120px) rotateX(0deg) rotateY(0deg); -o-transform: perspective(120px) rotateX(0deg) rotateY(0deg); -ms-transform: perspective(120px) rotateX(0deg) rotateY(0deg); transform: perspective(120px) rotateX(0deg) rotateY(0deg) ; }
50% { -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) ; -moz-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) ; -o-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) ; -ms-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) ; transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) }
100% { -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); -moz-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); -o-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); -ms-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg) ; }
}
@-moz-keyframes flip {
0% { -webkit-transform: perspective(120px) rotateX(0deg) rotateY(0deg); -moz-transform: perspective(120px) rotateX(0deg) rotateY(0deg); -o-transform: perspective(120px) rotateX(0deg) rotateY(0deg); -ms-transform: perspective(120px) rotateX(0deg) rotateY(0deg); transform: perspective(120px) rotateX(0deg) rotateY(0deg) ; }
50% { -webkit-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) ; -moz-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) ; -o-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) ; -ms-transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) ; transform: perspective(120px) rotateX(-180.1deg) rotateY(0deg) }
100% { -webkit-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); -moz-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); -o-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); -ms-transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg); transform: perspective(120px) rotateX(-180deg) rotateY(-179.9deg) ; }
}
@keyframes pulse {
0% { opacity: 0 }
5% { opacity: 1 }
30% { opacity: 1 }
70% { opacity: 0 }
100% { opacity: 0 }
}
@-webkit-keyframes pulse {
0% { opacity: 0 }
5% { opacity: 1 }
30% { opacity: 1 }
70% { opacity: 0 }
100% { opacity: 0 }
}
@-moz-keyframes pulse {
0% { opacity: 0 }
5% { opacity: 1 }
30% { opacity: 1 }
70% { opacity: 0 }
100% { opacity: 0 }
}

895
src/Ui/media/all.js Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

4
src/Ui/media/lib/00-jquery.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,81 @@
class ZeroWebsocket
constructor: (url) ->
@url = url
@next_message_id = 1
@waiting_cb = {}
@init()
init: ->
@
connect: ->
@ws = new WebSocket(@url)
@ws.onmessage = @onMessage
@ws.onopen = @onOpenWebsocket
@ws.onerror = @onErrorWebsocket
@ws.onclose = @onCloseWebsocket
onMessage: (e) =>
message = JSON.parse(e.data)
cmd = message.cmd
if cmd == "response"
if @waiting_cb[message.to]?
@waiting_cb[message.to](message.result)
else
@log "Websocket callback not found:", message
else if cmd == "ping"
@response message.id, "pong"
else
@route cmd, message
route: (cmd, message) =>
@log "Unknown command", message
response: (to, result) ->
@send {"cmd": "response", "to": to, "result": result}
cmd: (cmd, params={}, cb=null) ->
@send {"cmd": cmd, "params": params}, cb
send: (message, cb=null) ->
if not message.id?
message.id = @next_message_id
@next_message_id += 1
@ws.send(JSON.stringify(message))
if cb
@waiting_cb[message.id] = cb
log: (args...) =>
console.log "[ZeroWebsocket]", args...
onOpenWebsocket: (e) =>
@log "Open", e
if @onOpen? then @onOpen(e)
onErrorWebsocket: (e) =>
@log "Error", e
if @onError? then @onError(e)
onCloseWebsocket: (e) =>
@log "Closed", e
if e.code == 1000
@log "Server error, please reload the page"
else # Connection error
setTimeout (=>
@log "Reconnecting..."
@connect()
), 10000
if @onClose? then @onClose(e)
window.ZeroWebsocket = ZeroWebsocket

View file

@ -0,0 +1,27 @@
jQuery.cssHooks['scale'] = {
get: function(elem, computed, extra) {
var match = window.getComputedStyle(elem).transform.match("[0-9\.]+")
if (match) {
var scale = parseFloat(match[0])
return scale
} else {
return 1.0
}
},
set: function(elem, val) {
//var transforms = $(elem).css("transform").match(/[0-9\.]+/g)
var transforms = window.getComputedStyle(elem).transform.match(/[0-9\.]+/g)
if (transforms) {
transforms[0] = val
transforms[3] = val
//$(elem).css("transform", 'matrix('+transforms.join(", ")+")")
elem.style.transform = 'matrix('+transforms.join(", ")+')'
} else {
elem.style.transform = "scale("+val+")"
}
}
}
jQuery.fx.step.scale = function(fx) {
jQuery.cssHooks['scale'].set(fx.elem, fx.now)
};

View file

@ -0,0 +1,35 @@
jQuery.fn.readdClass = (class_name) ->
elem = @
elem.removeClass class_name
setTimeout ( ->
elem.addClass class_name
), 1
return @
jQuery.fn.removeLater = (time = 500) ->
elem = @
setTimeout ( ->
elem.remove()
), time
return @
jQuery.fn.hideLater = (time = 500) ->
elem = @
setTimeout ( ->
elem.css("display", "none")
), time
return @
jQuery.fn.addClassLater = (class_name, time = 5) ->
elem = @
setTimeout ( ->
elem.addClass(class_name)
), time
return @
jQuery.fn.cssLater = (name, val, time = 500) ->
elem = @
setTimeout ( ->
elem.css name, val
), time
return @

View file

@ -0,0 +1,205 @@
/*
* jQuery Easing v1.3 - http://gsgd.co.uk/sandbox/jquery/easing/
*
* Uses the built in easing capabilities added In jQuery 1.1
* to offer multiple easing options
*
* TERMS OF USE - jQuery Easing
*
* Open source under the BSD License.
*
* Copyright © 2008 George McGinley Smith
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* 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.
*
* Neither the name of the author nor the names of contributors may be used to endorse
* or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
* COPYRIGHT OWNER 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.
*
*/
// t: current time, b: begInnIng value, c: change In value, d: duration
jQuery.easing['jswing'] = jQuery.easing['swing'];
jQuery.extend( jQuery.easing,
{
def: 'easeOutQuad',
swing: function (x, t, b, c, d) {
//alert(jQuery.easing.default);
return jQuery.easing[jQuery.easing.def](x, t, b, c, d);
},
easeInQuad: function (x, t, b, c, d) {
return c*(t/=d)*t + b;
},
easeOutQuad: function (x, t, b, c, d) {
return -c *(t/=d)*(t-2) + b;
},
easeInOutQuad: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t + b;
return -c/2 * ((--t)*(t-2) - 1) + b;
},
easeInCubic: function (x, t, b, c, d) {
return c*(t/=d)*t*t + b;
},
easeOutCubic: function (x, t, b, c, d) {
return c*((t=t/d-1)*t*t + 1) + b;
},
easeInOutCubic: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t + b;
return c/2*((t-=2)*t*t + 2) + b;
},
easeInQuart: function (x, t, b, c, d) {
return c*(t/=d)*t*t*t + b;
},
easeOutQuart: function (x, t, b, c, d) {
return -c * ((t=t/d-1)*t*t*t - 1) + b;
},
easeInOutQuart: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t*t + b;
return -c/2 * ((t-=2)*t*t*t - 2) + b;
},
easeInQuint: function (x, t, b, c, d) {
return c*(t/=d)*t*t*t*t + b;
},
easeOutQuint: function (x, t, b, c, d) {
return c*((t=t/d-1)*t*t*t*t + 1) + b;
},
easeInOutQuint: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return c/2*t*t*t*t*t + b;
return c/2*((t-=2)*t*t*t*t + 2) + b;
},
easeInSine: function (x, t, b, c, d) {
return -c * Math.cos(t/d * (Math.PI/2)) + c + b;
},
easeOutSine: function (x, t, b, c, d) {
return c * Math.sin(t/d * (Math.PI/2)) + b;
},
easeInOutSine: function (x, t, b, c, d) {
return -c/2 * (Math.cos(Math.PI*t/d) - 1) + b;
},
easeInExpo: function (x, t, b, c, d) {
return (t==0) ? b : c * Math.pow(2, 10 * (t/d - 1)) + b;
},
easeOutExpo: function (x, t, b, c, d) {
return (t==d) ? b+c : c * (-Math.pow(2, -10 * t/d) + 1) + b;
},
easeInOutExpo: function (x, t, b, c, d) {
if (t==0) return b;
if (t==d) return b+c;
if ((t/=d/2) < 1) return c/2 * Math.pow(2, 10 * (t - 1)) + b;
return c/2 * (-Math.pow(2, -10 * --t) + 2) + b;
},
easeInCirc: function (x, t, b, c, d) {
return -c * (Math.sqrt(1 - (t/=d)*t) - 1) + b;
},
easeOutCirc: function (x, t, b, c, d) {
return c * Math.sqrt(1 - (t=t/d-1)*t) + b;
},
easeInOutCirc: function (x, t, b, c, d) {
if ((t/=d/2) < 1) return -c/2 * (Math.sqrt(1 - t*t) - 1) + b;
return c/2 * (Math.sqrt(1 - (t-=2)*t) + 1) + b;
},
easeInElastic: function (x, t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
return -(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
},
easeOutElastic: function (x, t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d)==1) return b+c; if (!p) p=d*.3;
if (a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
return a*Math.pow(2,-10*t) * Math.sin( (t*d-s)*(2*Math.PI)/p ) + c + b;
},
easeInOutElastic: function (x, t, b, c, d) {
var s=1.70158;var p=0;var a=c;
if (t==0) return b; if ((t/=d/2)==2) return b+c; if (!p) p=d*(.3*1.5);
if (a < Math.abs(c)) { a=c; var s=p/4; }
else var s = p/(2*Math.PI) * Math.asin (c/a);
if (t < 1) return -.5*(a*Math.pow(2,10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )) + b;
return a*Math.pow(2,-10*(t-=1)) * Math.sin( (t*d-s)*(2*Math.PI)/p )*.5 + c + b;
},
easeInBack: function (x, t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c*(t/=d)*t*((s+1)*t - s) + b;
},
easeOutBack: function (x, t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c*((t=t/d-1)*t*((s+1)*t + s) + 1) + b;
},
easeInOutBack: function (x, t, b, c, d, s) {
if (s == undefined) s = 1.70158;
if ((t/=d/2) < 1) return c/2*(t*t*(((s*=(1.525))+1)*t - s)) + b;
return c/2*((t-=2)*t*(((s*=(1.525))+1)*t + s) + 2) + b;
},
easeInBounce: function (x, t, b, c, d) {
return c - jQuery.easing.easeOutBounce (x, d-t, 0, c, d) + b;
},
easeOutBounce: function (x, t, b, c, d) {
if ((t/=d) < (1/2.75)) {
return c*(7.5625*t*t) + b;
} else if (t < (2/2.75)) {
return c*(7.5625*(t-=(1.5/2.75))*t + .75) + b;
} else if (t < (2.5/2.75)) {
return c*(7.5625*(t-=(2.25/2.75))*t + .9375) + b;
} else {
return c*(7.5625*(t-=(2.625/2.75))*t + .984375) + b;
}
},
easeInOutBounce: function (x, t, b, c, d) {
if (t < d/2) return jQuery.easing.easeInBounce (x, t*2, 0, c, d) * .5 + b;
return jQuery.easing.easeOutBounce (x, t*2-d, 0, c, d) * .5 + c*.5 + b;
}
});
/*
*
* TERMS OF USE - EASING EQUATIONS
*
* Open source under the BSD License.
*
* Copyright © 2001 Robert Penner
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
* 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.
*
* Neither the name of the author nor the names of contributors may be used to endorse
* or promote products derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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
* COPYRIGHT OWNER 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.
*
*/

View file

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html>
<head>
<title>{title} - ZeroNet</title>
<meta charset="utf-8">
<meta http-equiv="content-type" content="text/html; charset=utf-8" />
<link rel="stylesheet" href="/uimedia/all.css" />
</head>
<body>
<!-- Fixed button -->
<div class='fixbutton'>
<div class='fixbutton-text'>0</div>
<div class='fixbutton-burger'>&#9776;</div>
<a class='fixbutton-bg' href="/{homepage}"></a>
</div>
<!-- Notifications -->
<div class='notifications'>
<div class='notification template'><span class='notification-icon'>!</span> <span class='body'>Test notification</span><a class="close" href="#Close">&times;</a><div style="clear: both"></div></div>
</div>
<!-- Loadingscreen -->
<div class='loadingscreen'>
<div class='loading-text console'>
</div>
<div class="flipper-container">
<div class="flipper"> <div class="front"></div><div class="back"></div> </div>
</div>
</div>
<!-- Site Iframe -->
<iframe src='/media/{address}/{inner_path}#auth_key={auth_key}' id='inner-iframe' sandbox="allow-forms allow-scripts allow-top-navigation"></iframe>
<!-- Site info -->
<script>address = "{address}"</script>
<script>auth_key = "{auth_key}"</script>
<script>inner_path = "{inner_path}"</script>
<script>permissions = {permissions}</script>
<script>show_loadingscreen = {show_loadingscreen}</script>
<script type="text/javascript" src="/uimedia/all.js" asyc></script>
</body>
</html>