[cleanup] remove coffee script files
This commit is contained in:
parent
fee2a4f2c9
commit
574928e625
50 changed files with 0 additions and 4951 deletions
|
@ -1,15 +0,0 @@
|
|||
window.BINARY_EXTENSIONS = [
|
||||
"3dm", "3ds", "3g2", "3gp", "7z", "a", "aac", "adp", "ai", "aif", "aiff", "alz", "ape", "apk", "appimage", "ar", "arj", "asc", "asf", "au", "avi", "bak",
|
||||
"baml", "bh", "bin", "bk", "bmp", "btif", "bz2", "bzip2", "cab", "caf", "cgm", "class", "cmx", "cpio", "cr2", "cur", "dat", "dcm", "deb", "dex", "djvu",
|
||||
"dll", "dmg", "dng", "doc", "docm", "docx", "dot", "dotm", "dra", "DS_Store", "dsk", "dts", "dtshd", "dvb", "dwg", "dxf", "ecelp4800", "ecelp7470",
|
||||
"ecelp9600", "egg", "eol", "eot", "epub", "exe", "f4v", "fbs", "fh", "fla", "flac", "flatpak", "fli", "flv", "fpx", "fst", "fvt", "g3", "gh", "gif",
|
||||
"gpg", "graffle", "gz", "gzip", "h261", "h263", "h264", "icns", "ico", "ief", "img", "ipa", "iso", "jar", "jpeg", "jpg", "jpgv", "jpm", "jxr", "key",
|
||||
"ktx", "lha", "lib", "lvp", "lz", "lzh", "lzma", "lzo", "m3u", "m4a", "m4v", "mar", "mdi", "mht", "mid", "midi", "mj2", "mka", "mkv", "mmr", "mng",
|
||||
"mobi", "mov", "movie", "mp3", "mp4", "mp4a", "mpeg", "mpg", "mpga", "msgpack", "mxu", "nef", "npx", "numbers", "nupkg", "o", "oga", "ogg", "ogv",
|
||||
"otf", "pages", "pbm", "pcx", "pdb", "pdf", "pea", "pgm", "pic", "png", "pnm", "pot", "potm", "potx", "ppa", "ppam", "ppm", "pps", "ppsm", "ppsx",
|
||||
"ppt", "pptm", "pptx", "psd", "pya", "pyc", "pyo", "pyv", "qt", "rar", "ras", "raw", "resources", "rgb", "rip", "rlc", "rmf", "rmvb", "rpm", "rtf",
|
||||
"rz", "s3m", "s7z", "scpt", "sgi", "shar", "sig", "sil", "sketch", "slk", "smv", "snap", "snk", "so", "stl", "sub", "suo", "swf", "tar", "tbz2", "tbz",
|
||||
"tga", "tgz", "thmx", "tif", "tiff", "tlz", "ttc", "ttf", "txz", "udf", "uvh", "uvi", "uvm", "uvp", "uvs", "uvu", "viv", "vob", "war", "wav", "wax",
|
||||
"wbmp", "wdp", "weba", "webm", "webp", "whl", "wim", "wm", "wma", "wmv", "wmx", "woff2", "woff", "wrm", "wvx", "xbm", "xif", "xla", "xlam", "xls",
|
||||
"xlsb", "xlsm", "xlsx", "xlt", "xltm", "xltx", "xm", "xmind", "xpi", "xpm", "xwd", "xz", "z", "zip", "zipx"
|
||||
]
|
|
@ -1,179 +0,0 @@
|
|||
class FileEditor extends Class
|
||||
constructor: (@inner_path) ->
|
||||
@need_update = true
|
||||
@on_loaded = new Promise()
|
||||
@is_loading = false
|
||||
@content = ""
|
||||
@node_cm = null
|
||||
@cm = null
|
||||
@error = null
|
||||
@is_loaded = false
|
||||
@is_modified = false
|
||||
@is_saving = false
|
||||
@mode = "Loading"
|
||||
|
||||
update: ->
|
||||
is_required = Page.url_params.get("edit_mode") != "new"
|
||||
|
||||
Page.cmd "fileGet", {inner_path: @inner_path, required: is_required}, (res) =>
|
||||
if res?.error
|
||||
@error = res.error
|
||||
@content = res.error
|
||||
@log "Error loading: #{@error}"
|
||||
else
|
||||
if res
|
||||
@content = res
|
||||
else
|
||||
@content = ""
|
||||
@mode = "Create"
|
||||
if not @content
|
||||
@cm.getDoc().clearHistory()
|
||||
@cm.setValue(@content)
|
||||
if not @error
|
||||
@is_loaded = true
|
||||
Page.projector.scheduleRender()
|
||||
|
||||
isModified: =>
|
||||
return @content != @cm.getValue()
|
||||
|
||||
storeCmNode: (node) =>
|
||||
@node_cm = node
|
||||
|
||||
getMode: (inner_path) ->
|
||||
ext = inner_path.split(".").pop()
|
||||
types = {
|
||||
"py": "python",
|
||||
"json": "application/json",
|
||||
"js": "javascript",
|
||||
"coffee": "coffeescript",
|
||||
"html": "htmlmixed",
|
||||
"htm": "htmlmixed",
|
||||
"php": "htmlmixed",
|
||||
"rs": "rust",
|
||||
"css": "css",
|
||||
"md": "markdown",
|
||||
"xml": "xml",
|
||||
"svg": "xml"
|
||||
}
|
||||
return types[ext]
|
||||
|
||||
foldJson: (from, to) =>
|
||||
@log "foldJson", from, to
|
||||
# Get open / close token
|
||||
startToken = '{'
|
||||
endToken = '}'
|
||||
prevLine = @cm.getLine(from.line)
|
||||
if prevLine.lastIndexOf('[') > prevLine.lastIndexOf('{')
|
||||
startToken = '['
|
||||
endToken = ']'
|
||||
|
||||
# Get json content
|
||||
internal = @cm.getRange(from, to)
|
||||
toParse = startToken + internal + endToken
|
||||
|
||||
#Get key count
|
||||
try
|
||||
parsed = JSON.parse(toParse)
|
||||
count = Object.keys(parsed).length
|
||||
catch e
|
||||
null
|
||||
|
||||
return if count then "\u21A4#{count}\u21A6" else "\u2194"
|
||||
|
||||
createCodeMirror: ->
|
||||
mode = @getMode(@inner_path)
|
||||
@log "Creating CodeMirror", @inner_path, mode
|
||||
options = {
|
||||
value: "Loading...",
|
||||
mode: mode,
|
||||
lineNumbers: true,
|
||||
styleActiveLine: true,
|
||||
matchBrackets: true,
|
||||
keyMap: "sublime",
|
||||
theme: "mdn-like",
|
||||
extraKeys: {"Ctrl-Space": "autocomplete"},
|
||||
foldGutter: true,
|
||||
gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"]
|
||||
|
||||
}
|
||||
if mode == "application/json"
|
||||
options.gutters.unshift("CodeMirror-lint-markers")
|
||||
options.lint = true
|
||||
options.foldOptions = { widget: @foldJson }
|
||||
|
||||
@cm = CodeMirror(@node_cm, options)
|
||||
@cm.on "changes", (changes) =>
|
||||
if @is_loaded and not @is_modified
|
||||
@is_modified = true
|
||||
Page.projector.scheduleRender()
|
||||
|
||||
|
||||
loadEditor: ->
|
||||
if not @is_loading
|
||||
document.getElementsByTagName("head")[0].insertAdjacentHTML(
|
||||
"beforeend",
|
||||
"""<link rel="stylesheet" href="codemirror/all.css" />"""
|
||||
)
|
||||
script = document.createElement('script')
|
||||
script.src = "codemirror/all.js"
|
||||
script.onload = =>
|
||||
@createCodeMirror()
|
||||
@on_loaded.resolve()
|
||||
document.head.appendChild(script)
|
||||
return @on_loaded
|
||||
|
||||
handleSidebarButtonClick: =>
|
||||
Page.is_sidebar_closed = not Page.is_sidebar_closed
|
||||
return false
|
||||
|
||||
handleSaveClick: =>
|
||||
num_errors = (mark for mark in Page.file_editor.cm.getAllMarks() when mark.className == "CodeMirror-lint-mark-error").length
|
||||
if num_errors > 0
|
||||
Page.cmd "wrapperConfirm", ["<b>Warning:</b> The file looks invalid.", "Save anyway"], @save
|
||||
else
|
||||
@save()
|
||||
return false
|
||||
|
||||
save: =>
|
||||
Page.projector.scheduleRender()
|
||||
@is_saving = true
|
||||
Page.cmd "fileWrite", [@inner_path, Text.fileEncode(@cm.getValue())], (res) =>
|
||||
@is_saving = false
|
||||
if res.error
|
||||
Page.cmd "wrapperNotification", ["error", "Error saving #{res.error}"]
|
||||
else
|
||||
@is_save_done = true
|
||||
setTimeout (() =>
|
||||
@is_save_done = false
|
||||
Page.projector.scheduleRender()
|
||||
), 2000
|
||||
@content = @cm.getValue()
|
||||
@is_modified = false
|
||||
if @mode == "Create"
|
||||
@mode = "Edit"
|
||||
Page.file_list.need_update = true
|
||||
Page.projector.scheduleRender()
|
||||
|
||||
render: ->
|
||||
if @need_update
|
||||
@loadEditor().then =>
|
||||
@update()
|
||||
@need_update = false
|
||||
h("div.editor", {afterCreate: @storeCmNode, classes: {error: @error, loaded: @is_loaded}}, [
|
||||
h("a.sidebar-button", {href: "#Sidebar", onclick: @handleSidebarButtonClick}, h("span", "\u2039")),
|
||||
h("div.editor-head", [
|
||||
if @mode in ["Edit", "Create"]
|
||||
h("a.save.button",
|
||||
{href: "#Save", classes: {loading: @is_saving, done: @is_save_done, disabled: not @is_modified}, onclick: @handleSaveClick},
|
||||
if @is_save_done then "Save: done!" else "Save"
|
||||
)
|
||||
h("span.title", @mode, ": ", @inner_path)
|
||||
]),
|
||||
if @error
|
||||
h("div.error-message",
|
||||
h("h2", "Unable to load the file: #{@error}")
|
||||
h("a", {href: Page.file_list.getHref(@inner_path)}, "View in browser")
|
||||
)
|
||||
])
|
||||
|
||||
window.FileEditor = FileEditor
|
|
@ -1,194 +0,0 @@
|
|||
class FileItemList extends Class
|
||||
constructor: (@inner_path) ->
|
||||
@items = []
|
||||
@updating = false
|
||||
@files_modified = {}
|
||||
@dirs_modified = {}
|
||||
@files_added = {}
|
||||
@dirs_added = {}
|
||||
@files_optional = {}
|
||||
@items_by_name = {}
|
||||
|
||||
# Update item list
|
||||
update: (cb) ->
|
||||
@updating = true
|
||||
@logStart("Updating dirlist")
|
||||
Page.cmd "dirList", {inner_path: @inner_path, stats: true}, (res) =>
|
||||
if res.error
|
||||
@error = res.error
|
||||
else
|
||||
@error = null
|
||||
pattern_ignore = RegExp("^" + Page.site_info.content?.ignore)
|
||||
|
||||
@items.splice(0, @items.length) # Remove all items
|
||||
|
||||
@items_by_name = {}
|
||||
for row in res
|
||||
row.type = @getFileType(row)
|
||||
row.inner_path = @inner_path + row.name
|
||||
if Page.site_info.content?.ignore and row.inner_path.match(pattern_ignore)
|
||||
row.ignored = true
|
||||
@items.push(row)
|
||||
@items_by_name[row.name] = row
|
||||
|
||||
@sort()
|
||||
|
||||
if Page.site_info?.settings?.own
|
||||
@updateAddedFiles()
|
||||
|
||||
@updateOptionalFiles =>
|
||||
@updating = false
|
||||
cb?()
|
||||
@logEnd("Updating dirlist", @inner_path)
|
||||
Page.projector.scheduleRender()
|
||||
|
||||
@updateModifiedFiles =>
|
||||
Page.projector.scheduleRender()
|
||||
|
||||
|
||||
updateModifiedFiles: (cb) =>
|
||||
# Add modified attribute to changed files
|
||||
Page.cmd "siteListModifiedFiles", [], (res) =>
|
||||
@files_modified = {}
|
||||
@dirs_modified = {}
|
||||
for inner_path in res.modified_files
|
||||
@files_modified[inner_path] = true
|
||||
dir_inner_path = ""
|
||||
dir_parts = inner_path.split("/")
|
||||
for dir_part in dir_parts[..-2]
|
||||
if dir_inner_path
|
||||
dir_inner_path += "/#{dir_part}"
|
||||
else
|
||||
dir_inner_path = dir_part
|
||||
@dirs_modified[dir_inner_path] = true
|
||||
|
||||
cb?()
|
||||
|
||||
# Update newly added items list since last sign
|
||||
updateAddedFiles: =>
|
||||
Page.cmd "fileGet", "content.json", (res) =>
|
||||
if not res
|
||||
return false
|
||||
|
||||
content = JSON.parse(res)
|
||||
|
||||
# Check new files
|
||||
if not content.files?
|
||||
return false
|
||||
|
||||
@files_added = {}
|
||||
|
||||
for file in @items
|
||||
if file.name == "content.json" or file.is_dir
|
||||
continue
|
||||
if not content.files[@inner_path + file.name]
|
||||
@files_added[@inner_path + file.name] = true
|
||||
|
||||
# Check new dirs
|
||||
@dirs_added = {}
|
||||
|
||||
dirs_content = {}
|
||||
for file_name of Object.assign({}, content.files, content.files_optional)
|
||||
if not file_name.startsWith(@inner_path)
|
||||
continue
|
||||
|
||||
pattern = new RegExp("#{@inner_path}(.*?)/")
|
||||
match = file_name.match(pattern)
|
||||
|
||||
if not match
|
||||
continue
|
||||
|
||||
dirs_content[match[1]] = true
|
||||
|
||||
for file in @items
|
||||
if not file.is_dir
|
||||
continue
|
||||
if not dirs_content[file.name]
|
||||
@dirs_added[@inner_path + file.name] = true
|
||||
|
||||
# Update optional files list
|
||||
updateOptionalFiles: (cb) =>
|
||||
Page.cmd "optionalFileList", {filter: ""}, (res) =>
|
||||
@files_optional = {}
|
||||
for optional_file in res
|
||||
@files_optional[optional_file.inner_path] = optional_file
|
||||
|
||||
@addOptionalFilesToItems()
|
||||
|
||||
cb?()
|
||||
|
||||
# Add optional files to item list
|
||||
addOptionalFilesToItems: =>
|
||||
is_added = false
|
||||
for inner_path, optional_file of @files_optional
|
||||
if optional_file.inner_path.startsWith(@inner_path)
|
||||
if @getDirectory(optional_file.inner_path) == @inner_path
|
||||
# Add optional file to list
|
||||
file_name = @getFileName(optional_file.inner_path)
|
||||
if not @items_by_name[file_name]
|
||||
row = {
|
||||
"name": file_name, "type": "file", "optional_empty": true,
|
||||
"size": optional_file.size, "is_dir": false, "inner_path": optional_file.inner_path
|
||||
}
|
||||
@items.push(row)
|
||||
@items_by_name[file_name] = row
|
||||
is_added = true
|
||||
else
|
||||
# Add optional dir to list
|
||||
dir_name = optional_file.inner_path.replace(@inner_path, "").match(/(.*?)\//, "")?[1]
|
||||
if dir_name and not @items_by_name[dir_name]
|
||||
row = {
|
||||
"name": dir_name, "type": "dir", "optional_empty": true,
|
||||
"size": 0, "is_dir": true, "inner_path": optional_file.inner_path
|
||||
}
|
||||
@items.push(row)
|
||||
@items_by_name[dir_name] = row
|
||||
is_added = true
|
||||
|
||||
if is_added
|
||||
@sort()
|
||||
|
||||
getFileType: (file) =>
|
||||
if file.is_dir
|
||||
return "dir"
|
||||
else
|
||||
return "unknown"
|
||||
|
||||
getDirectory: (inner_path) ->
|
||||
if inner_path.indexOf("/") != -1
|
||||
return inner_path.replace(/^(.*\/)(.*?)$/, "$1")
|
||||
else
|
||||
return ""
|
||||
|
||||
getFileName: (inner_path) ->
|
||||
return inner_path.replace(/^(.*\/)(.*?)$/, "$2")
|
||||
|
||||
|
||||
isModified: (inner_path) =>
|
||||
return @files_modified[inner_path] or @dirs_modified[inner_path]
|
||||
|
||||
isAdded: (inner_path) =>
|
||||
return @files_added[inner_path] or @dirs_added[inner_path]
|
||||
|
||||
hasPermissionDelete: (file) =>
|
||||
if file.type in ["dir", "parent"]
|
||||
return false
|
||||
|
||||
if file.inner_path == "content.json"
|
||||
return false
|
||||
|
||||
optional_info = @getOptionalInfo(file.inner_path)
|
||||
if optional_info and optional_info.downloaded_percent > 0
|
||||
return true
|
||||
else
|
||||
return Page.site_info?.settings?.own
|
||||
|
||||
getOptionalInfo: (inner_path) =>
|
||||
return @files_optional[inner_path]
|
||||
|
||||
sort: =>
|
||||
@items.sort (a, b) ->
|
||||
return (b.is_dir - a.is_dir) || a.name.localeCompare(b.name)
|
||||
|
||||
|
||||
window.FileItemList = FileItemList
|
|
@ -1,268 +0,0 @@
|
|||
class FileList extends Class
|
||||
constructor: (@site, @inner_path, @is_owner=false) ->
|
||||
@need_update = true
|
||||
@error = null
|
||||
@url_root = "/list/" + @site + "/"
|
||||
if @inner_path
|
||||
@inner_path += "/"
|
||||
@url_root += @inner_path
|
||||
@log("inited", @url_root)
|
||||
@item_list = new FileItemList(@inner_path)
|
||||
@item_list.items = @item_list.items
|
||||
@menu_create = new Menu()
|
||||
|
||||
@select_action = null
|
||||
@selected = {}
|
||||
@selected_items_num = 0
|
||||
@selected_items_size = 0
|
||||
@selected_optional_empty_num = 0
|
||||
|
||||
isSelectedAll: ->
|
||||
false
|
||||
|
||||
update: =>
|
||||
@item_list.update =>
|
||||
document.body.classList.add("loaded")
|
||||
|
||||
getHref: (inner_path) =>
|
||||
return "/" + @site + "/" + inner_path
|
||||
|
||||
getListHref: (inner_path) =>
|
||||
return "/list/" + @site + "/" + inner_path
|
||||
|
||||
getEditHref: (inner_path, mode=null) =>
|
||||
href = @url_root + "?file=" + inner_path
|
||||
if mode
|
||||
href += "&edit_mode=#{mode}"
|
||||
return href
|
||||
|
||||
checkSelectedItems: =>
|
||||
@selected_items_num = 0
|
||||
@selected_items_size = 0
|
||||
@selected_optional_empty_num = 0
|
||||
for item in @item_list.items
|
||||
if @selected[item.inner_path]
|
||||
@selected_items_num += 1
|
||||
@selected_items_size += item.size
|
||||
optional_info = @item_list.getOptionalInfo(item.inner_path)
|
||||
if optional_info and not optional_info.downloaded_percent > 0
|
||||
@selected_optional_empty_num += 1
|
||||
|
||||
handleMenuCreateClick: =>
|
||||
@menu_create.items = []
|
||||
@menu_create.items.push ["File", @handleNewFileClick]
|
||||
@menu_create.items.push ["Directory", @handleNewDirectoryClick]
|
||||
@menu_create.toggle()
|
||||
return false
|
||||
|
||||
handleNewFileClick: =>
|
||||
Page.cmd "wrapperPrompt", "New file name:", (file_name) =>
|
||||
window.top.location.href = @getEditHref(@inner_path + file_name, "new")
|
||||
return false
|
||||
|
||||
handleNewDirectoryClick: =>
|
||||
Page.cmd "wrapperPrompt", "New directory name:", (res) =>
|
||||
alert("directory name #{res}")
|
||||
return false
|
||||
|
||||
handleSelectClick: (e) =>
|
||||
return false
|
||||
|
||||
handleSelectEnd: (e) =>
|
||||
document.body.removeEventListener('mouseup', @handleSelectEnd)
|
||||
@select_action = null
|
||||
|
||||
handleSelectMousedown: (e) =>
|
||||
inner_path = e.currentTarget.attributes.inner_path.value
|
||||
if @selected[inner_path]
|
||||
delete @selected[inner_path]
|
||||
@select_action = "deselect"
|
||||
else
|
||||
@selected[inner_path] = true
|
||||
@select_action = "select"
|
||||
@checkSelectedItems()
|
||||
document.body.addEventListener('mouseup', @handleSelectEnd)
|
||||
e.stopPropagation()
|
||||
Page.projector.scheduleRender()
|
||||
return false
|
||||
|
||||
handleRowMouseenter: (e) =>
|
||||
if e.buttons and @select_action
|
||||
inner_path = e.target.attributes.inner_path.value
|
||||
if @select_action == "select"
|
||||
@selected[inner_path] = true
|
||||
else
|
||||
delete @selected[inner_path]
|
||||
@checkSelectedItems()
|
||||
Page.projector.scheduleRender()
|
||||
return false
|
||||
|
||||
handleSelectbarCancel: =>
|
||||
@selected = {}
|
||||
@checkSelectedItems()
|
||||
Page.projector.scheduleRender()
|
||||
return false
|
||||
|
||||
handleSelectbarDelete: (e, remove_optional=false) =>
|
||||
for inner_path of @selected
|
||||
optional_info = @item_list.getOptionalInfo(inner_path)
|
||||
delete @selected[inner_path]
|
||||
if optional_info and not remove_optional
|
||||
Page.cmd "optionalFileDelete", inner_path
|
||||
else
|
||||
Page.cmd "fileDelete", inner_path
|
||||
@need_update = true
|
||||
Page.projector.scheduleRender()
|
||||
@checkSelectedItems()
|
||||
return false
|
||||
|
||||
handleSelectbarRemoveOptional: (e) =>
|
||||
return @handleSelectbarDelete(e, true)
|
||||
|
||||
renderSelectbar: =>
|
||||
h("div.selectbar", {classes: {visible: @selected_items_num > 0}}, [
|
||||
"Selected:",
|
||||
h("span.info", [
|
||||
h("span.num", "#{@selected_items_num} files"),
|
||||
h("span.size", "(#{Text.formatSize(@selected_items_size)})"),
|
||||
])
|
||||
h("div.actions", [
|
||||
if @selected_optional_empty_num > 0
|
||||
h("a.action.delete.remove_optional", {href: "#", onclick: @handleSelectbarRemoveOptional}, "Delete and remove optional")
|
||||
else
|
||||
h("a.action.delete", {href: "#", onclick: @handleSelectbarDelete}, "Delete")
|
||||
])
|
||||
h("a.cancel.link", {href: "#", onclick: @handleSelectbarCancel}, "Cancel")
|
||||
])
|
||||
|
||||
renderHead: =>
|
||||
parent_links = []
|
||||
inner_path_parent = ""
|
||||
for parent_dir in @inner_path.split("/")
|
||||
if not parent_dir
|
||||
continue
|
||||
if inner_path_parent
|
||||
inner_path_parent += "/"
|
||||
inner_path_parent += "#{parent_dir}"
|
||||
parent_links.push(
|
||||
[" / ", h("a", {href: @getListHref(inner_path_parent)}, parent_dir)]
|
||||
)
|
||||
return h("div.tr.thead", h("div.td.full",
|
||||
h("a", {href: @getListHref("")}, "root"),
|
||||
parent_links
|
||||
))
|
||||
|
||||
renderItemCheckbox: (item) =>
|
||||
if not @item_list.hasPermissionDelete(item)
|
||||
return [" "]
|
||||
|
||||
return h("a.checkbox-outer", {
|
||||
href: "#Select",
|
||||
onmousedown: @handleSelectMousedown,
|
||||
onclick: @handleSelectClick,
|
||||
inner_path: item.inner_path
|
||||
}, h("span.checkbox"))
|
||||
|
||||
renderItem: (item) =>
|
||||
if item.type == "parent"
|
||||
href = @url_root.replace(/^(.*)\/.{2,255}?$/, "$1/")
|
||||
else if item.type == "dir"
|
||||
href = @url_root + item.name
|
||||
else
|
||||
href = @url_root.replace(/^\/list\//, "/") + item.name
|
||||
|
||||
inner_path = @inner_path + item.name
|
||||
href_edit = @getEditHref(inner_path)
|
||||
is_dir = item.type in ["dir", "parent"]
|
||||
ext = item.name.split(".").pop()
|
||||
|
||||
is_editing = inner_path == Page.file_editor?.inner_path
|
||||
is_editable = not is_dir and item.size < 1024 * 1024 and ext not in window.BINARY_EXTENSIONS
|
||||
is_modified = @item_list.isModified(inner_path)
|
||||
is_added = @item_list.isAdded(inner_path)
|
||||
optional_info = @item_list.getOptionalInfo(inner_path)
|
||||
|
||||
style = ""
|
||||
title = ""
|
||||
|
||||
if optional_info
|
||||
downloaded_percent = optional_info.downloaded_percent
|
||||
if not downloaded_percent
|
||||
downloaded_percent = 0
|
||||
style += "background: linear-gradient(90deg, #fff6dd, #{downloaded_percent}%, white, #{downloaded_percent}%, white);"
|
||||
is_added = false
|
||||
|
||||
if item.ignored
|
||||
is_added = false
|
||||
|
||||
if is_modified then title += " (modified)"
|
||||
if is_added then title += " (new)"
|
||||
if optional_info or item.optional_empty then title += " (optional)"
|
||||
if item.ignored then title += " (ignored from content.json)"
|
||||
|
||||
classes = {
|
||||
"type-#{item.type}": true, editing: is_editing, nobuttons: not is_editable, selected: @selected[inner_path],
|
||||
modified: is_modified, added: is_added, ignored: item.ignored, optional: optional_info, optional_empty: item.optional_empty
|
||||
}
|
||||
|
||||
h("div.tr", {key: item.name, classes: classes, style: style, onmouseenter: @handleRowMouseenter, inner_path: inner_path}, [
|
||||
h("div.td.pre", {title: title},
|
||||
@renderItemCheckbox(item)
|
||||
),
|
||||
h("div.td.name", h("a.link", {href: href}, item.name))
|
||||
h("div.td.buttons", if is_editable then h("a.edit", {href: href_edit}, if Page.site_info.settings.own then "Edit" else "View"))
|
||||
h("div.td.size", if is_dir then "[DIR]" else Text.formatSize(item.size))
|
||||
])
|
||||
|
||||
|
||||
renderItems: =>
|
||||
return [
|
||||
if @item_list.error and not @item_list.items.length and not @item_list.updating then [
|
||||
h("div.tr", {key: "error"}, h("div.td.full.error", @item_list.error))
|
||||
],
|
||||
if @inner_path then @renderItem({"name": "..", type: "parent", size: 0})
|
||||
@item_list.items.map @renderItem
|
||||
]
|
||||
|
||||
renderFoot: =>
|
||||
files = (item for item in @item_list.items when item.type not in ["parent", "dir"])
|
||||
dirs = (item for item in @item_list.items when item.type == "dir")
|
||||
if files.length
|
||||
total_size = (item.size for file in files).reduce (a, b) -> a + b
|
||||
else
|
||||
total_size = 0
|
||||
|
||||
foot_text = "Total: "
|
||||
foot_text += "#{dirs.length} dir, #{files.length} file in #{Text.formatSize(total_size)}"
|
||||
|
||||
return [
|
||||
if dirs.length or files.length or Page.site_info?.settings?.own
|
||||
h("div.tr.foot-info.foot", h("div.td.full", [
|
||||
if @item_list.updating
|
||||
"Updating file list..."
|
||||
else
|
||||
if dirs.length or files.length then foot_text
|
||||
if Page.site_info?.settings?.own
|
||||
h("div.create", [
|
||||
h("a.link", {href: "#Create+new+file", onclick: @handleNewFileClick}, "+ New")
|
||||
@menu_create.render()
|
||||
])
|
||||
]))
|
||||
]
|
||||
|
||||
render: =>
|
||||
if @need_update
|
||||
@update()
|
||||
@need_update = false
|
||||
|
||||
if not @item_list.items
|
||||
return []
|
||||
|
||||
return h("div.files", [
|
||||
@renderSelectbar(),
|
||||
@renderHead(),
|
||||
h("div.tbody", @renderItems()),
|
||||
@renderFoot()
|
||||
])
|
||||
|
||||
window.FileList = FileList
|
|
@ -1,79 +0,0 @@
|
|||
window.h = maquette.h
|
||||
|
||||
class UiFileManager extends ZeroFrame
|
||||
init: ->
|
||||
@url_params = new URLSearchParams(window.location.search)
|
||||
@list_site = @url_params.get("site")
|
||||
@list_address = @url_params.get("address")
|
||||
@list_inner_path = @url_params.get("inner_path")
|
||||
@editor_inner_path = @url_params.get("file")
|
||||
@file_list = new FileList(@list_site, @list_inner_path)
|
||||
|
||||
@site_info = null
|
||||
@server_info = null
|
||||
|
||||
@is_sidebar_closed = false
|
||||
|
||||
if @editor_inner_path
|
||||
@file_editor = new FileEditor(@editor_inner_path)
|
||||
|
||||
window.onbeforeunload = =>
|
||||
if @file_editor?.isModified()
|
||||
return true
|
||||
else
|
||||
return null
|
||||
|
||||
window.onresize = =>
|
||||
@checkBodyWidth()
|
||||
|
||||
@checkBodyWidth()
|
||||
|
||||
@cmd("wrapperSetViewport", "width=device-width, initial-scale=0.8")
|
||||
|
||||
@cmd "serverInfo", {}, (server_info) =>
|
||||
@server_info = server_info
|
||||
@cmd "siteInfo", {}, (site_info) =>
|
||||
@cmd("wrapperSetTitle", "List: /#{@list_inner_path} - #{site_info.content.title} - ZeroNet")
|
||||
@site_info = site_info
|
||||
if @file_editor then @file_editor.on_loaded.then =>
|
||||
@file_editor.cm.setOption("readOnly", not site_info.settings.own)
|
||||
@file_editor.mode = if site_info.settings.own then "Edit" else "View"
|
||||
@projector.scheduleRender()
|
||||
|
||||
checkBodyWidth: =>
|
||||
if not @file_editor
|
||||
return false
|
||||
|
||||
if document.body.offsetWidth < 960 and not @is_sidebar_closed
|
||||
@is_sidebar_closed = true
|
||||
@projector?.scheduleRender()
|
||||
else if document.body.offsetWidth > 960 and @is_sidebar_closed
|
||||
@is_sidebar_closed = false
|
||||
@projector?.scheduleRender()
|
||||
|
||||
onRequest: (cmd, message) =>
|
||||
if cmd == "setSiteInfo"
|
||||
@site_info = message
|
||||
RateLimitCb 1000, (cb_done) =>
|
||||
@file_list.update(cb_done)
|
||||
@projector.scheduleRender()
|
||||
else if cmd == "setServerInfo"
|
||||
@server_info = message
|
||||
@projector.scheduleRender()
|
||||
else
|
||||
@log "Unknown incoming message:", cmd
|
||||
|
||||
createProjector: =>
|
||||
@projector = maquette.createProjector()
|
||||
@projector.replace($("#content"), @render)
|
||||
|
||||
render: =>
|
||||
return h("div.content#content", [
|
||||
h("div.manager", {classes: {editing: @file_editor, sidebar_closed: @is_sidebar_closed}}, [
|
||||
@file_list.render(),
|
||||
if @file_editor then @file_editor.render()
|
||||
])
|
||||
])
|
||||
|
||||
window.Page = new UiFileManager()
|
||||
window.Page.createProjector()
|
|
@ -1,138 +0,0 @@
|
|||
class Animation
|
||||
slideDown: (elem, props) ->
|
||||
if elem.offsetTop > 2000
|
||||
return
|
||||
|
||||
h = elem.offsetHeight
|
||||
cstyle = window.getComputedStyle(elem)
|
||||
margin_top = cstyle.marginTop
|
||||
margin_bottom = cstyle.marginBottom
|
||||
padding_top = cstyle.paddingTop
|
||||
padding_bottom = cstyle.paddingBottom
|
||||
transition = cstyle.transition
|
||||
|
||||
elem.style.boxSizing = "border-box"
|
||||
elem.style.overflow = "hidden"
|
||||
elem.style.transform = "scale(0.6)"
|
||||
elem.style.opacity = "0"
|
||||
elem.style.height = "0px"
|
||||
elem.style.marginTop = "0px"
|
||||
elem.style.marginBottom = "0px"
|
||||
elem.style.paddingTop = "0px"
|
||||
elem.style.paddingBottom = "0px"
|
||||
elem.style.transition = "none"
|
||||
|
||||
setTimeout (->
|
||||
elem.className += " animate-inout"
|
||||
elem.style.height = h+"px"
|
||||
elem.style.transform = "scale(1)"
|
||||
elem.style.opacity = "1"
|
||||
elem.style.marginTop = margin_top
|
||||
elem.style.marginBottom = margin_bottom
|
||||
elem.style.paddingTop = padding_top
|
||||
elem.style.paddingBottom = padding_bottom
|
||||
), 1
|
||||
|
||||
elem.addEventListener "transitionend", ->
|
||||
elem.classList.remove("animate-inout")
|
||||
elem.style.transition = elem.style.transform = elem.style.opacity = elem.style.height = null
|
||||
elem.style.boxSizing = elem.style.marginTop = elem.style.marginBottom = null
|
||||
elem.style.paddingTop = elem.style.paddingBottom = elem.style.overflow = null
|
||||
elem.removeEventListener "transitionend", arguments.callee, false
|
||||
|
||||
|
||||
slideUp: (elem, remove_func, props) ->
|
||||
if elem.offsetTop > 1000
|
||||
return remove_func()
|
||||
|
||||
elem.className += " animate-back"
|
||||
elem.style.boxSizing = "border-box"
|
||||
elem.style.height = elem.offsetHeight+"px"
|
||||
elem.style.overflow = "hidden"
|
||||
elem.style.transform = "scale(1)"
|
||||
elem.style.opacity = "1"
|
||||
elem.style.pointerEvents = "none"
|
||||
setTimeout (->
|
||||
elem.style.height = "0px"
|
||||
elem.style.marginTop = "0px"
|
||||
elem.style.marginBottom = "0px"
|
||||
elem.style.paddingTop = "0px"
|
||||
elem.style.paddingBottom = "0px"
|
||||
elem.style.transform = "scale(0.8)"
|
||||
elem.style.borderTopWidth = "0px"
|
||||
elem.style.borderBottomWidth = "0px"
|
||||
elem.style.opacity = "0"
|
||||
), 1
|
||||
elem.addEventListener "transitionend", (e) ->
|
||||
if e.propertyName == "opacity" or e.elapsedTime >= 0.6
|
||||
elem.removeEventListener "transitionend", arguments.callee, false
|
||||
remove_func()
|
||||
|
||||
|
||||
slideUpInout: (elem, remove_func, props) ->
|
||||
elem.className += " animate-inout"
|
||||
elem.style.boxSizing = "border-box"
|
||||
elem.style.height = elem.offsetHeight+"px"
|
||||
elem.style.overflow = "hidden"
|
||||
elem.style.transform = "scale(1)"
|
||||
elem.style.opacity = "1"
|
||||
elem.style.pointerEvents = "none"
|
||||
setTimeout (->
|
||||
elem.style.height = "0px"
|
||||
elem.style.marginTop = "0px"
|
||||
elem.style.marginBottom = "0px"
|
||||
elem.style.paddingTop = "0px"
|
||||
elem.style.paddingBottom = "0px"
|
||||
elem.style.transform = "scale(0.8)"
|
||||
elem.style.borderTopWidth = "0px"
|
||||
elem.style.borderBottomWidth = "0px"
|
||||
elem.style.opacity = "0"
|
||||
), 1
|
||||
elem.addEventListener "transitionend", (e) ->
|
||||
if e.propertyName == "opacity" or e.elapsedTime >= 0.6
|
||||
elem.removeEventListener "transitionend", arguments.callee, false
|
||||
remove_func()
|
||||
|
||||
|
||||
showRight: (elem, props) ->
|
||||
elem.className += " animate"
|
||||
elem.style.opacity = 0
|
||||
elem.style.transform = "TranslateX(-20px) Scale(1.01)"
|
||||
setTimeout (->
|
||||
elem.style.opacity = 1
|
||||
elem.style.transform = "TranslateX(0px) Scale(1)"
|
||||
), 1
|
||||
elem.addEventListener "transitionend", ->
|
||||
elem.classList.remove("animate")
|
||||
elem.style.transform = elem.style.opacity = null
|
||||
|
||||
|
||||
show: (elem, props) ->
|
||||
delay = arguments[arguments.length-2]?.delay*1000 or 1
|
||||
elem.style.opacity = 0
|
||||
setTimeout (->
|
||||
elem.className += " animate"
|
||||
), 1
|
||||
setTimeout (->
|
||||
elem.style.opacity = 1
|
||||
), delay
|
||||
elem.addEventListener "transitionend", ->
|
||||
elem.classList.remove("animate")
|
||||
elem.style.opacity = null
|
||||
elem.removeEventListener "transitionend", arguments.callee, false
|
||||
|
||||
hide: (elem, remove_func, props) ->
|
||||
delay = arguments[arguments.length-2]?.delay*1000 or 1
|
||||
elem.className += " animate"
|
||||
setTimeout (->
|
||||
elem.style.opacity = 0
|
||||
), delay
|
||||
elem.addEventListener "transitionend", (e) ->
|
||||
if e.propertyName == "opacity"
|
||||
remove_func()
|
||||
|
||||
addVisibleClass: (elem, props) ->
|
||||
setTimeout ->
|
||||
elem.classList.add("visible")
|
||||
|
||||
window.Animation = new Animation()
|
|
@ -1,23 +0,0 @@
|
|||
class Class
|
||||
trace: true
|
||||
|
||||
log: (args...) ->
|
||||
return unless @trace
|
||||
return if typeof console is 'undefined'
|
||||
args.unshift("[#{@.constructor.name}]")
|
||||
console.log(args...)
|
||||
@
|
||||
|
||||
logStart: (name, args...) ->
|
||||
return unless @trace
|
||||
@logtimers or= {}
|
||||
@logtimers[name] = +(new Date)
|
||||
@log "#{name}", args..., "(started)" if args.length > 0
|
||||
@
|
||||
|
||||
logEnd: (name, args...) ->
|
||||
ms = +(new Date)-@logtimers[name]
|
||||
@log "#{name}", args..., "(Done in #{ms}ms)"
|
||||
@
|
||||
|
||||
window.Class = Class
|
|
@ -1,3 +0,0 @@
|
|||
window.$ = (selector) ->
|
||||
if selector.startsWith("#")
|
||||
return document.getElementById(selector.replace("#", ""))
|
|
@ -1,26 +0,0 @@
|
|||
class ItemList
|
||||
constructor: (@item_class, @key) ->
|
||||
@items = []
|
||||
@items_bykey = {}
|
||||
|
||||
sync: (rows, item_class, key) ->
|
||||
@items.splice(0, @items.length) # Empty items
|
||||
for row in rows
|
||||
current_obj = @items_bykey[row[@key]]
|
||||
if current_obj
|
||||
current_obj.row = row
|
||||
@items.push current_obj
|
||||
else
|
||||
item = new @item_class(row, @)
|
||||
@items_bykey[row[@key]] = item
|
||||
@items.push item
|
||||
|
||||
deleteItem: (item) ->
|
||||
index = @items.indexOf(item)
|
||||
if index > -1
|
||||
@items.splice(index, 1)
|
||||
else
|
||||
console.log "Can't delete item", item
|
||||
delete @items_bykey[item.row[@key]]
|
||||
|
||||
window.ItemList = ItemList
|
|
@ -1,110 +0,0 @@
|
|||
class Menu
|
||||
constructor: ->
|
||||
@visible = false
|
||||
@items = []
|
||||
@node = null
|
||||
@height = 0
|
||||
@direction = "bottom"
|
||||
|
||||
show: =>
|
||||
window.visible_menu?.hide()
|
||||
@visible = true
|
||||
window.visible_menu = @
|
||||
@direction = @getDirection()
|
||||
|
||||
hide: =>
|
||||
@visible = false
|
||||
|
||||
toggle: =>
|
||||
if @visible
|
||||
@hide()
|
||||
else
|
||||
@show()
|
||||
Page.projector.scheduleRender()
|
||||
|
||||
|
||||
addItem: (title, cb, selected=false) ->
|
||||
@items.push([title, cb, selected])
|
||||
|
||||
|
||||
storeNode: (node) =>
|
||||
@node = node
|
||||
# Animate visible
|
||||
if @visible
|
||||
node.className = node.className.replace("visible", "")
|
||||
setTimeout (=>
|
||||
node.className += " visible"
|
||||
node.attributes.style.value = @getStyle()
|
||||
), 20
|
||||
node.style.maxHeight = "none"
|
||||
@height = node.offsetHeight
|
||||
node.style.maxHeight = "0px"
|
||||
@direction = @getDirection()
|
||||
|
||||
getDirection: =>
|
||||
if @node and @node.parentNode.getBoundingClientRect().top + @height + 60 > document.body.clientHeight and @node.parentNode.getBoundingClientRect().top - @height > 0
|
||||
return "top"
|
||||
else
|
||||
return "bottom"
|
||||
|
||||
handleClick: (e) =>
|
||||
keep_menu = false
|
||||
for item in @items
|
||||
[title, cb, selected] = item
|
||||
if title == e.currentTarget.textContent or e.currentTarget["data-title"] == title
|
||||
keep_menu = cb?(item)
|
||||
break
|
||||
if keep_menu != true and cb != null
|
||||
@hide()
|
||||
return false
|
||||
|
||||
renderItem: (item) =>
|
||||
[title, cb, selected] = item
|
||||
if typeof(selected) == "function"
|
||||
selected = selected()
|
||||
|
||||
if title == "---"
|
||||
return h("div.menu-item-separator", {key: Time.timestamp()})
|
||||
else
|
||||
if cb == null
|
||||
href = undefined
|
||||
onclick = @handleClick
|
||||
else if typeof(cb) == "string" # Url
|
||||
href = cb
|
||||
onclick = true
|
||||
else # Callback
|
||||
href = "#"+title
|
||||
onclick = @handleClick
|
||||
classes = {
|
||||
"selected": selected,
|
||||
"noaction": (cb == null)
|
||||
}
|
||||
return h("a.menu-item", {href: href, onclick: onclick, "data-title": title, key: title, classes: classes}, title)
|
||||
|
||||
getStyle: =>
|
||||
if @visible
|
||||
max_height = @height
|
||||
else
|
||||
max_height = 0
|
||||
style = "max-height: #{max_height}px"
|
||||
if @direction == "top"
|
||||
style += ";margin-top: #{0 - @height - 50}px"
|
||||
else
|
||||
style += ";margin-top: 0px"
|
||||
return style
|
||||
|
||||
render: (class_name="") =>
|
||||
if @visible or @node
|
||||
h("div.menu#{class_name}", {classes: {"visible": @visible}, style: @getStyle(), afterCreate: @storeNode}, @items.map(@renderItem))
|
||||
|
||||
window.Menu = Menu
|
||||
|
||||
# Hide menu on outside click
|
||||
document.body.addEventListener "mouseup", (e) ->
|
||||
if not window.visible_menu or not window.visible_menu.node
|
||||
return false
|
||||
menu_node = window.visible_menu.node
|
||||
menu_parents = [menu_node, menu_node.parentNode]
|
||||
if e.target.parentNode not in menu_parents and e.target.parentNode.parentNode not in menu_parents
|
||||
window.visible_menu.hide()
|
||||
Page.projector.scheduleRender()
|
|
@ -1,74 +0,0 @@
|
|||
# From: http://dev.bizo.com/2011/12/promises-in-javascriptcoffeescript.html
|
||||
|
||||
class Promise
|
||||
@when: (tasks...) ->
|
||||
num_uncompleted = tasks.length
|
||||
args = new Array(num_uncompleted)
|
||||
promise = new Promise()
|
||||
|
||||
for task, task_id in tasks
|
||||
((task_id) ->
|
||||
task.then(() ->
|
||||
args[task_id] = Array.prototype.slice.call(arguments)
|
||||
num_uncompleted--
|
||||
promise.complete.apply(promise, args) if num_uncompleted == 0
|
||||
)
|
||||
)(task_id)
|
||||
|
||||
return promise
|
||||
|
||||
constructor: ->
|
||||
@resolved = false
|
||||
@end_promise = null
|
||||
@result = null
|
||||
@callbacks = []
|
||||
|
||||
resolve: ->
|
||||
if @resolved
|
||||
return false
|
||||
@resolved = true
|
||||
@data = arguments
|
||||
if not arguments.length
|
||||
@data = [true]
|
||||
@result = @data[0]
|
||||
for callback in @callbacks
|
||||
back = callback.apply callback, @data
|
||||
if @end_promise
|
||||
@end_promise.resolve(back)
|
||||
|
||||
fail: ->
|
||||
@resolve(false)
|
||||
|
||||
then: (callback) ->
|
||||
if @resolved == true
|
||||
callback.apply callback, @data
|
||||
return
|
||||
|
||||
@callbacks.push callback
|
||||
|
||||
@end_promise = new Promise()
|
||||
|
||||
window.Promise = Promise
|
||||
|
||||
###
|
||||
s = Date.now()
|
||||
log = (text) ->
|
||||
console.log Date.now()-s, Array.prototype.slice.call(arguments).join(", ")
|
||||
|
||||
log "Started"
|
||||
|
||||
cmd = (query) ->
|
||||
p = new Promise()
|
||||
setTimeout ( ->
|
||||
p.resolve query+" Result"
|
||||
), 100
|
||||
return p
|
||||
|
||||
back = cmd("SELECT * FROM message").then (res) ->
|
||||
log res
|
||||
return "Return from query"
|
||||
.then (res) ->
|
||||
log "Back then", res
|
||||
|
||||
log "Query started", back
|
||||
###
|
|
@ -1,9 +0,0 @@
|
|||
String::startsWith = (s) -> @[...s.length] is s
|
||||
String::endsWith = (s) -> s is '' or @[-s.length..] is s
|
||||
String::repeat = (count) -> new Array( count + 1 ).join(@)
|
||||
|
||||
window.isEmpty = (obj) ->
|
||||
for key of obj
|
||||
return false
|
||||
return true
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
last_time = {}
|
||||
calling = {}
|
||||
calling_iterval = {}
|
||||
call_after_interval = {}
|
||||
|
||||
# Rate limit function call and don't allow to run in parallel (until callback is called)
|
||||
window.RateLimitCb = (interval, fn, args=[]) ->
|
||||
cb = -> # Callback when function finished
|
||||
left = interval - (Date.now() - last_time[fn]) # Time life until next call
|
||||
# console.log "CB, left", left, "Calling:", calling[fn]
|
||||
if left <= 0 # No time left from rate limit interval
|
||||
delete last_time[fn]
|
||||
if calling[fn] # Function called within interval
|
||||
RateLimitCb(interval, fn, calling[fn])
|
||||
delete calling[fn]
|
||||
else # Time left from rate limit interval
|
||||
setTimeout (->
|
||||
delete last_time[fn]
|
||||
if calling[fn] # Function called within interval
|
||||
RateLimitCb(interval, fn, calling[fn])
|
||||
delete calling[fn]
|
||||
), left
|
||||
if last_time[fn] # Function called within interval
|
||||
calling[fn] = args # Schedule call and update arguments
|
||||
else # Not called within interval, call instantly
|
||||
last_time[fn] = Date.now()
|
||||
fn.apply(this, [cb, args...])
|
||||
|
||||
|
||||
window.RateLimit = (interval, fn) ->
|
||||
if calling_iterval[fn] > interval
|
||||
clearInterval calling[fn]
|
||||
delete calling[fn]
|
||||
|
||||
if not calling[fn]
|
||||
call_after_interval[fn] = false
|
||||
fn() # First call is not delayed
|
||||
calling_iterval[fn] = interval
|
||||
calling[fn] = setTimeout (->
|
||||
if call_after_interval[fn]
|
||||
fn()
|
||||
delete calling[fn]
|
||||
delete call_after_interval[fn]
|
||||
), interval
|
||||
else # Called within iterval, delay the call
|
||||
call_after_interval[fn] = true
|
||||
|
||||
|
||||
###
|
||||
window.s = Date.now()
|
||||
window.load = (done, num) ->
|
||||
console.log "Loading #{num}...", Date.now()-window.s
|
||||
setTimeout (-> done()), 1000
|
||||
|
||||
RateLimit 500, window.load, [0] # Called instantly
|
||||
RateLimit 500, window.load, [1]
|
||||
setTimeout (-> RateLimit 500, window.load, [300]), 300
|
||||
setTimeout (-> RateLimit 500, window.load, [600]), 600 # Called after 1000ms
|
||||
setTimeout (-> RateLimit 500, window.load, [1000]), 1000
|
||||
setTimeout (-> RateLimit 500, window.load, [1200]), 1200 # Called after 2000ms
|
||||
setTimeout (-> RateLimit 500, window.load, [3000]), 3000 # Called after 3000ms
|
||||
###
|
|
@ -1,147 +0,0 @@
|
|||
class Text
|
||||
toColor: (text, saturation=30, lightness=50) ->
|
||||
hash = 0
|
||||
for i in [0..text.length-1]
|
||||
hash += text.charCodeAt(i)*i
|
||||
hash = hash % 1777
|
||||
return "hsl(" + (hash % 360) + ",#{saturation}%,#{lightness}%)";
|
||||
|
||||
|
||||
renderMarked: (text, options={}) ->
|
||||
options["gfm"] = true
|
||||
options["breaks"] = true
|
||||
options["sanitize"] = true
|
||||
options["renderer"] = marked_renderer
|
||||
text = marked(text, options)
|
||||
return @fixHtmlLinks text
|
||||
|
||||
emailLinks: (text) ->
|
||||
return text.replace(/([a-zA-Z0-9]+)@zeroid.bit/g, "<a href='?to=$1' onclick='return Page.message_create.show(\"$1\")'>$1@zeroid.bit</a>")
|
||||
|
||||
# Convert zeronet html links to relaitve
|
||||
fixHtmlLinks: (text) ->
|
||||
if window.is_proxy
|
||||
return text.replace(/href="http:\/\/(127.0.0.1|localhost):43110/g, 'href="http://zero')
|
||||
else
|
||||
return text.replace(/href="http:\/\/(127.0.0.1|localhost):43110/g, 'href="')
|
||||
|
||||
# Convert a single link to relative
|
||||
fixLink: (link) ->
|
||||
if window.is_proxy
|
||||
back = link.replace(/http:\/\/(127.0.0.1|localhost):43110/, 'http://zero')
|
||||
return back.replace(/http:\/\/zero\/([^\/]+\.bit)/, "http://$1") # Domain links
|
||||
else
|
||||
return link.replace(/http:\/\/(127.0.0.1|localhost):43110/, '')
|
||||
|
||||
toUrl: (text) ->
|
||||
return text.replace(/[^A-Za-z0-9]/g, "+").replace(/[+]+/g, "+").replace(/[+]+$/, "")
|
||||
|
||||
getSiteUrl: (address) ->
|
||||
if window.is_proxy
|
||||
if "." in address # Domain
|
||||
return "http://"+address+"/"
|
||||
else
|
||||
return "http://zero/"+address+"/"
|
||||
else
|
||||
return "/"+address+"/"
|
||||
|
||||
|
||||
fixReply: (text) ->
|
||||
return text.replace(/(>.*\n)([^\n>])/gm, "$1\n$2")
|
||||
|
||||
toBitcoinAddress: (text) ->
|
||||
return text.replace(/[^A-Za-z0-9]/g, "")
|
||||
|
||||
|
||||
jsonEncode: (obj) ->
|
||||
return unescape(encodeURIComponent(JSON.stringify(obj)))
|
||||
|
||||
jsonDecode: (obj) ->
|
||||
return JSON.parse(decodeURIComponent(escape(obj)))
|
||||
|
||||
fileEncode: (obj) ->
|
||||
if typeof(obj) == "string"
|
||||
return btoa(unescape(encodeURIComponent(obj)))
|
||||
else
|
||||
return btoa(unescape(encodeURIComponent(JSON.stringify(obj, undefined, '\t'))))
|
||||
|
||||
utf8Encode: (s) ->
|
||||
return unescape(encodeURIComponent(s))
|
||||
|
||||
utf8Decode: (s) ->
|
||||
return decodeURIComponent(escape(s))
|
||||
|
||||
|
||||
distance: (s1, s2) ->
|
||||
s1 = s1.toLocaleLowerCase()
|
||||
s2 = s2.toLocaleLowerCase()
|
||||
next_find_i = 0
|
||||
next_find = s2[0]
|
||||
match = true
|
||||
extra_parts = {}
|
||||
for char in s1
|
||||
if char != next_find
|
||||
if extra_parts[next_find_i]
|
||||
extra_parts[next_find_i] += char
|
||||
else
|
||||
extra_parts[next_find_i] = char
|
||||
else
|
||||
next_find_i++
|
||||
next_find = s2[next_find_i]
|
||||
|
||||
if extra_parts[next_find_i]
|
||||
extra_parts[next_find_i] = "" # Extra chars on the end doesnt matter
|
||||
extra_parts = (val for key, val of extra_parts)
|
||||
if next_find_i >= s2.length
|
||||
return extra_parts.length + extra_parts.join("").length
|
||||
else
|
||||
return false
|
||||
|
||||
|
||||
parseQuery: (query) ->
|
||||
params = {}
|
||||
parts = query.split('&')
|
||||
for part in parts
|
||||
[key, val] = part.split("=")
|
||||
if val
|
||||
params[decodeURIComponent(key)] = decodeURIComponent(val)
|
||||
else
|
||||
params["url"] = decodeURIComponent(key)
|
||||
return params
|
||||
|
||||
encodeQuery: (params) ->
|
||||
back = []
|
||||
if params.url
|
||||
back.push(params.url)
|
||||
for key, val of params
|
||||
if not val or key == "url"
|
||||
continue
|
||||
back.push("#{encodeURIComponent(key)}=#{encodeURIComponent(val)}")
|
||||
return back.join("&")
|
||||
|
||||
highlight: (text, search) ->
|
||||
if not text
|
||||
return [""]
|
||||
parts = text.split(RegExp(search, "i"))
|
||||
back = []
|
||||
for part, i in parts
|
||||
back.push(part)
|
||||
if i < parts.length-1
|
||||
back.push(h("span.highlight", {key: i}, search))
|
||||
return back
|
||||
|
||||
formatSize: (size) ->
|
||||
if isNaN(parseInt(size))
|
||||
return ""
|
||||
size_mb = size/1024/1024
|
||||
if size_mb >= 1000
|
||||
return (size_mb/1024).toFixed(1)+" GB"
|
||||
else if size_mb >= 100
|
||||
return size_mb.toFixed(0)+" MB"
|
||||
else if size/1024 >= 1000
|
||||
return size_mb.toFixed(2)+" MB"
|
||||
else
|
||||
return (parseInt(size)/1024).toFixed(2)+" KB"
|
||||
|
||||
window.is_proxy = (document.location.host == "zero" or window.location.pathname == "/")
|
||||
window.Text = new Text()
|
|
@ -1,59 +0,0 @@
|
|||
class Time
|
||||
since: (timestamp) ->
|
||||
now = +(new Date)/1000
|
||||
if timestamp > 1000000000000 # In ms
|
||||
timestamp = timestamp/1000
|
||||
secs = now - timestamp
|
||||
if secs < 60
|
||||
back = "Just now"
|
||||
else if secs < 60*60
|
||||
minutes = Math.round(secs/60)
|
||||
back = "" + minutes + " minutes ago"
|
||||
else if secs < 60*60*24
|
||||
back = "#{Math.round(secs/60/60)} hours ago"
|
||||
else if secs < 60*60*24*3
|
||||
back = "#{Math.round(secs/60/60/24)} days ago"
|
||||
else
|
||||
back = "on "+@date(timestamp)
|
||||
back = back.replace(/^1 ([a-z]+)s/, "1 $1") # 1 days ago fix
|
||||
return back
|
||||
|
||||
dateIso: (timestamp=null) ->
|
||||
if not timestamp
|
||||
timestamp = window.Time.timestamp()
|
||||
|
||||
if timestamp > 1000000000000 # In ms
|
||||
timestamp = timestamp/1000
|
||||
tzoffset = (new Date()).getTimezoneOffset() * 60
|
||||
return (new Date((timestamp - tzoffset) * 1000)).toISOString().split("T")[0]
|
||||
|
||||
date: (timestamp=null, format="short") ->
|
||||
if not timestamp
|
||||
timestamp = window.Time.timestamp()
|
||||
|
||||
if timestamp > 1000000000000 # In ms
|
||||
timestamp = timestamp/1000
|
||||
parts = (new Date(timestamp * 1000)).toString().split(" ")
|
||||
if format == "short"
|
||||
display = parts.slice(1, 4)
|
||||
else if format == "day"
|
||||
display = parts.slice(1, 3)
|
||||
else if format == "month"
|
||||
display = [parts[1], parts[3]]
|
||||
else if format == "long"
|
||||
display = parts.slice(1, 5)
|
||||
return display.join(" ").replace(/( [0-9]{4})/, ",$1")
|
||||
|
||||
weekDay: (timestamp) ->
|
||||
if timestamp > 1000000000000 # In ms
|
||||
timestamp = timestamp/1000
|
||||
return ["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"][ (new Date(timestamp * 1000)).getDay() ]
|
||||
|
||||
timestamp: (date="") ->
|
||||
if date == "now" or date == ""
|
||||
return parseInt(+(new Date)/1000)
|
||||
else
|
||||
return parseInt(Date.parse(date)/1000)
|
||||
|
||||
|
||||
window.Time = new Time
|
|
@ -1,85 +0,0 @@
|
|||
class ZeroFrame extends Class
|
||||
constructor: (url) ->
|
||||
@url = url
|
||||
@waiting_cb = {}
|
||||
@wrapper_nonce = document.location.href.replace(/.*wrapper_nonce=([A-Za-z0-9]+).*/, "$1")
|
||||
@connect()
|
||||
@next_message_id = 1
|
||||
@history_state = {}
|
||||
@init()
|
||||
|
||||
|
||||
init: ->
|
||||
@
|
||||
|
||||
|
||||
connect: ->
|
||||
@target = window.parent
|
||||
window.addEventListener("message", @onMessage, false)
|
||||
@cmd("innerReady")
|
||||
|
||||
# Save scrollTop
|
||||
window.addEventListener "beforeunload", (e) =>
|
||||
@log "save scrollTop", window.pageYOffset
|
||||
@history_state["scrollTop"] = window.pageYOffset
|
||||
@cmd "wrapperReplaceState", [@history_state, null]
|
||||
|
||||
# Restore scrollTop
|
||||
@cmd "wrapperGetState", [], (state) =>
|
||||
@history_state = state if state?
|
||||
@log "restore scrollTop", state, window.pageYOffset
|
||||
if window.pageYOffset == 0 and state
|
||||
window.scroll(window.pageXOffset, state.scrollTop)
|
||||
|
||||
|
||||
onMessage: (e) =>
|
||||
message = 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 == "wrapperReady" # Wrapper inited later
|
||||
@cmd("innerReady")
|
||||
else if cmd == "ping"
|
||||
@response message.id, "pong"
|
||||
else if cmd == "wrapperOpenedWebsocket"
|
||||
@onOpenWebsocket()
|
||||
else if cmd == "wrapperClosedWebsocket"
|
||||
@onCloseWebsocket()
|
||||
else
|
||||
@onRequest cmd, message.params
|
||||
|
||||
|
||||
onRequest: (cmd, message) =>
|
||||
@log "Unknown request", 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) ->
|
||||
message.wrapper_nonce = @wrapper_nonce
|
||||
message.id = @next_message_id
|
||||
@next_message_id += 1
|
||||
@target.postMessage(message, "*")
|
||||
if cb
|
||||
@waiting_cb[message.id] = cb
|
||||
|
||||
|
||||
onOpenWebsocket: =>
|
||||
@log "Websocket open"
|
||||
|
||||
|
||||
onCloseWebsocket: =>
|
||||
@log "Websocket close"
|
||||
|
||||
|
||||
|
||||
window.ZeroFrame = ZeroFrame
|
Loading…
Add table
Add a link
Reference in a new issue