standalone

This commit is contained in:
monsterkodi 2016-10-10 18:28:35 +02:00
parent 233ab81967
commit 9b85e2a5b3
37 changed files with 1984 additions and 20 deletions

11
.gitignore vendored
View File

@ -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

View File

@ -1 +1,10 @@
coffee . ext js . replace .. coffee/ js/
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/

23
app/package.noon Normal file
View File

@ -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

8
bin/build.sh Executable file
View File

@ -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 ..

36
bin/kiki Executable file
View File

@ -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)" "$@" &

BIN
build/background.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.3 KiB

BIN
build/background@2x.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
build/icon.icns Normal file

Binary file not shown.

View File

@ -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

319
coffee/app.coffee Normal file
View File

@ -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

View File

@ -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 @

View File

@ -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

85
coffee/mainmenu.coffee Normal file
View File

@ -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

42
coffee/stage.coffee Normal file
View File

@ -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

20
coffee/titlebar.coffee Normal file
View File

@ -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

View File

@ -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

100
coffee/tools/drag.coffee Normal file
View File

@ -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

View File

@ -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, '&nbsp;'
else
''

View File

@ -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, "&nbsp;" if not tag
s

68
coffee/tools/fps.coffee Normal file
View File

@ -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

View File

@ -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

21
coffee/tools/log.coffee Normal file
View File

@ -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

172
coffee/tools/matchr.coffee Normal file
View File

@ -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

94
coffee/tools/pos.coffee Normal file
View File

@ -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 = ("<x:#{@x} " if @x?) or "<NaN "
s += ("y:#{@y}>" 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

76
coffee/tools/prefs.coffee Normal file
View File

@ -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

View File

@ -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

View File

@ -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]

25
coffee/tools/salt.coffee Normal file
View File

@ -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

21
coffee/tools/str.coffee Normal file
View File

@ -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

199
coffee/tools/tools.coffee Normal file
View File

@ -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()

118
coffee/tools/walker.coffee Normal file
View File

@ -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

63
coffee/window.coffee Normal file
View File

@ -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}"

View File

@ -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?

View File

@ -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
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

11
pug/index.pug Normal file
View File

@ -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')

64
styl/about.styl Normal file
View File

@ -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

50
styl/style.styl Normal file
View File

@ -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