class Wrapper constructor: (ws_url) -> @log "Created!" @loading = new Loading() @notifications = new Notifications($(".notifications")) @fixbutton = new Fixbutton() 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 @event_site_info = $.Deferred() # Event when site_info received @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 @address = null window.onload = @onLoad # On iframe loaded $(window).on "hashchange", => # On hash change @log "Hashchange", window.location.hash if window.location.hash src = $("#inner-iframe").attr("src").replace(/#.*/, "")+window.location.hash $("#inner-iframe").attr("src", src) $("#inner-iframe").focus() # 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 type = message.params[0] id = "notification-#{message.id}" if "-" in message.params[0] # - in first param: message id definied [id, type] = message.params[0].split("-") @notifications.add(id, type, message.params[1], message.params[2]) else if cmd == "prompt" # Prompt input @displayPrompt message.params[0], message.params[1], message.params[2], (res) => @ws.response message.id, res else if cmd == "setSiteInfo" @sendInner message # Pass to inner frame if message.params.address == @address # Current page @setSiteInfo message.params else if cmd == "error" @notifications.add("notification-#{message.id}", "error", message.params, 0) else if cmd == "updating" # Close connection @ws.ws.close() @ws.onCloseWebsocket(null, 4000) 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 if @ws.ws.readyState == 1 and not @wrapperWsInited # If ws already opened @sendInner {"cmd": "wrapperOpenedWebsocket"} @wrapperWsInited = true else if cmd == "innerLoaded" if window.location.hash $("#inner-iframe")[0].src += window.location.hash # Hash tag @log "Added hash to location", $("#inner-iframe")[0].src else if cmd == "wrapperNotification" # Display notification @actionNotification(message) else if cmd == "wrapperConfirm" # Display confirm message @actionConfirm(message) else if cmd == "wrapperPrompt" # Prompt input @actionPrompt(message) else if cmd == "wrapperSetViewport" # Set the viewport @actionSetViewport(message) else if cmd == "wrapperReload" # Reload current page @actionReload(message) else if cmd == "wrapperGetLocalStorage" @actionGetLocalStorage(message) else if cmd == "wrapperSetLocalStorage" @actionSetLocalStorage(message) else if cmd == "wrapperPushState" query = @toRelativeQuery(message.params[2]) window.history.pushState(message.params[0], message.params[1], query) else if cmd == "wrapperReplaceState" query = @toRelativeQuery(message.params[2]) window.history.replaceState(message.params[0], message.params[1], query) else # Send to websocket if message.id < 1000000 @ws.send(message) # Pass message to websocket else @log "Invalid inner message id" toRelativeQuery: (query) -> back = window.location.pathname if back.slice(-1) != "/" back += "/" if query.replace("?", "") back += "?"+query.replace("?", "") return back # - Actions - actionNotification: (message) -> message.params = @toHtmlSafe(message.params) # Escape html body = $(""+message.params[1]+"") @notifications.add("notification-#{message.id}", message.params[0], body, message.params[2]) displayConfirm: (message, caption, cb) -> body = $(""+message+"") button = $("#{caption}") # Add confirm button button.on "click", cb body.append(button) @notifications.add("notification-#{caption}", "ask", body) button.focus() $(".notification").scrollLeft(0) actionConfirm: (message, cb=false) -> message.params = @toHtmlSafe(message.params) # Escape html if message.params[1] then caption = message.params[1] else caption = "ok" @displayConfirm message.params[0], caption, => @sendInner {"cmd": "response", "to": message.id, "result": "boom"} # Response to confirm return false displayPrompt: (message, type, caption, cb) -> body = $(""+message+"") input = $("") # Add input input.on "keyup", (e) => # Send on enter if e.keyCode == 13 button.trigger "click" # Response to confirm body.append(input) button = $("#{caption}") # Add confirm button button.on "click", => # Response on button click cb input.val() return false body.append(button) @notifications.add("notification-#{message.id}", "ask", body) input.focus() $(".notification").scrollLeft(0) actionPrompt: (message) -> message.params = @toHtmlSafe(message.params) # Escape html if message.params[1] then type = message.params[1] else type = "text" caption = "OK" @displayPrompt message.params[0], type, caption, (res) => @sendInner {"cmd": "response", "to": message.id, "result": res} # Response to confirm actionSetViewport: (message) -> @log "actionSetViewport", message if $("#viewport").length > 0 $("#viewport").attr("content", @toHtmlSafe message.params) else $('').attr("content", @toHtmlSafe message.params).appendTo("head") reload: (url_post="") -> if url_post if window.location.toString().indexOf("?") > 0 window.location += "&"+url_post else window.location += "?"+url_post else window.location.reload() actionGetLocalStorage: (message) -> $.when(@event_site_info).done => data = localStorage.getItem "site.#{@site_info.address}.#{@site_info.auth_address}" if not data # Migrate from non auth_address based local storage data = localStorage.getItem "site.#{@site_info.address}" if data localStorage.setItem "site.#{@site_info.address}.#{@site_info.auth_address}", data localStorage.removeItem "site.#{@site_info.address}" @log "Migrated LocalStorage from global to auth_address based" if data then data = JSON.parse(data) @sendInner {"cmd": "response", "to": message.id, "result": data} actionSetLocalStorage: (message) -> back = localStorage.setItem "site.#{@site_info.address}.#{@site_info.auth_address}", JSON.stringify(message.params) # EOF actions onOpenWebsocket: (e) => @ws.cmd "channelJoin", {"channel": "siteChanged"} # Get info on modifications 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 UiServer Websocket 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 and e.code == 1000 and e.wasClean == false # 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 UiServer Websocket was lost. Reconnecting...") ), 1000 # Iframe loaded 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() else if @site_info and @site_info.content?.title? window.document.title = @site_info.content.title+" - ZeroNet" @log "Setting title to", window.document.title # Send message to innerframe sendInner: (message) -> @inner.postMessage(message, '*') # Get site info from UiServer reloadSiteInfo: -> if @loading.screen_visible # Loading screen visible params = {"file_status": window.file_inner_path} # Query the current required file status else params = {} @ws.cmd "siteInfo", params, (site_info) => @address = site_info.address @setSiteInfo site_info if site_info.settings.size > site_info.size_limit*1024*1024 # Site size too large and not displaying it yet if @loading.screen_visible @loading.showTooLarge(site_info) else @displayConfirm "Site is larger than allowed: #{(site_info.settings.size/1024/1024).toFixed(1)}MB/#{site_info.size_limit}MB", "Set limit to #{site_info.next_size_limit}MB", => @ws.cmd "siteSetLimit", [site_info.next_size_limit], (res) => @notifications.add("size_limit", "done", res, 5000) if site_info.content 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 @loading.printLine("#{site_info.bad_files} 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.file_inner_path # File downloaded we currently on @loading.hideScreen() if not @site_info then @reloadSiteInfo() if site_info.content window.document.title = site_info.content.title+" - ZeroNet" @log "Required file done, setting title to", window.document.title if not $(".loadingscreen").length # Loading screen already removed (loaded +2sec) @notifications.add("modified", "info", "New version of this page has just released.
Reload to see the modified content.") # File failed downloading else if site_info.event[0] == "file_failed" @site_error = site_info.event[1] if site_info.settings.size > site_info.size_limit*1024*1024 # Site size too large and not displaying it yet @loading.showTooLarge(site_info) else @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" if not @site_info and not @loading.screen_visible and $("#inner-iframe").attr("src").replace("?wrapper=False", "").replace(/\?wrapper_nonce=[A-Za-z0-9]+/, "").indexOf("?") == -1 # First site info and we are on mainpage (does not have other parameter thatn wrapper) if site_info.size_limit*1.1 < site_info.next_size_limit # Need upgrade soon @displayConfirm "Running out of size limit (#{(site_info.settings.size/1024/1024).toFixed(1)}MB/#{site_info.size_limit}MB)", "Set limit to #{site_info.next_size_limit}MB", => @ws.cmd "siteSetLimit", [site_info.next_size_limit], (res) => @notifications.add("size_limit", "done", res, 5000) return false if @loading.screen_visible and @inner_loaded and site_info.settings.size < site_info.size_limit*1024*1024 and site_info.settings.size > 0 # Loading screen still visible, but inner loaded @loading.hideScreen() if site_info.tasks > 0 and site_info.started_task_num > 0 @loading.setProgress 1-(site_info.tasks / site_info.started_task_num) else @loading.hideProgress() @site_info = site_info @event_site_info.resolve() toHtmlSafe: (values) -> if values not instanceof Array then values = [values] # Convert to array if its not for value, i in values value = String(value).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"') # Escape value = value.replace(/<([\/]{0,1}(br|b|u|i))>/g, "<$1>") # Unescape b, i, u, br tags values[i] = value return values setSizeLimit: (size_limit, reload=true) => @ws.cmd "siteSetLimit", [size_limit], (res) => @loading.printLine res @inner_loaded = false # Inner frame not loaded, just a 404 page displayed if reload src = $("iframe").attr("src") $("iframe").attr "src", "" $("iframe").attr "src", src return false isProxyRequest: -> return window.location.pathname == "/" gotoSite: (elem) => href = $(elem).attr("href") if @isProxyRequest() # Fix for proxy request $(elem).attr("href", "http://zero#{href}") log: (args...) -> console.log "[Wrapper]", args... origin = window.server_url or window.location.origin if origin.indexOf("https:") == 0 proto = { ws: 'wss', http: 'https' } else proto = { ws: 'ws', http: 'http' } ws_url = proto.ws + ":" + origin.replace(proto.http+":", "") + "/Websocket?wrapper_key=" + window.wrapper_key window.wrapper = new Wrapper(ws_url)