From 9b85e2a5b39de43d52658959acf176af5cc34e65 Mon Sep 17 00:00:00 2001 From: monsterkodi Date: Mon, 10 Oct 2016 18:28:35 +0200 Subject: [PATCH] standalone --- .gitignore | 11 +- .konrad.noon | 11 +- app/package.noon | 23 ++ bin/build.sh | 8 + bin/kiki | 36 ++++ build/background.png | Bin 0 -> 5446 bytes build/background@2x.png | Bin 0 -> 12433 bytes build/icon.icns | Bin 0 -> 109583 bytes coffee/actor.coffee | 4 - coffee/app.coffee | 319 ++++++++++++++++++++++++++++ coffee/item.coffee | 1 - coffee/{main.coffee => kiki.coffee} | 7 +- coffee/mainmenu.coffee | 85 ++++++++ coffee/stage.coffee | 42 ++++ coffee/titlebar.coffee | 20 ++ coffee/tools/ansidiss.coffee | 165 ++++++++++++++ coffee/tools/drag.coffee | 100 +++++++++ coffee/tools/encode.coffee | 14 ++ coffee/tools/enspce.coffee | 15 ++ coffee/tools/fps.coffee | 68 ++++++ coffee/tools/keyinfo.coffee | 51 +++++ coffee/tools/log.coffee | 21 ++ coffee/tools/matchr.coffee | 172 +++++++++++++++ coffee/tools/pos.coffee | 94 ++++++++ coffee/tools/prefs.coffee | 76 +++++++ coffee/tools/profile.coffee | 25 +++ coffee/tools/ranges.coffee | 23 ++ coffee/tools/salt.coffee | 25 +++ coffee/tools/str.coffee | 21 ++ coffee/tools/tools.coffee | 199 +++++++++++++++++ coffee/tools/walker.coffee | 118 ++++++++++ coffee/window.coffee | 63 ++++++ coffee/world.coffee | 1 - package.noon | 61 +++++- pug/index.pug | 11 + styl/about.styl | 64 ++++++ styl/style.styl | 50 +++++ 37 files changed, 1984 insertions(+), 20 deletions(-) create mode 100644 app/package.noon create mode 100755 bin/build.sh create mode 100755 bin/kiki create mode 100644 build/background.png create mode 100644 build/background@2x.png create mode 100644 build/icon.icns create mode 100644 coffee/app.coffee rename coffee/{main.coffee => kiki.coffee} (89%) create mode 100644 coffee/mainmenu.coffee create mode 100644 coffee/stage.coffee create mode 100644 coffee/titlebar.coffee create mode 100644 coffee/tools/ansidiss.coffee create mode 100644 coffee/tools/drag.coffee create mode 100644 coffee/tools/encode.coffee create mode 100644 coffee/tools/enspce.coffee create mode 100644 coffee/tools/fps.coffee create mode 100644 coffee/tools/keyinfo.coffee create mode 100644 coffee/tools/log.coffee create mode 100644 coffee/tools/matchr.coffee create mode 100644 coffee/tools/pos.coffee create mode 100644 coffee/tools/prefs.coffee create mode 100644 coffee/tools/profile.coffee create mode 100644 coffee/tools/ranges.coffee create mode 100644 coffee/tools/salt.coffee create mode 100644 coffee/tools/str.coffee create mode 100644 coffee/tools/tools.coffee create mode 100644 coffee/tools/walker.coffee create mode 100644 coffee/window.coffee create mode 100644 pug/index.pug create mode 100644 styl/about.styl create mode 100644 styl/style.styl diff --git a/.gitignore b/.gitignore index 5fad1d4..72beba7 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,12 @@ js +css +dist +app/img +app/css +app/bin +app/js +app/index.html +app/sound node_modules -package.json +*.json +*.html \ No newline at end of file diff --git a/.konrad.noon b/.konrad.noon index acfa9e8..2af532f 100644 --- a/.konrad.noon +++ b/.konrad.noon @@ -1 +1,10 @@ -coffee . ext js . replace .. coffee/ js/ \ No newline at end of file +ignore + /dist/ + /app/css/ + /app/img/ + /app/js/ + +noon . ext json . replace .. coffee/ js/ +pug . ext html . replace .. pug/ ./ +styl . ext css . replace .. styl/ css/ + diff --git a/app/package.noon b/app/package.noon new file mode 100644 index 0000000..9a811c0 --- /dev/null +++ b/app/package.noon @@ -0,0 +1,23 @@ +name kiki +productName kiki +main js/app.js +description kiki the nanobot +version 0.1.0 +preferGlobal true +keywords +license Unlicense +author monsterkodi +maintainers + . + name monsterkodi + email monsterkodi@gmx.net +dependencies + coffee-script ^1.10.0 + howler ^2.0.0 + keycode ^2.1.7 + lodash ^4.13.1 + noon >=1.0.12 + performance-now ^0.2.0 + sprintf-js ^1.0.3 + three 0.79.0 + underscore.string ^3.3.4 \ No newline at end of file diff --git a/bin/build.sh b/bin/build.sh new file mode 100755 index 0000000..8cf2781 --- /dev/null +++ b/bin/build.sh @@ -0,0 +1,8 @@ +#!/usr/bin/env bash + +rm -rf dist app/js app/css app/bin app/sound app/img app/*.html app/node_modules +cp -r js css bin sound *.html app +mkdir app/img +cp img/glow.png app/img/glow.png +cd app && npm install +cd .. diff --git a/bin/kiki b/bin/kiki new file mode 100755 index 0000000..e4d12f3 --- /dev/null +++ b/bin/kiki @@ -0,0 +1,36 @@ +#!/bin/bash + +_NAME="kiki.app" + +while getopts ":t" opt; do + case "$opt" in + t) + _DEV=1 + ;; + esac +done + +if [ $_DEV ]; then + killall Electron 2> /dev/null + killall Electron 2> /dev/null + _DIR=$(pwd) + cd ~/s/kiki + echo "$(pwd)/./node_modules/electron-prebuilt/cli.js ./js/app.js $_DIR" + ./node_modules/electron-prebuilt/cli.js ./js/app.js "$_DIR" "$@" & + exit 0 +elif [ -x "/Applications/$_NAME" ]; then + _PATH="/Applications" +elif [ -x "$HOME/Applications/$_NAME" ]; then + _PATH="$HOME/Applications" +elif [ -x "$HOME/s/kiki/dist/mac/$_NAME" ]; then + _PATH="$HOME/s/kiki/dist/mac" + echo "using dev build $_PATH" +else + _PATH="$(mdfind \"kMDItemCFBundleIdentifier == 'net.monsterkodi.kiki'\" | grep -v ShipIt | head -1 | xargs -0 dirname)" + if [ ! -x "$_PATH/$_NAME" ]; then + node "`dirname $0`/../lib/node_modules/kiki/bin/download.js" $* + exit 0 + fi +fi + +"$_PATH/$_NAME/Contents/MacOS/kiki" "$(pwd)" "$@" & diff --git a/build/background.png b/build/background.png new file mode 100644 index 0000000000000000000000000000000000000000..2ab5c11e199371ed8c139062490a72857bcedf80 GIT binary patch literal 5446 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 literal 0 HcmV?d00001 diff --git a/build/background@2x.png b/build/background@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..ba84356f74af6a7dfe2b9dbdb6f616434691d4b8 GIT binary patch literal 12433 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 literal 0 HcmV?d00001 diff --git a/coffee/actor.coffee b/coffee/actor.coffee index 8682ac8..5003d87 100644 --- a/coffee/actor.coffee +++ b/coffee/actor.coffee @@ -29,7 +29,6 @@ class Actor extends Emitter # 00000000 0 00000000 000 000 000 addEventWithName: (eventName) -> - # log "Actor.addEventWithName #{@name} eventName:#{eventName}" if @getEventWithName eventName # to be removed log "Actor.addEventWithName [WARNING] '#{eventName}' already in use!" return -1; # shouldn't happen anyway :-) @@ -49,7 +48,6 @@ class Actor extends Emitter # 000 000 000 000 000 000 000 000 0000 # 000 000 0000000 000 000 0000000 000 000 - # addAction: (action) -> @actions[action.id] = action addAction: (action) -> @actions[action.name] = action del: -> @deleteActions() @@ -58,7 +56,6 @@ class Actor extends Emitter a?.del() for a in @actions @actions = [] - # removeAction: (action) -> @actions[action.id] = null removeAction: (action) -> @actions[action.name] = null getActionWithId: (actionId) -> _.find @actions, (a) -> a?.id == actionId @@ -84,7 +81,6 @@ class Actor extends Emitter startTimedAction: (action, duration) -> action.duration = duration if duration >= 0 - # log "Actor.startTimedAction #{action.name} duration: #{action.duration}" Timer.addAction action module.exports = Actor diff --git a/coffee/app.coffee b/coffee/app.coffee new file mode 100644 index 0000000..1a9cf5b --- /dev/null +++ b/coffee/app.coffee @@ -0,0 +1,319 @@ +# 0000000 00000000 00000000 +# 000 000 000 000 000 000 +# 000000000 00000000 00000000 +# 000 000 000 000 +# 000 000 000 000 +{ +first, +fileList, +dirExists, +fileExists, +resolve} = require './tools/tools' +log = require './tools/log' +str = require './tools/str' +pkg = require '../package.json' +MainMenu = require './mainmenu' +_ = require 'lodash' +fs = require 'fs' +noon = require 'noon' +colors = require 'colors' +electron = require 'electron' +childp = require 'child_process' +app = electron.app +BrowserWindow = electron.BrowserWindow +Menu = electron.Menu +clipboard = electron.clipboard +ipc = electron.ipcMain +dialog = electron.dialog +main = undefined # < created in app.on 'ready' +openFiles = [] +wins = [] + +# 0000000 00000000 0000000 0000000 +# 000 000 000 000 000 000 +# 000000000 0000000 000 0000 0000000 +# 000 000 000 000 000 000 000 +# 000 000 000 000 0000000 0000000 + +args = require('karg') """ + +#{pkg.productName} + + filelist . ? files to open . ** + verbose . ? log more . = false + DevTools . ? open developer tools . = false + debug . = false + test . = false + +version #{pkg.version} + +""", dontExit: true + +app.exit 0 if not args? + +while args.filelist.length and dirExists first args.filelist + process.chdir args.filelist.shift() + +if args.verbose + log colors.white.bold "\n#{pkg.productName}", colors.gray "v#{pkg.version}\n" + log colors.yellow.bold 'process' + p = cwd: process.cwd() + log noon.stringify p, colors:true + log colors.yellow.bold 'args' + log noon.stringify args, colors:true + log '' + +# 000 00000000 0000000 +# 000 000 000 000 +# 000 00000000 000 +# 000 000 000 +# 000 000 0000000 + +ipc.on 'toggleDevTools', (event) => event.sender.toggleDevTools() +ipc.on 'maximizeWindow', (event, winID) => main.toggleMaximize winWithID winID +ipc.on 'activateWindow', (event, winID) => main.activateWindowWithID winID +ipc.on 'saveBounds', (event, winID) => main.saveWinBounds winWithID winID +ipc.on 'reloadWindow', (event, winID) => main.reloadWin winWithID winID +ipc.on 'reloadMenu', () => main.reloadMenu() # still in use? + +# 000 000 000 000 000 0000000 +# 000 0 000 000 0000 000 000 +# 000000000 000 000 0 000 0000000 +# 000 000 000 000 0000 000 +# 00 00 000 000 000 0000000 + +wins = -> BrowserWindow.getAllWindows().sort (a,b) -> a.id - b.id +activeWin = -> BrowserWindow.getFocusedWindow() +visibleWins = -> (w for w in wins() when w?.isVisible() and not w?.isMinimized()) +winWithID = (winID) -> + wid = parseInt winID + for w in wins() + return w if w.id == wid + +# 00 00 0000000 000 000 000 +# 000 000 000 000 000 0000 000 +# 000000000 000000000 000 000 0 000 +# 000 0 000 000 000 000 000 0000 +# 000 000 000 000 000 000 000 + +class Main + + constructor: (openFiles) -> + + if app.makeSingleInstance @otherInstanceStarted + app.exit 0 + return + + app.setName pkg.productName + + log app.name + + if not openFiles.length and args.filelist.length + openFiles = fileList args.filelist + + if openFiles.length + for file in openFiles + @createWindow file + + if not wins().length + w = @createWindow() + + if args.DevTools + wins()?[0]?.webContents.openDevTools() + + MainMenu.init @ + + setTimeout @showWindows, 10 + + # 000 000 000 000 000 0000000 0000000 000 000 0000000 + # 000 0 000 000 0000 000 000 000 000 000 000 0 000 000 + # 000000000 000 000 0 000 000 000 000 000 000000000 0000000 + # 000 000 000 000 0000 000 000 000 000 000 000 000 + # 00 00 000 000 000 0000000 0000000 00 00 0000000 + + wins: wins + winWithID: winWithID + activeWin: activeWin + visibleWins: visibleWins + + reloadMenu: => MainMenu.init @ + + reloadWin: (win) -> + if win? + dev = win.webContents.isDevToolsOpened() + if dev + win.webContents.closeDevTools() + setTimeout win.webContents.reloadIgnoringCache, 100 + else + win.webContents.reloadIgnoringCache() + + toggleMaximize: (win) -> + if win.isMaximized() + win.unmaximize() + else + win.maximize() + + toggleWindows: => + if wins().length + if visibleWins().length + if activeWin() + @hideWindows() + else + @raiseWindows() + else + @showWindows() + else + @createWindow() + + hideWindows: => + for w in wins() + w.hide() + + showWindows: => + for w in wins() + w.show() + app.dock.show() + + raiseWindows: => + if visibleWins().length + for w in visibleWins() + w.showInactive() + visibleWins()[0].showInactive() + visibleWins()[0].focus() + + activateWindowWithID: (wid) => + w = winWithID wid + return if not w? + if not w.isVisible() + w.show() + w.focus() + + closeOtherWindows:=> + for w in wins() + if w != activeWin() + @closeWindow w + + closeWindow: (w) => w?.close() + + closeWindows: => + for w in wins() + @closeWindow w + + closeWindowsAndQuit: => + @closeWindows() + @quit() + + # 0000000 0000000 00000000 00000000 00000000 000 000 + # 000 000 000 000 000 000 0000 000 + # 0000000 000 0000000 0000000 0000000 000 0 000 + # 000 000 000 000 000 000 000 0000 + # 0000000 0000000 000 000 00000000 00000000 000 000 + + screenSize: -> electron.screen.getPrimaryDisplay().workAreaSize + + # 0000000 00000000 00000000 0000000 000000000 00000000 + # 000 000 000 000 000 000 000 000 + # 000 0000000 0000000 000000000 000 0000000 + # 000 000 000 000 000 000 000 000 + # 0000000 000 000 00000000 000 000 000 00000000 + + newWindowWithFile: (file, pos) -> @createWindow(file, pos).id + + createWindow: (openFile, pos) -> + + {width, height} = @screenSize() + ww = height + 122 + + win = new BrowserWindow + x: parseInt (width-ww)/2 + y: 0 + width: ww + height: height + minWidth: 140 + minHeight: 130 + useContentSize: true + fullscreenable: true + show: true + hasShadow: false + backgroundColor: '#000' + titleBarStyle: 'hidden' + + win.loadURL "file://#{__dirname}/../index.html" + app.dock.show() + win.on 'close', @onCloseWin + win.on 'move', @onMoveWin + win.on 'resize', @onResizeWin + + winReady = => win.webContents.send 'setWinID', win.id + winLoaded = => + + win.webContents.on 'dom-ready', winReady + win.webContents.on 'did-finish-load', winLoaded + win + + onMoveWin: (event) => + + # 00000000 00000000 0000000 000 0000000 00000000 + # 000 000 000 000 000 000 000 + # 0000000 0000000 0000000 000 000 0000000 + # 000 000 000 000 000 000 000 + # 000 000 00000000 0000000 000 0000000 00000000 + + onResizeWin: (event) => + onCloseWin: (event) => + + otherInstanceStarted: (args, dir) => + if not visibleWins().length + @toggleWindows() + + for arg in args.slice(2) + continue if arg.startsWith '-' + file = arg + if not arg.startsWith '/' + file = resolve dir + '/' + arg + continue if not fileExists file + w = @activateWindowWithFile file + w = @createWindow file if not w? + + if !activeWin() + visibleWins()[0]?.focus() + + quit: => + app.exit 0 + process.exit 0 + + # 0000000 0000000 0000000 000 000 000000000 + # 000 000 000 000 000 000 000 000 000 + # 000000000 0000000 000 000 000 000 000 + # 000 000 000 000 000 000 000 000 000 + # 000 000 0000000 0000000 0000000 000 + + showAbout: => + cwd = __dirname + w = new BrowserWindow + dir: cwd + preloadWindow: true + resizable: true + frame: true + show: true + center: true + backgroundColor: '#333' + width: 400 + height: 420 + w.loadURL "file://#{cwd}/../about.html" + w.on 'openFileDialog', @createWindow + + log: -> log (str(s) for s in [].slice.call arguments, 0).join " " if args.verbose + dbg: -> log (str(s) for s in [].slice.call arguments, 0).join " " if args.debug + +# 0000000 00000000 00000000 0000000 000 000 +# 000 000 000 000 000 000 000 000 0000 000 +# 000000000 00000000 00000000 000 000 000 0 000 +# 000 000 000 000 000 000 000 000 0000 +# 000 000 000 000 000 0000000 000 000 + +app.on 'ready', => main = new Main openFiles +app.on 'window-all-closed', => app.exit 0 + +app.setName pkg.productName + diff --git a/coffee/item.coffee b/coffee/item.coffee index d56b922..b22257b 100644 --- a/coffee/item.coffee +++ b/coffee/item.coffee @@ -25,7 +25,6 @@ class Item extends Actor del: -> return if @name == 'del' super - # log "item del !!!!!!!!!!!!!!!!!!!!!! #{@name}" @name = 'del' world.scene.remove @mesh if @mesh? world.removeObject @ diff --git a/coffee/main.coffee b/coffee/kiki.coffee similarity index 89% rename from coffee/main.coffee rename to coffee/kiki.coffee index bb962ef..63fd9ff 100644 --- a/coffee/main.coffee +++ b/coffee/kiki.coffee @@ -5,13 +5,14 @@ # 000 000 000 000 000 000 # 000 000 000 000 000 000 -Stage = require '/Users/kodi/s/ko/js/area/stage' -log = require '/Users/kodi/s/ko/js/tools/log' +Stage = require './stage' +log = require './tools/log' World = require './world' class Kiki extends Stage constructor: (@view) -> + log "view:", @view.className super @view @view.focus() @@ -47,7 +48,7 @@ class Kiki extends Stage @elem.remove() @pause() - resized: (w,h) -> @world.resized w, h + resized: () -> @world.resized @view.clientWidth, @view.clientHeight modKeyComboEventDown: (mod, key, combo, event) -> world.modKeyComboEventDown mod, key, combo, event modKeyComboEventUp: (mod, key, combo, event) -> world.modKeyComboEventUp mod, key, combo, event diff --git a/coffee/mainmenu.coffee b/coffee/mainmenu.coffee new file mode 100644 index 0000000..02fe8b5 --- /dev/null +++ b/coffee/mainmenu.coffee @@ -0,0 +1,85 @@ +# 00 00 0000000 000 000 000 00 00 00000000 000 000 000 000 +# 000 000 000 000 000 0000 000 000 000 000 0000 000 000 000 +# 000000000 000000000 000 000 0 000 000000000 0000000 000 0 000 000 000 +# 000 0 000 000 000 000 000 0000 000 0 000 000 000 0000 000 000 +# 000 000 000 000 000 000 000 000 000 00000000 000 000 0000000 +{ +unresolve +} = require './tools/tools' +log = require './tools/log' +pkg = require '../package.json' +fs = require 'fs' +path = require 'path' +Menu = require('electron').Menu + +class MainMenu + + @init: (main) -> + + fileLabel = (f) -> + return path.basename(f) + ' - ' + unresolve path.dirname(f) if f? + "untitled" + + Menu.setApplicationMenu Menu.buildFromTemplate [ + + label: pkg.name + submenu: [ + label: "About #{pkg.productName}" + click: main.showAbout + , + type: 'separator' + , + label: "Hide #{pkg.productName}" + accelerator: 'Command+H' + click: main.hideWindows + , + label: 'Hide Others' + accelerator: 'Command+Alt+H' + role: 'hideothers' + , + type: 'separator' + , + label: 'Quit' + accelerator: 'Command+Q' + click: main.quit + ] + , + # 000 000 000 000 000 0000000 0000000 000 000 + # 000 0 000 000 0000 000 000 000 000 000 000 0 000 + # 000000000 000 000 0 000 000 000 000 000 000000000 + # 000 000 000 000 0000 000 000 000 000 000 000 + # 00 00 000 000 000 0000000 0000000 00 00 + + label: 'Window' + submenu: [ + label: 'Minimize' + accelerator: 'Alt+Cmd+M' + click: (i,win) -> win?.minimize() + , + label: 'Maximize' + accelerator: 'Cmd+Shift+m' + click: (i,win) -> main.toggleMaximize win + , + type: 'separator' + , + label: 'Reload Window' + accelerator: 'Ctrl+Alt+Cmd+L' + click: (i,win) -> main.reloadWin win + , + label: 'Toggle FullScreen' + accelerator: 'Ctrl+Command+Alt+F' + click: (i,win) -> win?.setFullScreen !win.isFullScreen() + ] + , + # 000 000 00000000 000 00000000 + # 000 000 000 000 000 000 + # 000000000 0000000 000 00000000 + # 000 000 000 000 000 + # 000 000 00000000 0000000 000 + + label: 'Help' + role: 'help' + submenu: [] + ] + +module.exports = MainMenu diff --git a/coffee/stage.coffee b/coffee/stage.coffee new file mode 100644 index 0000000..1819e8c --- /dev/null +++ b/coffee/stage.coffee @@ -0,0 +1,42 @@ +# 0000000 000000000 0000000 0000000 00000000 +# 000 000 000 000 000 000 +# 0000000 000 000000000 000 0000 0000000 +# 000 000 000 000 000 000 000 +# 0000000 000 000 000 0000000 00000000 + +log = require './tools/log' +keyinfo = require './tools/keyinfo' + +class Stage + + constructor: (@view) -> + @paused = false + @view.onkeydown = @onKeyDown + @view.onkeyup = @onKeyUp + + start: => @animate() + pause: => @paused = true + resume: => @paused = false + + animate: => + requestAnimationFrame @animate + secs = 1.0/60.0 + if not @paused + step = + delta: secs*1000 + dsecs: secs + @animationStep step + + onKeyDown: (event) => + {mod, key, combo} = keyinfo.forEvent event + return if not combo + return if key == 'right click' # weird right command key + @modKeyComboEventDown? mod, key, combo, event + + onKeyUp: (event) => + {mod, key, combo} = keyinfo.forEvent event + return if not combo + return if key == 'right click' # weird right command key + @modKeyComboEventUp? mod, key, combo, event + +module.exports = Stage diff --git a/coffee/titlebar.coffee b/coffee/titlebar.coffee new file mode 100644 index 0000000..398c88b --- /dev/null +++ b/coffee/titlebar.coffee @@ -0,0 +1,20 @@ +# 000000000 000 000000000 000 00000000 0000000 0000000 00000000 +# 000 000 000 000 000 000 000 000 000 000 000 +# 000 000 000 000 0000000 0000000 000000000 0000000 +# 000 000 000 000 000 000 000 000 000 000 000 +# 000 000 000 0000000 00000000 0000000 000 000 000 000 + +{$} = require './tools/tools' +electron = require 'electron' +ipc = electron.ipcRenderer + +class Titlebar + + constructor: () -> + @elem = $('.titlebar') + @elem.ondblclick = (event) => + console.log window.winID + ipc.send 'maximizeWindow', window.winID + @selected = -1 + +module.exports = Titlebar diff --git a/coffee/tools/ansidiss.coffee b/coffee/tools/ansidiss.coffee new file mode 100644 index 0000000..557e876 --- /dev/null +++ b/coffee/tools/ansidiss.coffee @@ -0,0 +1,165 @@ +# based on code from https://github.com/rburns/ansi-to-html + +log = require './log' +entities = require 'entities' +_ = require 'lodash' + +STYLES = + f0: 'color:#000' # normal intensity + f1: 'color:#E00' + f2: 'color:#0A0' + f3: 'color:#A50' + f4: 'color:#00E' + f5: 'color:#A0A' + f6: 'color:#0AA' + f7: 'color:#AAA' + f8: 'color:#555' # high intensity + f9: 'color:#F55' + f10: 'color:#5F5' + f11: 'color:#FF5' + f12: 'color:#55F' + f13: 'color:#F5F' + f14: 'color:#5FF' + f15: 'color:#FFF' + b0: 'background-color:#000' # normal intensity + b1: 'background-color:#A00' + b2: 'background-color:#0A0' + b3: 'background-color:#A50' + b4: 'background-color:#00A' + b5: 'background-color:#A0A' + b6: 'background-color:#0AA' + b7: 'background-color:#AAA' + b8: 'background-color:#555' # high intensity + b9: 'background-color:#F55' + b10: 'background-color:#5F5' + b11: 'background-color:#FF5' + b12: 'background-color:#55F' + b13: 'background-color:#F5F' + b14: 'background-color:#5FF' + b15: 'background-color:#FFF' + +toHexString = (num) -> + num = num.toString(16) + while num.length < 2 then num = "0#{num}" + num + +[0..5].forEach (red) -> + [0..5].forEach (green) -> + [0..5].forEach (blue) -> + c = 16 + (red * 36) + (green * 6) + blue + r = if red > 0 then red * 40 + 55 else 0 + g = if green > 0 then green * 40 + 55 else 0 + b = if blue > 0 then blue * 40 + 55 else 0 + rgb = (toHexString(n) for n in [r, g, b]).join('') + STYLES["f#{c}"] = "color:##{rgb}" + STYLES["b#{c}"] = "background-color:##{rgb}" + +[0..23].forEach (gray) -> + c = gray+232 + l = toHexString(gray*10 + 8) + STYLES["f#{c}"] = "color:##{l}#{l}#{l}" + STYLES["b#{c}"] = "background-color:##{l}#{l}#{l}" + +# 0000000 000 000 0000000 000 0000000 000 0000000 0000000 +# 000 000 0000 000 000 000 000 000 000 000 000 +# 000000000 000 0 000 0000000 000 000 000 000 0000000 0000000 +# 000 000 000 0000 000 000 000 000 000 000 000 +# 000 000 000 000 0000000 000 0000000 000 0000000 0000000 + +class AnsiDiss + + constructor: () -> + + dissect: (@input) -> + @diss = [] + @text = "" + @tokenize() + [@text, @diss] + + tokenize: () -> + + start = 0 + ansiHandler = 2 + ansiMatch = false + + fg = bg = '' + st = [] + + resetStyle = () -> + fg = '' + bg = '' + st = [] + + addStyle = (style) -> st.push style if style not in st + delStyle = (style) -> _.pull st, style + + addText = (t) => + @text += t + txt = @text.slice start + match = txt.trim() + if match.length + style = '' + style += fg + ';' if fg.length + style += bg + ';' if bg.length + style += st.join ';' if st.length + @diss.push + match: match + start: start + txt.search /[^\s]/ + styl: style + start = @text.length + '' + + toHighIntensity = (c) -> + for i in [0..7] + if c == STYLES["f#{i}"] + return STYLES["f#{8+i}"] + c + + ansiCode = (m, c) => + ansiMatch = true + c = '0' if c.trim().length is 0 + cs = c.trimRight(';').split(';') + for code in cs + code = parseInt code, 10 + switch + when code is 0 then resetStyle() + when code is 1 + addStyle 'font-weight:bold' + fg = toHighIntensity fg + when code is 2 then addStyle 'opacity:0.5' + when code is 4 then addStyle 'text-decoration:underline' + when code is 8 then addStyle 'display:none' + when code is 9 then addStyle 'text-decoration:line-through' + when code is 39 then fg = STYLES["f15"] # default foreground + when code is 49 then bg = STYLES["b0"] # default background + when code is 38 then fg = STYLES["f#{cs[2]}"] # extended fg 38;5;[0-255] + when code is 48 then bg = STYLES["b#{cs[2]}"] # extended bg 48;5;[0-255] + when 30 <= code <= 37 then fg = STYLES["f#{code - 30}"] # normal intensity + when 40 <= code <= 47 then bg = STYLES["b#{code - 40}"] + when 90 <= code <= 97 then fg = STYLES["f#{8+code - 90}"] # high intensity + when 100 <= code <= 107 then bg = STYLES["b#{8+code - 100}"] + when code is 28 then delStyle 'display:none' + when code is 22 + delStyle 'font-weight:bold' + delStyle 'opacity:0.5' + break if code in [38, 48] + '' + + tokens = [ + {pattern: /^\x08+/, sub: ''} + {pattern: /^\x1b\[[012]?K/, sub: ''} + {pattern: /^\x1b\[((?:\d{1,3};?)+|)m/, sub: ansiCode} + {pattern: /^\x1b\[?[\d;]{0,3}/, sub: ''} + {pattern: /^([^\x1b\x08\n]+)/, sub: addText} + ] + + process = (handler, i) => + return if i > ansiHandler and ansiMatch # give ansiHandler another chance if it matches + ansiMatch = false + @input = @input.replace handler.pattern, handler.sub + + while (length = @input.length) > 0 + process(handler, i) for handler, i in tokens + break if @input.length == length + +module.exports = AnsiDiss diff --git a/coffee/tools/drag.coffee b/coffee/tools/drag.coffee new file mode 100644 index 0000000..b8e5d95 --- /dev/null +++ b/coffee/tools/drag.coffee @@ -0,0 +1,100 @@ +# 0000000 00000000 0000000 0000000 +# 000 000 000 000 000 000 000 +# 000 000 0000000 000000000 000 0000 +# 000 000 000 000 000 000 000 000 +# 0000000 000 000 000 000 0000000 + +_ = require 'lodash' + +{def, absPos} = require './tools' +log = require './log' + +error = -> console.error "ERROR: " + ([].slice.call arguments, 0).join " " + +class Drag + + constructor: (cfg) -> + + _.extend @, def cfg, + target : null + handle : null + onStart : null + onMove : null + onStop : null + active : true + cursor : 'move' + + if typeof @target is 'string' + t = document.getElementById @target + if not t? + error 'cant find drag target with id', @target + return + @target = t + if not @target? + error 'cant find drag target' + return + + @dragging = false + @listening = false + @handle = document.getElementById(@handle) if typeof (@handle) is 'string' + @handle = @target unless @handle? + @handle.style.cursor = @cursor + @activate() if @active + return + + dragStart: (event) => + + return if @dragging or not @listening + @dragging = true + @startPos = absPos event + @pos = absPos event + @onStart @, event if @onStart? + @lastPos = absPos event + + event.preventDefault() + + document.addEventListener 'mousemove', @dragMove + document.addEventListener 'mouseup', @dragUp + + dragMove: (event) => + + return if not @dragging + + @pos = absPos event + @delta = @lastPos.to @pos + @deltaSum = @startPos.to @pos + + if @onMove? + @onMove this, event + + @lastPos = @pos + + dragUp: (event) => @dragStop event + + dragStop: (event) => + + return if not @dragging + document.removeEventListener 'mousemove', @dragMove + document.removeEventListener 'mouseup', @dragUp + delete @lastPos + delete @startPos + @onStop this, event if @onStop? and event? + @dragging = false + return + + activate: => + + return if @listening + @listening = true + @handle.addEventListener 'mousedown', @dragStart + return + + deactivate: => + + return if not @listening + @handle.removeEventListener 'mousedown', @dragStart + @listening = false + @dragStop() if @dragging + return + +module.exports = Drag diff --git a/coffee/tools/encode.coffee b/coffee/tools/encode.coffee new file mode 100644 index 0000000..3aa2b07 --- /dev/null +++ b/coffee/tools/encode.coffee @@ -0,0 +1,14 @@ +# 00000000 000 000 0000000 0000000 0000000 00000000 +# 000 0000 000 000 000 000 000 000 000 +# 0000000 000 0 000 000 000 000 000 000 0000000 +# 000 000 0000 000 000 000 000 000 000 +# 00000000 000 000 0000000 0000000 0000000 00000000 + +encode = require('html-entities').XmlEntities.encode + +module.exports = (s) -> + if s + r = encode s + r = r.replace /\s/g, ' ' + else + '' \ No newline at end of file diff --git a/coffee/tools/enspce.coffee b/coffee/tools/enspce.coffee new file mode 100644 index 0000000..36ac2d6 --- /dev/null +++ b/coffee/tools/enspce.coffee @@ -0,0 +1,15 @@ +# 00000000 000 000 0000000 00000000 0000000 00000000 +# 000 0000 000 000 000 000 000 000 +# 0000000 000 0 000 0000000 00000000 000 0000000 +# 000 000 0000 000 000 000 000 +# 00000000 000 000 0000000 000 0000000 00000000 + +module.exports = (s) -> + return "" if not s? + tag = false + for i in [s.length-1..0] + switch s[i] + when '>' then tag = true + when '<' then tag = false + when ' ' then s = s.splice i, 1, " " if not tag + s \ No newline at end of file diff --git a/coffee/tools/fps.coffee b/coffee/tools/fps.coffee new file mode 100644 index 0000000..6b1e3b4 --- /dev/null +++ b/coffee/tools/fps.coffee @@ -0,0 +1,68 @@ + +# 00000000 00000000 0000000 +# 000 000 000 000 +# 000000 00000000 0000000 +# 000 000 000 +# 000 000 0000000 +{ +clamp, +first, +last, +$} = require '../tools/tools' +log = require '../tools/log' +now = require 'performance-now' + +class FPS + + constructor: () -> + + @elem = document.createElement 'div' + @elem.className = 'fps' + @elem.style.display = 'none' + + @canvas = document.createElement 'canvas' + @canvas.className = "fpsCanvas" + @canvas.height = 30*2 + @canvas.width = 130*2 + @elem.appendChild @canvas + + y = parseInt -30/2 + x = parseInt -130/2 + t = "translate3d(#{x}px, #{y}px, 0px) scale3d(0.5, 0.5, 1)" + @canvas.style.transform = t + + @history = [] + @last = now() + + $('.commandline-span').appendChild @elem + window.requestAnimationFrame @draw + + # 0000000 00000000 0000000 000 000 + # 000 000 000 000 000 000 000 0 000 + # 000 000 0000000 000000000 000000000 + # 000 000 000 000 000 000 000 000 + # 0000000 000 000 000 000 00 00 + + draw: => + time = now() + @history.push time-@last + @history.shift() while @history.length > 260 + @canvas.height = @canvas.height + ctx = @canvas.getContext '2d' + for i in [0...@history.length] + ms = Math.max 0, @history[i]-17 + red = parseInt 32 + (255-32)*clamp 0,1, (ms-16)/16 + green = parseInt 32 + (255-32)*clamp 0,1, (ms-32)/32 + ctx.fillStyle = "rgb(#{red}, #{green}, 32)" + h = Math.min ms, 60 + ctx.fillRect 260-@history.length+i, 60-h, 2, h + @last = time + window.requestAnimationFrame @draw + + toggle: -> + @elem.style.display = @elem.style.display == 'none' and 'unset' or 'none' + @history.push 49 + window.setState 'fps', @elem.style.display != 'none' + +module.exports = FPS + diff --git a/coffee/tools/keyinfo.coffee b/coffee/tools/keyinfo.coffee new file mode 100644 index 0000000..3581160 --- /dev/null +++ b/coffee/tools/keyinfo.coffee @@ -0,0 +1,51 @@ +# 000 000 00000000 000 000 000 000 0000000 00 00 00000000 +# 000 000 000 000 000 0000 000 000 000 000 000 000 +# 0000000 0000000 00000 000 0 000 000000000 000000000 0000000 +# 000 000 000 000 000 0000 000 000 000 0 000 000 +# 000 000 00000000 000 000 000 000 000 000 000 00000000 + +keycode = require 'keycode' + +class Keyinfo + + @modifierNames = ['shift', 'ctrl', 'alt', 'command'] + @modifierChars = ['⇧', '^', '⌥', '⌘'] + + @isModifier: (keyname) -> keyname in @modifierNames + + @modifiersForEvent: (event) => + mods = [] + mods.push 'command' if event.metaKey + mods.push 'alt' if event.altKey + mods.push 'ctrl' if event.ctrlKey + mods.push 'shift' if event.shiftKey + return mods.join '+' + + @join: () -> + args = [].slice.call arguments, 0 + args = args.filter (e) -> e.length + args.join '+' + + @comboForEvent: (event) => + key = keycode event + if key not in @modifierNames + return @join @modifiersForEvent(event), key + return "" + + @keynameForEvent: (event) => + name = keycode event + return "" if name in ["left command", "right command", "ctrl", "alt", "shift"] + name + + @forEvent: (event) => + mod: @modifiersForEvent event + key: @keynameForEvent event + combo: @comboForEvent event + + @short: (combo) -> + for i in [0...@modifierNames.length] + modifierName = @modifierNames[i]+'+' + combo = combo.replace modifierName, @modifierChars[i] + combo.toUpperCase() + +module.exports = Keyinfo diff --git a/coffee/tools/log.coffee b/coffee/tools/log.coffee new file mode 100644 index 0000000..778adde --- /dev/null +++ b/coffee/tools/log.coffee @@ -0,0 +1,21 @@ +#000 0000000 0000000 +#000 000 000 000 +#000 000 000 000 0000 +#000 000 000 000 000 +#0000000 0000000 0000000 + +str = require './str' + +log = -> + console.log (str(s) for s in [].slice.call arguments, 0).join " " + +logScroll = -> + s = (str(s) for s in [].slice.call arguments, 0).join " " + console.log s + window.logview?.appendText s + +if window? + module.exports = logScroll +else + module.exports = log + \ No newline at end of file diff --git a/coffee/tools/matchr.coffee b/coffee/tools/matchr.coffee new file mode 100644 index 0000000..65f053f --- /dev/null +++ b/coffee/tools/matchr.coffee @@ -0,0 +1,172 @@ +# 00 00 0000000 000000000 0000000 000 000 00000000 +# 000 000 000 000 000 000 000 000 000 000 +# 000000000 000000000 000 000 000000000 0000000 +# 000 0 000 000 000 000 000 000 000 000 000 +# 000 000 000 000 000 0000000 000 000 000 000 +{ +last +} = require './tools' +_ = require 'lodash' + +# 0000000 0000000 000 000 00000000 000 0000000 +# 000 000 000 0000 000 000 000 000 +# 000 000 000 000 0 000 000000 000 000 0000 +# 000 000 000 000 0000 000 000 000 000 +# 0000000 0000000 000 000 000 000 0000000 + +# convert the patterns object to a list of [RegExp(key), value] pairs + +config = (patterns) -> ( [new RegExp(p), a] for p,a of patterns ) + +sortRanges = (rgs) -> + rgs.sort (a,b) -> + if a.start == b.start + if a.match.length == b.match.length + a.index - b.index + else + a.match.length - b.match.length + else + a.start - b.start + +# 00000000 0000000 000 000 0000000 00000000 0000000 +# 000 000 000 000 0000 000 000 000 000 +# 0000000 000000000 000 0 000 000 0000 0000000 0000000 +# 000 000 000 000 000 0000 000 000 000 000 +# 000 000 000 000 000 000 0000000 00000000 0000000 + +# accepts a list of [regexp, value(s)] pairs and a string +# returns a list of objects with information about the matches: + +# match: the matched substring +# start: position of match in str +# value: the value for the match +# index: the index of the regexp + +# the objects are sorted by start, match.length and index + +# if the regexp has capture groups then +# the value for the match of the nth group is +# the nth item of values(s) if value(s) is an array +# the nth [key, value] pair if value(s) is an object + +ranges = (regexes, str) -> + + rgs = [] + return rgs if not str? + for r in [0...regexes.length] + reg = regexes[r][0] + arg = regexes[r][1] + i = 0 + s = str + while s.length + match = reg.exec s + break if not match? + if match.length == 1 + rgs.push + start: match.index + i + match: match[0] + value: arg + index: r + i += match.index + match[0].length + s = str.slice i + else + gs = 0 + for j in [0..match.length-2] + value = arg + if _.isArray(value) and j < value.length then value = value[j] + else if _.isObject(value) and j < _.size(value) + value = [_.keys(value)[j], value[_.keys(value)[j]]] + gi = match[0].slice(gs).indexOf match[j+1] + rgs.push + start: match.index + i + gs + gi + match: match[j+1] + value: value + index: r + gs += match[j+1].length + i += match.index + match[0].length + s = str.slice i + sortRanges rgs + +# 0000000 000 0000000 0000000 00000000 0000000 000000000 +# 000 000 000 000 000 000 000 000 +# 000 000 000 0000000 0000000 0000000 000 000 +# 000 000 000 000 000 000 000 000 +# 0000000 000 0000000 0000000 00000000 0000000 000 + +# accepts a list of ranges +# returns a list of objects: + +# match: the matched substring +# start: position of match in str +# cls: list of classnames +# clss: string of classnames joined with a space + +# with none of the [start, start+match.length] ranges overlapping + +dissect = (ranges, opt={join:false}) -> + return [] if not ranges.length + # console.log "dissect -- #{JSON.stringify ranges}" + di = [] + for ri in [0...ranges.length] + rg = ranges[ri] + di.push [rg.start, ri] + di.push [rg.start + rg.match.length] + di.sort (a,b) -> + if a[0]==b[0] + a[1]-b[1] + else + a[0]-b[0] + d = [] + si = -1 + for i in [0...di.length-1] + if di[i][0] > si + si = di[i][0] + d.push + start: si + cid: 0 + cls: [] + + p = 0 + for ri in [0...ranges.length] + rg = ranges[ri] + while d[p].start < rg.start + p += 1 + pn = p + while d[pn].start < rg.start+rg.match.length + if (d[pn].cid < rg.index or opt.join) and rg.value? + if not rg.value.split? + for r in rg.value + continue if not r.split? + for c in r.split '.' + d[pn].cls.push c if d[pn].cls.indexOf(c) < 0 + else + for c in rg.value.split '.' + d[pn].cls.push c if d[pn].cls.indexOf(c) < 0 + d[pn].cid = rg.index + if pn+1 < d.length + if not d[pn].match + d[pn].match = rg.match.substr d[pn].start-rg.start, d[pn+1].start-d[pn].start + pn += 1 + else + if not d[pn].match + d[pn].match = rg.match.substr d[pn].start-rg.start + break + + d = d.filter (i) -> i.match?.trim().length + for i in d + i.clss = i.cls.join ' ' + if d.length > 1 + for i in [d.length-2..0] + if d[i].start + d[i].match.length == d[i+1].start + if d[i].clss == d[i+1].clss + d[i].match += d[i+1].match + d.splice i+1, 1 + + # console.log "dissect ==", JSON.stringify d + d + +module.exports = + config: config + ranges: ranges + dissect: dissect + sortRanges: sortRanges diff --git a/coffee/tools/pos.coffee b/coffee/tools/pos.coffee new file mode 100644 index 0000000..9ea27d9 --- /dev/null +++ b/coffee/tools/pos.coffee @@ -0,0 +1,94 @@ +#00000000 0000000 0000000 +#000 000 000 000 000 +#00000000 000 000 0000000 +#000 000 000 000 +#000 0000000 0000000 + +class Pos + + constructor: (@x, @y) -> + + copy: => new Pos @x, @y + + plus: (val) => + newPos = @copy() + if val? + newPos.x += val.x unless isNaN(val.x) + newPos.y += val.y unless isNaN(val.y) + newPos + + minus: (val) => + newPos = @copy() + if val? + newPos.x -= val.x unless isNaN(val.x) + newPos.y -= val.y unless isNaN(val.y) + newPos + + times: (val) => @copy().scale val + + clamped: (lower, upper) => @copy().clamp lower, upper + + to: (other) => other.minus @ + mid: (other) => @plus(other).scale 0.5 + + min: (val) => + newPos = @copy() + return newPos unless val? + newPos.x = val.x if not isNaN(val.x) and @x > val.x + newPos.y = val.y if not isNaN(val.y) and @y > val.y + newPos + + max: (val) => + newPos = @copy() + return newPos unless val? + newPos.x = val.x if not isNaN(val.x) and @x < val.x + newPos.y = val.y if not isNaN(val.y) and @y < val.y + newPos + + length: => return Math.sqrt @square() + square: => (@x * @x) + (@y * @y) + distSquare: (o) => @minus(o).square() + dist: (o) => Math.sqrt @distSquare(o) + same: (o) => @x == o?.x and @y == o?.y + notSame: (o) => @x != o?.x or @y != o?.y + + check: => + newPos = @copy() + newPos.x = 0 if isNaN(newPos.x) + newPos.y = 0 if isNaN(newPos.y) + newPos + + _str: => + s = ("" if @y?) or "NaN>" + + #_________________________________________________________ destructive + + scale: (val) => + @x *= val + @y *= val + @ + + mul: (other) => + @x *= other.x + @y *= other.y + @ + + add: (other) => + @x += other.x + @y += other.y + @ + + sub: (other) => + @x -= other.x + @y -= other.y + @ + + clamp: (lower, upper) => + if lower? and upper? + {clamp} = require './tools' + @x = clamp(lower.x, upper.x, @x) + @y = clamp(lower.y, upper.y, @y) + @ + +module.exports = (x,y) -> new Pos x,y diff --git a/coffee/tools/prefs.coffee b/coffee/tools/prefs.coffee new file mode 100644 index 0000000..6e439aa --- /dev/null +++ b/coffee/tools/prefs.coffee @@ -0,0 +1,76 @@ +# 00000000 00000000 00000000 00000000 0000000 +# 000 000 000 000 000 000 000 +# 00000000 0000000 0000000 000000 0000000 +# 000 000 000 000 000 000 +# 000 000 000 00000000 000 0000000 + +_ = require 'lodash' +noon = require 'noon' +log = require './log' +fs = require 'fs' + +class Prefs + + @timeout = 2000 + + # 000 000 000 000 000000000 + # 000 0000 000 000 000 + # 000 000 0 000 000 000 + # 000 000 0000 000 000 + # 000 000 000 000 000 + + @init: (path, defs={}) -> + + if window? + @ipc = require('electron').ipcRenderer + else + @path = path + @timer = null + nconf.use 'user', + type: 'file' + format: + parse: noon.parse + stringify: (o,n,i) -> noon.stringify o, {indent: i, maxalign: 8} + file: path + nconf.defaults defs + + # 0000000 00000000 000000000 0000000 00000000 000000000 + # 000 000 000 000 000 000 000 + # 000 0000 0000000 000 0000000 0000000 0000000 000 + # 000 000 000 000 000 000 000 000 + # 0000000 00000000 000 0000000 00000000 000 + + @get: (key, value) -> + if @ipc? + @ipc.sendSync 'prefGet', key, value + else + nconf.get(key) ? value + + @set: (key, value) -> + if @ipc? + @ipc.send 'prefSet', key, value + else + clearTimeout @timer if @timer + @timer = setTimeout @save, @timeout + + if value? + nconf.set key, value + else + nconf.clear key + + @del: (key, value) -> @set key + + # 0000000 0000000 000 000 00000000 + # 000 000 000 000 000 000 + # 0000000 000000000 000 000 0000000 + # 000 000 000 000 000 + # 0000000 000 000 0 00000000 + + @save: (cb) => + clearTimeout @timer if @timer + @timer = null + nconf.save (err) => + log "nconf save error:", err if err? + cb? !err? + +module.exports = Prefs diff --git a/coffee/tools/profile.coffee b/coffee/tools/profile.coffee new file mode 100644 index 0000000..93e59b3 --- /dev/null +++ b/coffee/tools/profile.coffee @@ -0,0 +1,25 @@ +# 00000000 00000000 0000000 00000000 000 000 00000000 +# 000 000 000 000 000 000 000 000 000 000 +# 00000000 0000000 000 000 000000 000 000 0000000 +# 000 000 000 000 000 000 000 000 000 +# 000 000 000 0000000 000 000 0000000 00000000 + +now = require 'performance-now' +log = require './log' + +start = undefined +s_msg = undefined + +profile = (msg) -> + + if start? and s_msg.length + ms = (now()-start).toFixed 0 + if ms > 1000 + log "#{s_msg} in #{(ms/1000).toFixed(3)} sec" + else + log "#{s_msg} in #{ms} ms" + + start = now() + s_msg = msg + +module.exports = profile diff --git a/coffee/tools/ranges.coffee b/coffee/tools/ranges.coffee new file mode 100644 index 0000000..316e0ab --- /dev/null +++ b/coffee/tools/ranges.coffee @@ -0,0 +1,23 @@ +# 00000000 0000000 000 000 0000000 00000000 0000000 +# 000 000 000 000 0000 000 000 000 000 +# 0000000 000000000 000 0 000 000 0000 0000000 0000000 +# 000 000 000 000 000 0000 000 000 000 000 +# 000 000 000 000 000 000 0000000 00000000 0000000 + +module.exports = + + # 0000000 0000000 00000000 000000000 + # 000 000 000 000 000 000 + # 0000000 000 000 0000000 000 + # 000 000 000 000 000 000 + # 0000000 0000000 000 000 000 + + sort: (rgs) -> + rgs.sort (a,b) -> + if a[0]!=b[0] + a[0]-b[0] + else + if a[1][0]!=b[1][0] + a[1][0]-b[1][0] + else + a[1][1]-b[1][1] diff --git a/coffee/tools/salt.coffee b/coffee/tools/salt.coffee new file mode 100644 index 0000000..698f52b --- /dev/null +++ b/coffee/tools/salt.coffee @@ -0,0 +1,25 @@ + +# 0000000 0000000 000 000000000 +# 000 000 000 000 000 +# 0000000 000000000 000 000 +# 000 000 000 000 000 +# 0000000 000 000 0000000 000 + +font = require './font.json' +_ = require 'lodash' + +salt = (text) -> + + s = text.toLowerCase().trim() + + cs = [] + for c in s + if font[c]? + cs.push font[c] + + zs = _.zip.apply(null, cs) + rs = _.map(zs, (j) -> j.join(' ')) + + rs.join '\n' + +module.exports = salt \ No newline at end of file diff --git a/coffee/tools/str.coffee b/coffee/tools/str.coffee new file mode 100644 index 0000000..ea7abea --- /dev/null +++ b/coffee/tools/str.coffee @@ -0,0 +1,21 @@ +# 0000000 000000000 00000000 +# 000 000 000 000 +# 0000000 000 0000000 +# 000 000 000 000 +# 0000000 000 000 000 + +noon = require 'noon' + +str = (o) -> + return 'null' if not o? + if typeof o == 'object' + if o._str? and typeof(o._str) == 'function' + o._str() + else + s = noon.stringify o, + circular: true + "\n#{s}" + else + String o + +module.exports = str \ No newline at end of file diff --git a/coffee/tools/tools.coffee b/coffee/tools/tools.coffee new file mode 100644 index 0000000..a21bc5b --- /dev/null +++ b/coffee/tools/tools.coffee @@ -0,0 +1,199 @@ +# 000000000 0000000 0000000 000 0000000 +# 000 000 000 000 000 000 000 +# 000 000 000 000 000 000 0000000 +# 000 000 000 000 000 000 000 +# 000 0000000 0000000 0000000 0000000 + +pos = require './pos' +log = require './log' +_ = require 'lodash' +sfmt = require 'sprintf-js' +path = require 'path' +os = require 'os' +fs = require 'fs' + +module.exports = + + # 0000000 000 0000000 000000000 + # 000 000 000 000 000 + # 000 000 000 000 000 + # 000 000 000 000 000 + # 0000000 000 0000000 000 + + def: (c,d) -> + if c? + _.defaults(_.clone(c), d) + else if d? + _.clone(d) + else + {} + + del: (l,e) -> _.remove l, (n) -> n == e + + # 0000000 00000000 00000000 0000000 000 000 + # 000 000 000 000 000 000 000 000 000 000 + # 000000000 0000000 0000000 000000000 00000 + # 000 000 000 000 000 000 000 000 000 + # 000 000 000 000 000 000 000 000 000 + + last: (a) -> a[a.length-1] if a?.length + first: (a) -> a[0] if a?.length + + startOf: (r) -> r[0] + endOf: (r) -> r[0] + Math.max 1, r[1]-r[0] + + # 000 000 0000000 000 000 000 00000000 + # 000 000 000 000 000 000 000 000 + # 000 000 000000000 000 000 000 0000000 + # 000 000 000 000 000 000 000 + # 0 000 000 0000000 0000000 00000000 + + clamp: (r1, r2, v) -> + if r1 > r2 + [r1,r2] = [r2,r1] + v = Math.max(v, r1) if r1? + v = Math.min(v, r2) if r2? + v + + absMax: (a,b) -> if Math.abs(a) >= Math.abs(b) then a else b + absMin: (a,b) -> if Math.abs(a) < Math.abs(b) then a else b + + randInt: (r) -> Math.floor Math.random() * r + + shortCount: (v) -> + v = parseInt v + switch + when v > 999999 then "#{Math.floor v/1000000}M" + when v > 999 then "#{Math.floor v/1000}k" + else "#{v}" + + rad2deg: (r) -> 180 * r / Math.PI + + # 00000000 0000000 000000000 000 000 + # 000 000 000 000 000 000 000 + # 00000000 000000000 000 000000000 + # 000 000 000 000 000 000 + # 000 000 000 000 000 000 + + resolve: (p) -> path.normalize path.resolve p.replace /^\~/, process.env.HOME + unresolve: (p) -> p.replace os.homedir(), "~" + fileName: (p) -> path.basename p, path.extname p + extName: (p) -> path.extname(p).slice 1 + + fileList: (paths, opt={ignoreHidden: true, logError: true}) -> + files = [] + paths = [paths] if typeof paths == 'string' + for p in paths + try + [p,l] = p.split ':' + stat = fs.statSync p + if stat.isDirectory() + dirfiles = fs.readdirSync p + dirfiles = (path.join(p,f) for f in dirfiles) + dirfiles = (f for f in dirfiles when fs.statSync(f).isFile()) + if opt.ignoreHidden + dirfiles = dirfiles.filter (f) -> not path.basename(f).startsWith '.' + files = files.concat dirfiles + + else if stat.isFile() + if opt.ignoreHidden and path.basename(p).startsWith '.' + continue + p += ":#{l}" if l? + files.push p + catch err + if opt.logError + log 'tools.fileList.error:', err + files + + fileExists: (file) -> + try + if fs.statSync(file).isFile() + fs.accessSync file, fs.R_OK | fs.W_OK + return true + catch + return false + + dirExists: (dir) -> + try + if fs.statSync(dir).isDirectory() + fs.accessSync dir, fs.R_OK + return true + catch + return false + + relative: (absolute, to) -> + return absolute if not absolute?.startsWith '/' + d = path.normalize path.resolve to.replace /\~/, process.env.HOME + r = path.relative d, absolute + if r.startsWith '../../' + unresolved = absolute.replace(os.homedir(), "~") + if unresolved.length < r.length + r = unresolved + if absolute.length < r.length + r = absolute + r + + swapExt: (p, ext) -> path.join(path.dirname(p), path.basename(p, path.extname(p))) + ext + + # 0000000 0000000 0000000 + # 000 000 000 + # 000 0000000 0000000 + # 000 000 000 + # 0000000 0000000 0000000 + + setStyle: (selector, key, value, ssid=0) -> + for rule in document.styleSheets[ssid].cssRules + if rule.selectorText == selector + rule.style[key] = value + return + + getStyle: (selector, key, value, ssid=0) -> + for rule in document.styleSheets[ssid].cssRules + if rule.selectorText == selector + return rule.style[key] + return value + + # 0000000 0000000 00 00 + # 000 000 000 000 000 000 + # 000 000 000 000 000000000 + # 000 000 000 000 000 0 000 + # 0000000 0000000 000 000 + + $: (idOrClass,e=document) -> + if idOrClass.startsWith '.' + e.getElementsByClassName(idOrClass.substr(1).split('.').join " ")[0] + else + e.getElementById idOrClass + + absPos: (event) -> + event = if event? then event else window.event + if isNaN window.scrollX + return pos(event.clientX + document.documentElement.scrollLeft + document.body.scrollLeft, + event.clientY + document.documentElement.scrollTop + document.body.scrollTop) + else + return pos(event.clientX + window.scrollX, event.clientY + window.scrollY) + + sw: () -> document.body.clientWidth + sh: () -> document.body.clientHeight + +# 0000000 000000000 00000000 000 000 000 0000000 +# 000 000 000 000 000 0000 000 000 +# 0000000 000 0000000 000 000 0 000 000 0000 +# 000 000 000 000 000 000 0000 000 000 +# 0000000 000 000 000 000 000 000 0000000 + +if not String.prototype.splice + String.prototype.splice = (start, delCount, newSubStr='') -> + @slice(0, start) + newSubStr + @slice(start + Math.abs(delCount)) + String.prototype.strip = String.prototype.trim + String.prototype.fmt = -> sfmt.vsprintf @, [].slice.call arguments + +# 0000000 00000000 00000000 0000000 000 000 +# 000 000 000 000 000 000 000 000 000 000 +# 000000000 0000000 0000000 000000000 00000 +# 000 000 000 000 000 000 000 000 000 +# 000 000 000 000 000 000 000 000 000 + +if not Array.prototype.reversed + Array.prototype.reversed = -> + _.clone(@).reverse() diff --git a/coffee/tools/walker.coffee b/coffee/tools/walker.coffee new file mode 100644 index 0000000..5a0d4bd --- /dev/null +++ b/coffee/tools/walker.coffee @@ -0,0 +1,118 @@ +# 000 000 0000000 000 000 000 00000000 00000000 +# 000 0 000 000 000 000 000 000 000 000 000 +# 000000000 000000000 000 0000000 0000000 0000000 +# 000 000 000 000 000 000 000 000 000 000 +# 00 00 000 000 0000000 000 000 00000000 000 000 +{ +fileExists, +dirExists, +relative, +resolve} = require './tools' +log = require './log' +walkdir = require 'walkdir' +path = require 'path' +fs = require 'fs' + +class Walker + + constructor: (@cfg) -> + + @cfg.files = [] + @cfg.stats = [] + @cfg.maxDepth ?= 3 + @cfg.dotFiles ?= false + @cfg.includeDirs ?= true + @cfg.maxFiles ?= 500 + @cfg.ignore ?= ['node_modules', 'app', 'img', 'dist', 'build', 'Library', 'Applications'] + @cfg.include ?= ['.konrad.noon', '.gitignore', '.npmignore'] + @cfg.ignoreExt ?= ['.app'] + @cfg.includeExt ?= ['.coffee', '.js', '.styl', '.css', '.pug', '.jade', '.html', + '.md', '.txt', '.noon', '.json', '.cpp', '.cc', '.c', '.h', '.hpp', '.sh', '.py'] + # log "walker", @cfg + + # 0000000 000000000 0000000 00000000 000000000 + # 000 000 000 000 000 000 000 + # 0000000 000 000000000 0000000 000 + # 000 000 000 000 000 000 000 + # 0000000 000 000 000 000 000 000 + + start: -> + # profile 'walker start' + try + dir = @cfg.root + @walker = walkdir.walk dir, max_depth: @cfg.maxDepth + onWalkerPath = (cfg) -> (p,stat) -> + name = path.basename p + extn = path.extname p + + if cfg.filter?(p) + return @ignore p + else if name in ['.DS_Store', 'Icon\r'] or extn in ['.pyc'] + return @ignore p + else if cfg.includeDir? and path.dirname(p) == cfg.includeDir + cfg.files.push p + cfg.stats.push stat + @ignore p if name in cfg.ignore + @ignore p if name.startsWith('.') and not cfg.dotFiles + else if name in cfg.ignore + return @ignore p + else if name in cfg.include + cfg.files.push p + cfg.stats.push stat + else if name.startsWith '.' + if cfg.dotFiles + cfg.files.push p + cfg.stats.push stat + else + return @ignore p + else if extn in cfg.ignoreExt + return @ignore p + else if extn in cfg.includeExt or cfg.includeExt.indexOf('') >= 0 + cfg.files.push p + cfg.stats.push stat + else if stat.isDirectory() + if p != cfg.root and cfg.includeDirs + cfg.files.push p + cfg.stats.push stat + + cfg.path? p, stat + if stat.isDirectory() + if cfg.includeDirs + cfg.dir? p, stat + else + if path.extname(p) in cfg.includeExt or path.basename(p) in cfg.include or cfg.includeExt.indexOf('') >= 0 + cfg.file? p, stat + + if cfg.files.length > cfg.maxFiles + # log 'max files reached', @end? + @end() + + @walker.on 'path', onWalkerPath @cfg + @walker.on 'end', => @cfg.done? @cfg.files, @cfg.stats + + catch err + log "walker.start.error: #{err} dir: #{dir}" + log "#{err.stack}" + + stop: -> + @walker?.end() + @walker = null + + # 00000000 0000000 0000000 000 000 0000000 0000000 00000000 + # 000 000 000 000 000 000 000 000 000 000 000 + # 00000000 000000000 000 0000000 000000000 000 0000 0000000 + # 000 000 000 000 000 000 000 000 000 000 000 + # 000 000 000 0000000 000 000 000 000 0000000 00000000 + + @packagePath: (p) -> + while p.length and p not in ['.', '/'] + if fs.existsSync path.join p, 'package.noon' + return resolve p + if fs.existsSync path.join p, 'package.json' + return resolve p + if fs.existsSync path.join p, '.git' + return resolve p + p = path.dirname p + null + +module.exports = Walker diff --git a/coffee/window.coffee b/coffee/window.coffee new file mode 100644 index 0000000..310ef38 --- /dev/null +++ b/coffee/window.coffee @@ -0,0 +1,63 @@ +# 000 000 000 000 000 0000000 0000000 000 000 +# 000 0 000 000 0000 000 000 000 000 000 000 0 000 +# 000000000 000 000 0 000 000 000 000 000 000000000 +# 000 000 000 000 0000 000 000 000 000 000 000 +# 00 00 000 000 000 0000000 0000000 00 00 +{ +last, +sw,sh,$, +fileList, +fileExists, +del,clamp, +resolve} = require './tools/tools' +Kiki = require './kiki' +keyinfo = require './tools/keyinfo' +log = require './tools/log' +str = require './tools/str' +_ = require 'lodash' +fs = require 'fs' +path = require 'path' +electron = require 'electron' +pkg = require '../package.json' + +ipc = electron.ipcRenderer +remote = electron.remote +BrowserWindow = remote.BrowserWindow +winID = null + +# 000 00000000 0000000 +# 000 000 000 000 +# 000 00000000 000 +# 000 000 000 +# 000 000 0000000 + +ipc.on 'setWinID', (event, id) => winID = window.winID = id + +# 00000000 00000000 0000000 000 0000000 00000000 +# 000 000 000 000 000 000 000 +# 0000000 0000000 0000000 000 000 0000000 +# 000 000 000 000 000 000 000 +# 000 000 00000000 0000000 000 0000000 00000000 + +screenSize = => electron.screen.getPrimaryDisplay().workAreaSize + +window.onresize = => window.stage.resized() +window.onunload = => +window.onload = => + window.stage = new Kiki $(".stage") + window.stage.start() + +# 0000000 0000000 00000000 00000000 00000000 000 000 0000000 000 000 0000000 000000000 +#000 000 000 000 000 000 0000 000 000 000 000 000 000 000 +#0000000 000 0000000 0000000 0000000 000 0 000 0000000 000000000 000 000 000 +# 000 000 000 000 000 000 000 0000 000 000 000 000 000 000 +#0000000 0000000 000 000 00000000 00000000 000 000 0000000 000 000 0000000 000 + +screenShot = -> + win = BrowserWindow.fromId winID + win.capturePage (img) -> + file = 'screenShot.png' + remote.require('fs').writeFile file, img.toPng(), (err) -> + log 'saving screenshot failed', err if err? + log "screenshot saved to #{file}" + \ No newline at end of file diff --git a/coffee/world.coffee b/coffee/world.coffee index faec886..802dea3 100644 --- a/coffee/world.coffee +++ b/coffee/world.coffee @@ -695,7 +695,6 @@ class World extends Actor d = stone.position.minus(@player.camera.getPosition()).length() if d < 1.0 - console.log 'd', d stone.mesh.material.orig_opacity = stone.mesh.material.opacity if not stone.mesh.material.orig_opacity? stone.mesh.material.opacity = 0.2 + d * 0.5 else if stone.mesh.material.orig_opacity? diff --git a/package.noon b/package.noon index 66f4b61..0699610 100644 --- a/package.noon +++ b/package.noon @@ -1,10 +1,53 @@ -name kiki -main js/main.js +name kiki +productName kiki +main js/app.js +version 0.1.0 +preferGlobal true +bin + kiki ./bin/kiki +scripts + dist ./bin/build.sh && build +repository + type git + url git+https://github.com/monsterkodi/kiki.git +keywords +license Unlicense +author monsterkodi +maintainers + . + name monsterkodi + email monsterkodi@gmx.net +bugs + url https://github.com/monsterkodi/kiki/issues +build + appId net.monsterkodi.kiki + app-category-type public.app-category.utilities + asar false + productName kiki + mac + background build/background.png + target + dmg + contents + . + x 100 + y 180 + type link + path /Applications + . + x 100 + y 70 + type file +devDependencies + electron-builder >=7.11.4 + electron-prebuilt >=1.4.3 dependencies - coffee-script ^1.10.0 - howler ^2.0.0 - lodash ^4.13.1 - noon >=1.0.12 - performance-now ^0.2.0 - sprintf-js ^1.0.3 - underscore.string ^3.3.4 \ No newline at end of file + coffee-script ^1.10.0 + howler ^2.0.0 + keycode ^2.1.7 + lodash ^4.13.1 + noon >=1.0.12 + performance-now ^0.2.0 + sprintf-js ^1.0.3 + three 0.79.0 + underscore.string ^3.3.4 \ No newline at end of file diff --git a/pug/index.pug b/pug/index.pug new file mode 100644 index 0000000..925f062 --- /dev/null +++ b/pug/index.pug @@ -0,0 +1,11 @@ +doctype html +html(lang="en") + head + meta(charset='utf-8') + title kiki + link(rel='stylesheet' href='css/style.css' type='text/css') + script(src='./node_modules/three/build/three.js') + body + .stage(tabindex="0") + script. + require('./js/window.js') diff --git a/styl/about.styl b/styl/about.styl new file mode 100644 index 0000000..7b3e13f --- /dev/null +++ b/styl/about.styl @@ -0,0 +1,64 @@ +// 0000000 0000000 0000000 000 000 000000000 +// 000 000 000 000 000 000 000 000 000 +// 000000000 0000000 000 000 000 000 000 +// 000 000 000 000 000 000 000 000 000 +// 000 000 0000000 0000000 0000000 000 + +clr = {} +clr.black = #000000 +clr.gray-most-dark = #181818 +clr.gray-dark = #333 +clr.gray-medium = #666666 +clr.gray = #888888 +clr.white = #ffffff +clr.yellow = #ffff00 +clr.orange = #ff8800 +clr.red = #ff0000 +clr.green = #00aa00 +clr.blue-medium = #6666ff +clr.blue-bright = #8888ff +clr.blue-very-bright = #aaaaff + +clr.background = clr.gray-dark + +body + font-family Verdana + background-color: clr.background + color: clr.white + +::selection + color: clr.yellow + +body + margin 0px + overflow hidden + +a + text-decoration none + color: clr.blue-medium + &:hover + color: clr.orange + +#stage + position absolute + top 0 + bottom 0 + left 0 + right 0 + +#icon + margin-top 50px + margin-bottom 10px + padding-left 30px + cursor pointer + +#name + font-weight bold + font-size 60px + display none + +#version + font-size 20px + color: clr.gray-medium + &:hover + color: clr.blue-medium diff --git a/styl/style.styl b/styl/style.styl new file mode 100644 index 0000000..50a5044 --- /dev/null +++ b/styl/style.styl @@ -0,0 +1,50 @@ + +// 0000000 000000000 000 000 000 +// 000 000 000 000 000 +// 0000000 000 00000 000 +// 000 000 000 000 +// 0000000 000 000 0000000 + +fill-abs() + position absolute + bottom 0 + right 0 + left 0 + top 0 + +pre + margin 0 + +// 0000000 0000000 0000000 000 000 +// 000 000 000 000 000 000 000 000 +// 0000000 000 000 000 000 00000 +// 000 000 000 000 000 000 000 +// 0000000 0000000 0000000 000 + +* + outline-width 0 + +body + fill-abs() + margin 0 + background #000 + color #fff + overflow hidden + font-family font + -webkit-user-select none + -webkit-app-region drag + +// 0000000 000000000 0000000 0000000 00000000 +// 000 000 000 000 000 000 +// 0000000 000 000000000 000 0000 0000000 +// 000 000 000 000 000 000 000 +// 0000000 000 000 000 0000000 00000000 + +.stage + overflow hidden + position absolute + left 0 + right 0 + bottom 0 + top 0 + box-sizing border-box