Compare commits

..

6 commits

Author SHA1 Message Date
Iam Naughton Phier
f8134f41d2 Fix: Font Picker now shows font chosen and saves font. 2015-03-27 18:10:58 +02:00
Iam Naughton Phier
16207fc2ae avoid confusion over which is "documentation" and which is code 2015-03-27 15:05:45 +02:00
Iam Naughton Phier
fc8693be0b Removed cakefile as it is unused in this setup (and too complex) 2015-03-27 14:42:02 +02:00
Iam Naughton Phier
466b416506 Merge branch 'fixdamage' into overhaul 2015-03-27 14:41:06 +02:00
Iam Naughton Phier
aeb76cefc0 change from literate to straight coffeescript
- was getting lots of linting errors due to tabulation
 - probably better this way, though I still like literate's concept
 - all seems to work but need to triple check everything
2015-03-27 14:40:28 +02:00
Iam Naughton Phier
6d5c51d0e5 indentation fix, start of changing font in editor 2015-03-27 11:49:00 +02:00
26 changed files with 1335 additions and 1936 deletions

179
.gitignore vendored
View file

@ -1,179 +0,0 @@
# Created by https://www.gitignore.io/api/git,emacs,python,coffeescript
# Edit at https://www.gitignore.io/?templates=git,emacs,python,coffeescript
### CoffeeScript ###
*.js
### Emacs ###
# -*- mode: gitignore; -*-
*~
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
*.elc
auto-save-list
tramp
.\#*
# Org-mode
.org-id-locations
*_archive
# flymake-mode
*_flymake.*
# eshell files
/eshell/history
/eshell/lastdir
# elpa packages
/elpa/
# reftex files
*.rel
# AUCTeX auto folder
/auto/
# cask packages
.cask/
dist/
# Flycheck
flycheck_*.el
# server auth directory
/server/
# projectiles files
.projectile
# directory configuration
.dir-locals.el
# network security
/network-security.data
### Git ###
# Created by git for backups. To disable backups in Git:
# $ git config --global mergetool.keepBackup false
*.orig
# Created by git when using merge tools for conflicts
*.BACKUP.*
*.BASE.*
*.LOCAL.*
*.REMOTE.*
*_BACKUP_*.txt
*_BASE_*.txt
*_LOCAL_*.txt
*_REMOTE_*.txt
### Python ###
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
pip-wheel-metadata/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.hypothesis/
.pytest_cache/
# Translations
*.mo
*.pot
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
target/
# pyenv
.python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# celery beat schedule file
celerybeat-schedule
# SageMath parsed files
*.sage.py
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
#project settings
sahli-venv/*
*.wp*

View file

@ -1,47 +0,0 @@
fs = require 'fs'
option '-o', '--output [dir]', 'dir for compiled code'
task 'watch', 'watch current dir', (options) ->
{spawn} = require 'child_process'
args = ['-w','-c']
if options.output
args = args.concat ['./']
process.chdir __originalDirname
coffee = spawn 'coffee', args
coffee.stderr.on 'data', (data) ->
process.stderr.write data.toString()
coffee.stdout.on 'data', (data) ->
console.log data.toString()
source = [
'16col/imgtxtmode.coffee',
'16col/ansi.coffee',
'16col/bin.coffee',
'16col/idf.coffee',
'16col/adf.coffee',
'16col/sauce.coffee',
'16col/tundra.coffee',
'16col/pcboard.coffee',
'16col/avatar.coffee',
'16col/xbin.coffee',
'16col/pallette.coffee',
'16col/fonts.coffee',
]
task 'build', 'Build merged file for production', (options) ->
{exec} = require 'child_process'
content = []
for file, index in source then do (file, index) ->
fs.readFile file, 'utf8', (err, fileContents) ->
throw err if err
content[index] = fileContents
if index == source.length - 1
coffee = content.join('\n')
fs.writeFile 'textmode.coffee', coffee, 'utf8', (err) ->
throw err if err
command = 'coffee --compile textmode.coffee'
exec command, (err, stdout, stderr) ->
throw err if err
console.log stdout + stderr

View file

@ -6,7 +6,7 @@
l__________/__________|___|______l__________j_____j
Ansi/Ansi Viewer in Ecmascript
Coded by Sir Garbagetruck / Accession 2013+
Coded by Sir Garbagetruck / Accession 2013
Uses fonts by DMG, http:trueschool.se
Uses SixteenColors textmode js library for rendering
(well, my optimized version...)
@ -48,8 +48,6 @@ idea...
* Android tablets ARE considered modern; Ipad/Iphone too.
* I haven't tested on MorphOS yet.
For Chrome/Chromium:
To use with _local_ files you need to run your browser
in "developer" mode, that means:
@ -65,30 +63,6 @@ This works just fine if you use remote urls for the files.
EVERY PERSON ALIVE, DEAD, OR IMAGINARY,
INCLUDING THE NONEXISTENT. ***
Mr Doob (who I want to call Trace (: ) has a good page
explaining how to do this and alternatives here:
https://github.com/mrdoob/three.js/wiki/How-to-run-things-locally
Firefox (and Icecat, possibly other Firefox codebased browsers)
seem to work today (Sept 2016) _without_ making any changes, so
you can use it 'out of the box.' M0qui was able to, and I tested,
and so the big 72-point "not supported on Windows+Firefox" warning
goes away.
Again, DON'T FREAKING TURN OFF JAVASCRIPT SECURITY AND FORGET TO
TURN IT BACK ON. DON'T. NOT JUST ON WINDOWS. BUT ESPECIALLY ON
WINDOWS.
I may be able to turn this into a chrome app at some point, which
will eliminate that issue. Possibly also a Firefox solution, but:
As of today: I do not support this on Windows + Firefox. That is
100% at your own risk, because of the possibility you will forget
to set the setting back.
It's just plain safer to install this on your webserver at the
party place in a directory and use _that._
This product is licensed under the WTFPL. See:
http://www.wtfpl.net
for details, or just get the license:
@ -124,8 +98,3 @@ Azzarro/Madwizards.
- More examples came from 16colors archive. When the final
Sahli ver. B comes out, I'll select some test files and
put those credits in here too.
- Thanks to M0qui for submitting a patch for 'backwards'
(in case you accidentally hit space. Which we c64 sceners
never do, because that would be the NEXT demopart, and
you'd have to reload (: )

View file

@ -1,98 +0,0 @@
label, input, textarea{
display: block;
}
input, textarea {
width: 90%;
}
textarea {
height: 8em;
}
.colorbox {
border: 0;
padding: 0;
height: 2em;
}
pre {
font-family: 'TopazPlus a500a1000a2000','mOsOul',Monaco,monospace;
font-size: 16;
border: 2px inset #808080;
}
label {
font-size: 10;
}
div ul li{
list-style-type: none
}
#list {
border: 1px solid black;
}
#formica {
font-size: medium;
}
.entry {
margin: 0 3px 3px 3px;
padding: 0.4em;
padding-left: 1.5em;
width: 95%;
height: 18px;
border: 2px outset rgb(255,228,196);
background-color: rgb(245,245,220);
}
.righty {
float: right;
}
.entry span {
display: inline-block;
}
# this does not actually change anything because the design is set by
# the jquery-ui bit. Altering in the script does work. Here for reference.
.45box {
width: 45%;
display: inline-block;
}
div.groupbox p {
margin: .25 ex;
}
#dumparea {
position: absolute;
top: 1em;
padding: 1em;
margin: 1em;
width: 90%;
height: 90%;
background: floralwhite;
border: inset 2px royalblue;
}
#sahlioutput {
font-family: 'TopazPlus a500a1000a2000','mOsOul',Monaco,monospace;
font-size: 16;
background: aliceblue;
overflow-wrap: break-word;
word-wrap: break-word;
width: 100%;
height: 95%;
padding: 1ex;
border: inset 2px royalblue;
}
#closespan {
float: right;
margin: 0px 0px 1ex;
padding: 0px;
}

View file

@ -1,415 +0,0 @@
Sahli Editor
============
Editor for Sahli files.
- open existing file
- create new item
* get filename from dir
* insert SAUCE data if available
* use SAUCE data to find font
* allow Amiga choices
* colorpicker
- edit existing item
- remove item
- clear whole file
- copy/clone
- move items around
- sort items
- output to screen (copy into file)
* run from node - save filename dialog
***
It should be noted that this does not do bounds checking, and it would be very
possible to overflow this by using a debugger and such. As the purpose of this
is limited, and this should NOT be put on a live website, I feel that is ok for
now. Perhaps I will fix it after Revision.
***
== Create Initial crappage
We need to make a screen that has a few things in it for starters
Title, load existing, and new file options.
Silliness for checking that this works.
$(-> $("h1").hide().slideDown(500))
Create buttos to choose between the New and Load functionalities
(As we aren't going to ever load a file _and_ do a new file.)
(If someone wants to do that, they can restart with F5 or something.)
Also hide the editor until needed, and initialize some elements.
$(->
$("#newsahli")
.button { disabled: false}
.click -> newsahli()
)
$(->
$("#loadsahli")
.button { disabled: false}
.click -> loadsahli()
)
$(->
$(".hidden").hide()
$("#entryamiga").button {icons: {primary:"ui-icon-gear"}}
.click ->
stuff = $(@).children()
if @.value == "1"
stuff[1].textContent = 'Ansi'
@.value = "0"
else
stuff[1].textContent = 'Ascii'
@.value = "1"
$(".45box").css {width:'45%',display:'inline-block'}
$(".groupbox p").css {margin:"0 0 .25em 0"}
$(".colorbox").change =>
sahlicolor()
$("#entryfilepick").change ->
if @.files[0]? then $("#entryfile").val @.files[0].name
$("#entryfile").click ->
$("#entryfilepick").click()
)
The sahli file definition format is as follows:
"file" - the actual filename on disk, "name" - the title of the piece,
the boolean 'amiga' indicates if it is ansi or ascii (True = ascii),
width is the width (widest point of the file), author the author of the piece,
the color and bg items define the color for amiga ascii, and the font
defines the font similarly. For PC ansi, this should be 'ansifont.'
The three remaining lines are informational and optional.
The slide format is currently unused, but consists of a background picture,
a html template, and a css file.
class emptyfiledef
constructor: ->
@file = ""
@name = ""
@amiga = true
@filetype = 'plain'
@width = ""
@author = ""
@font = "Propaz"
@color = [ 255,255,255,255 ]
@bg = [ 0,0,0,0 ]
@line1 = ""
@line2 = ""
@text = ""
class Sahli
constructor: ->
@emptyfiledef = new emptyfiledef
@emptyslidesdef = {
"background": "",
"template": "",
"css": ""
}
@empty = {
"location": "",
"slides": @emptyslidesdef,
"filedata": [ ]
}
loader: ->
$.ajax {
url: '../list.sahli',
dataType: "json",
success: (result) =>
@data = result
@.edit()
}
Editor functionality:
Close the new/load buttons - unneeded now.
list, and allow dragon-droppings for sorting. Doubleclick to edit, or use
edit button.
edit: ->
$('#buttonbox').hide()
$('#dirlocation').change (event) =>
@data.location = event.target.value
$('#listsave').button {icons: {primary:"ui-icon-disk"}}
.click =>
$('#sahlioutput').text dumpjson @data
$('#dumparea').show 100
console.log dumpjson @data
$('#listlist').button {icons: {primary:"ui-icon-folder-open"}}
.click ->
getfilelist()
$('#listappend').button {icons: {primary:"ui-icon-1-n"}}
.click (event) =>
newentry = new emptyfiledef
@data.filedata.push newentry
@buildlist @data
$('#listdisplay').button {icons: {primary:"ui-icon-refresh"}}
.click =>
@buildlist @data
$('#closespan').click ->
$(@parentElement.parentElement).hide()
$('#sahlioutput').text ''
You need to save the order, and extract these in that order; moving around
does not alter the array. Alternately, _have_ it alter the array.
@buildlist @data
buildlist: (data) ->
$('#list').show 100
$('#list ol li').remove()
console.log i.author for i in @data.filedata
x = 0
$('#dirlocation').val @data.location
$('#sortlist').append @.additem item,x++ for item in @data.filedata
$('#sortlist').sortable
start: (event,ui) ->
ui.item.data {startpos:ui.item.index()}
stop: (event,ui) =>
s = ui.item.data().startpos
e = ui.item.index()
@data.filedata = @.rearrangearray s,e,@data.filedata
console.log name.author,name.name,name.file for name in @data.filedata
console.log '---'
@buildlist @data
Given a start and and end position, pop the array element at start off and
insert it into the array at end position. A la the draggon-dropping.
rearrangearray: (startpos,endpos,a) ->
moving = a[startpos]
alen = a.length
tarr = a[0...startpos].concat a[startpos+1..-1]
tarr[0...endpos].concat [moving].concat tarr[endpos..-1]
additem: (item,pos) ->
entry = @genentryline item,pos
entry.dblclick =>
@editline item,pos
genentryline: (item,pos) ->
arrows = "<span class='ui-icon ui-icon-arrowthick-2-n-s'></span>"
amigastatus = ansiorascii booltoint item.amiga
delbutton = $("<span class='righty' id=del-#{pos}>delete</span>")
.click (event) =>
pos = event.currentTarget.id.replace "del-",""
@data.filedata.splice pos,1
@buildlist @data
entry = $("<li class='entry' id='#{item.file}'>#{arrows}#{amigastatus} | #{item.author} : #{item.name} : #{item.file}</li>")
entry.append delbutton
save: ->
pos = $("#entryindex").val()
entry = @data.filedata[pos]
entry.name = $("#entryname").val()
entry.author = $("#entryauthor").val()
entry.amiga = statustobool $("#entryamiga").children()[1].textContent
console.log $("#entryamiga").children()[1].textContent,entry.amiga,entry.author
entry.color = colortoarray $("#entrycolor").val()
entry.bg = colortoarray $("#entrybg").val()
entry.width = $("#entrywidth").val()
entry.line1 = $("#entryline1").val()
entry.line2 = $("#entryline2").val()
entry.text = $("#entrytext").val()
entry.file = $("#entryfile").val()
entry.filetype = $("#entryfiletype").val()
@buildlist @data
editline: (data,pos) ->
$("#formica").dialog {
width:'800',
modal: false,
title:"Entry #{data.file} ",
buttons: [{
text: "Cancel",
icons: {primary: 'ui-icon-trash'},
click: ->
$(@).dialog "close"
},{
text: "Save",
icons: {primary: 'ui-icon-disk'},
click: (event) =>
event.preventDefault()
@save()
event.currentTarget.previousElementSibling.click()
}]
}
data.amiga = booltoint data.amiga
fcol = colortoname arraytocolor data.color
bcol = colortoname arraytocolor data.bg
$("#entryindex").val pos
$("#entryname").val data.name
$("#entryauthor").val data.author
$("#entryfiletpye").val data.filetype
$("#entryfiletype").children()[resolvefiletype data.filetype].selected = true
$("#entryamiga").val data.amiga
$("#entryamiga").children()[1].textContent = ansiorascii data.amiga
$("#entryfont").val data.font
$("#entrycolor").val fcol
$("#entrycolor").children()[colorindex fcol ].selected = true
$("#entrybg").val bcol
$("#entrybg").children()[colorindex bcol ].selected = true
$("#entrywidth").val data.width
$("#entryline1").val data.line1
$("#entryline2").val data.line2
$("#entrytext").val data.text
$("#entryfile").val data.file
sahlicolor()
A Helper function to dump json out of an object as text:
dumpjson = (obj) ->
JSON.stringify obj,null,"\t"
Boolean / integer Helpers
booltoint = (bool) ->
bool + 1 - 1
inttobool = (intstr) ->
(intstr == 1).toString()
statustobool = (status) ->
if status is 'Ascii' then true else false
Resolve filetype offset in array:
resolvefiletype = (filetype) ->
options = {
"plain":0
"ansi":1
"xbin":2
"ice":3
"adf":4
"avatar":5
"bin":6
"idf":7
"pcboard":8
"tundra":9
}
options[filetype]
Resolve ansi or ascii status
ansiorascii = (status) ->
if status is 0 then "Ansi" else "Ascii"
Color conversion from array to color item:
This decimal to hex conversion only handles 00-FF but it's fine for this purpose;
we actually _want_ that limitation in the output.
dec2hex = (num) ->
"#{('000'+num.toString 16).slice -2}"
hex2dec = (num) ->
parseInt num,16
arraytocolor = (array) ->
c = (dec2hex x for x in array)[0..2].join ''
"##{c}"
colortoarray = (color) ->
color = color.slice(1)
c1 = [ color[0..1], color[2..3], color[4..5] ]
x = (hex2dec i for i in c1)
x.push 255
x
Need a way to convert the array back to the color name.
colortoname = (color) ->
names = {
"#E0E0E0":"Light Grey"
"#A0A0E0":"Light Blue"
"#9AFE2E":"Light Green"
"#FF0000":"Red"
"#FF8000":"Orange"
"#FFFF00":"Yellow"
"#00F000":"Green"
"#2EFEF7":"Cyan"
"#002EF7":"Blue"
"#0B0B3B":"Navy"
"#FF00FF":"Magenta"
"#8000FF":"Purple"
"#0A2A0A":"Dark Green"
"#3B3B3B":"Dark Grey"
"#FFFFFF":"White"
"#000000":"Black"
}
color = color.toUpperCase()
colorname = names[color]
bw = if hex2dec(color.slice(1)) > 8421504 then 'White' else "Black"
ret = if colorname? then colorname else bw
Similarly, need to be able to get the color index.
colorindex = (colorname) ->
names = {
"Light Grey":0
"Light Blue":1
"Light Green":2
"Red":3
"Orange":4
"Yellow":5
"Green":6
"Cyan":7
"Blue":8
"Navy":9
"Magenta":10
"Purple":11
"Dark Green":12
"Dark Grey":13
"White":14
"Black":15
}
names[colorname]
A function for changing the fore and background colors of the sahli ascii example
sahlicolor = ->
fg = $('#entrycolor').val()
bg = $('#entrybg').val()
console.log 'sahlicolor',fg,bg
$('#sahliascii').css {'color':fg,'background':bg}
Function for loading the filelist from the specified directory on the server/filesystem.
getfilelist = ->
location = $("#dirlocation").val()
$.get("../#{location}", (listing) ->
console.log listing
)
When clicking 'New' we want to make a brand new Sahli, and then clear out
the buttons and create the editor bit as blank.
newsahli = ->
sahli = new Sahli
sahli.data = sahli.empty
newentry = new emptyfiledef
sahli.data.filedata.push newentry
sahli.edit()
And when clicking 'load' we want to load the existing sahli file.
loadsahli = ->
sahli = new Sahli
sahli.loader 'list.sahli'

160
editor.py
View file

@ -1,160 +0,0 @@
#!/usr/bin/env python
# coding:utf-8
"""
Author: Sir Garbagetruck --<truck@notonfire.somewhere>
Purpose: make editing the list.sahli file easier
Created: 2020/04/09
"""
import json
import argparse
import os
from sauce import SAUCE
from PIL import Image
from sahliEditorPython import sahlifile as SF
def getfilesindir(directory):
"""return the files in a directory as an array"""
for root, dirs, files, rootfd in os.fwalk(directory):
return files
def getfilenames(filedata):
"""return the file names from a sahli filedata array"""
f = []
for i in filedata:
f.append(i['file'])
return f
def getdata(filedata, name):
"""get the filedata entry where file = name"""
for i in filedata:
if i['file'] == name:
return i
return []
def getpicdata(filename):
"""extract picture data from filename"""
imagedata = Image.open(filename)
picdata = {
'width': imagedata.width,
'height': imagedata.height
}
return picdata
def getansidata(filename):
"""extract SAUCE data from filename"""
saucedata = SAUCE(filename)
ansidata = {
'author': saucedata.author,
'group': saucedata.group,
'title': saucedata.title,
'filesize': saucedata.filesize,
'comments': saucedata.comments,
'width': None,
'height': None
}
tinfonames = [saucedata.tinfo1_name,
saucedata.tinfo2_name,
saucedata.tinfo3_name,
saucedata.tinfo4_name]
tinfo = [saucedata.tinfo1,
saucedata.tinfo2,
saucedata.tinfo3,
saucedata.tinfo4]
for i in range(0, 3):
if tinfonames[i] == 'width':
ansidata['width'] = tinfo[i]
if tinfonames[i] == 'height':
ansidata['height'] = tinfo[i]
# print(tinfonames[i])
return ansidata
def getamigadata(filename):
"""try to get some form of info from file (:"""
with open(filename, encoding='latin1') as f:
ascii = f.readlines()
width = 0
for i in ascii:
if len(i) > width:
width = len(i)
return {'height': len(ascii),
'width': width}
def main(args):
"""maintain a list.sahli file"""
if args.new:
mysahli = SF.sahlifile(None)
else:
mysahli = SF.sahlifile(args.filename)
mysahli.sahli['location'] = args.directory
files = getfilesindir(args.directory)
filedata = mysahli.sahli['filedata']
filedatanames = getfilenames(filedata)
newdata = []
for i in files:
dirfile = '{}/{}'.format(args.directory, i)
if i in filedatanames:
print('found! {}'.format(i))
# todo: _if_ I ever make this a non-preparser, then... futz with
a = getansidata(dirfile)
newdata.append(getdata(filedata, i))
else:
print('not found! {}'.format(i))
suf = i.split('.')[-1]
if suf in ['png', 'jpg', 'jpeg', 'gif',
'PNG', 'JPG', 'JPEG', 'GIF']:
stuff = getpicdata(dirfile)
entry = mysahli.blank_picture()
entry['width'] = stuff['width']
entry['height'] = stuff['height']
entry['file'] = i
entry['name'] = i
newdata.append(entry)
elif suf in ['ans', 'ANS', 'BIN', 'bin', 'XB', 'xb']:
stuff = getansidata(dirfile)
entry = mysahli.blank_ansi()
entry['file'] = i
entry['name'] = stuff['title']
entry['author'] = '{}/{}'.format(
stuff['author'], stuff['group'])
entry['text'] = stuff['comments']
if stuff['height'] is not None:
entry['height'] = stuff['height']
if stuff['width'] is not None:
entry['width'] = stuff['width']
newdata.append(entry)
elif suf in ['TXT', 'ASC', 'txt', 'asc',
'NFO', 'nfo', 'diz', 'DIZ']:
stuff = getamigadata(dirfile)
entry = mysahli.blank_amiga_ascii()
entry['name'] = i
# entry['title'] = i
# entry['height'] = stuff['height']
entry['file'] = i
newdata.append(entry)
else:
print("dunno what type of file this is... {}".format(dirfile))
mysahli.sahli['filedata'] = newdata
out = json.dumps(mysahli.sahli, sort_keys=False, indent=4)
if args.outfile == '>stdout':
print(out)
else:
with open(args.outfile, 'w') as f:
json.dump(mysahli.sahli, f, sort_keys=False, indent=4)
if __name__ == '__main__':
ap = argparse.ArgumentParser()
ap.add_argument('-f', '--filename', default='list.sahli')
ap.add_argument('-n', '--new', action='store_true')
ap.add_argument('-o', '--outfile', type=str, default='>stdout')
ap.add_argument('-d', '--directory', type=str, required=True,
help='directory where compo files are')
main(ap.parse_args())

427
editor/sahliedit.coffee Normal file
View file

@ -0,0 +1,427 @@
###
Sahli Editor
============
Editor for Sahli files.
- open existing file
- create new item
* get filename from dir
* insert SAUCE data if available
* use SAUCE data to find font
* allow Amiga choices
* colorpicker
- edit existing item
- remove item
- clear whole file
- copy/clone
- move items around
- sort items
- output to screen (copy into file)
* run from node - save filename dialog
***
It should be noted that this does not do bounds checking, and it would be very
possible to overflow this by using a debugger and such. As the purpose of this
is limited, and this should NOT be put on a live website, I feel that is ok for
now. Perhaps I will fix it after Revision.
***
== Create Initial crappage
We need to make a screen that has a few things in it for starters
Title, load existing, and new file options.
Silliness for checking that this works.
###
$(-> $("h1").hide().slideDown(500))
###
Create buttons to choose between the New and Load functionalities
(As we aren't going to ever load a file _and_ do a new file.)
(If someone wants to do that, they can restart with F5 or something.)
Also hide the editor until needed, and initialize some elements.
###
$(->
$("#newsahli")
.button { disabled: false}
.click -> newsahli()
)
$(->
$("#loadsahli")
.button { disabled: false}
.click -> loadsahli()
)
$(->
$(".hidden").hide()
$("#entryamiga").button {icons: {primary:"ui-icon-gear"}}
.click ->
stuff = $(@).children()
if @.value == "1"
stuff[1].textContent = 'Ansi'
@.value = "0"
else
stuff[1].textContent = 'Ascii'
@.value = "1"
$(".45box").css {width:'40ex',display:'inline-block'}
$(".groupbox p").css {margin:"0 0 .25em 0"}
$(".colorbox").change ->
sahlicolor()
$("#entryfont").change ->
font = $("#entryfont").val()
if font == "ansifont"
font = "BlockZone"
$('pre').css 'font-family',font
$("#entryfilepick").change ->
if @.files[0]? then $("#entryfile").val @.files[0].name
$("#entryfile").click ->
$("#entryfilepick").click()
)
###
The sahli file definition format is as follows:
"file" - the actual filename on disk, "name" - the title of the piece,
the boolean 'amiga' indicates if it is ansi or ascii (True = ascii),
width is the width (widest point of the file), author the author of the piece,
the color and bg items define the color for amiga ascii, and the font
defines the font similarly. For PC ansi, this should be 'ansifont.'
The three remaining lines are informational and optional.
The slide format is currently unused, but consists of a background picture,
a html template, and a css file.
###
class Emptyfiledef
constructor: ->
@file = ""
@name = ""
@amiga = true
@filetype = 'plain'
@width = ""
@author = ""
@font = "Propaz"
@color = [ 255,255,255,255 ]
@bg = [ 0,0,0,0 ]
@line1 = ""
@line2 = ""
@text = ""
class Sahli
constructor: ->
@emptyfiledef = new Emptyfiledef
@emptyslidesdef = {
"background": "",
"template": "",
"css": ""
}
@empty = {
"location": "",
"slides": @emptyslidesdef,
"filedata": [ ]
}
loader: ->
$.ajax {
url: '../list.sahli',
dataType: "json",
success: (result) =>
@data = result
@.edit()
}
edit: ->
$('#buttonbox').hide()
$('#dirlocation').change (event) =>
@data.location = event.target.value
$('#listsave').button {icons: {primary:"ui-icon-disk"}}
.click =>
$('#sahlioutput').text dumpjson @data
$('#dumparea').show 100
$('#listlist').button {icons: {primary:"ui-icon-folder-open"}}
.click ->
getfilelist()
$('#listappend').button {icons: {primary:"ui-icon-1-n"}}
.click (event) =>
newentry = new Emptyfiledef
@data.filedata.push newentry
@buildlist @data
$('#listdisplay').button {icons: {primary:"ui-icon-refresh"}}
.click =>
@buildlist @data
$('#closespan').click ->
$(@parentElement.parentElement).hide()
$('#sahlioutput').text ''
@buildlist @data
buildlist: (data) ->
$('#list').show 100
$('#list ol li').remove()
console.log i.author for i in @data.filedata
x = 0
$('#dirlocation').val @data.location
$('#sortlist').append @.additem item,x++ for item in @data.filedata
$('#sortlist').sortable
start: (event,ui) ->
ui.item.data
startpos:ui.item.index()
stop: (event,ui) =>
a = 2
s = ui.item.data().startpos
e = ui.item.index()
@data.filedata = @.rearrangearray s,e,@data.filedata
@buildlist @data
rearrangearray: (startpos,endpos,a) ->
moving = a[startpos]
alen = a.length
tarr = a[0...startpos].concat a[startpos+1..-1]
tarr[0...endpos].concat [moving].concat tarr[endpos..-1]
additem: (item,pos) ->
entry = @genentryline item,pos
entry.dblclick =>
@editline item,pos
genentryline: (item,pos) ->
arrows = "<span class='ui-icon ui-icon-arrowthick-2-n-s'></span>"
amigastatus = ansiorascii booltoint item.amiga
delbutton = $("<span class='righty' id=del-#{pos}>delete</span>")
.click (event) =>
pos = event.currentTarget.id.replace "del-",""
@data.filedata.splice pos,1
@buildlist @data
whichone = "<li class='entry' id='#{item.file}'>#{arrows}#{amigastatus} |"
whichone += " #{item.author} : #{item.name} : #{item.file}</li>"
entry = $(whichone)
entry.append delbutton
save: ->
pos = $("#entryindex").val()
entry = @data.filedata[pos]
entry.name = $("#entryname").val()
entry.author = $("#entryauthor").val()
entry.amiga = statustobool $("#entryamiga").children()[1].textContent
console.log $("#entryamiga").children()[1].textContent
console.log entry.amiga,entry.author
entry.color = colortoarray $("#entrycolor").val()
entry.bg = colortoarray $("#entrybg").val()
entry.width = $("#entrywidth").val()
entry.line1 = $("#entryline1").val()
entry.line2 = $("#entryline2").val()
entry.text = $("#entrytext").val()
entry.font = $("#entryfont").val()
entry.file = $("#entryfile").val()
entry.filetype = $("#entryfiletype").val()
@buildlist @data
editline: (data,pos) ->
$("#formica").dialog {
width:'800',
modal: false,
title:"Entry #{data.file} ",
buttons: [{
text: "Cancel",
icons: {primary: 'ui-icon-trash'},
click: ->
$(@).dialog "close"
},{
text: "Save",
icons: {primary: 'ui-icon-disk'},
click: (event) =>
event.preventDefault()
@save()
event.currentTarget.previousElementSibling.click()
}]
}
data.amiga = booltoint data.amiga
fcol = colortoname arraytocolor data.color
bcol = colortoname arraytocolor data.bg
$("#entryindex").val pos
$("#entryname").val data.name
$("#entryauthor").val data.author
$("#entryfiletpye").val data.filetype
$("#entryfiletype").children()[resolvefiletype data.filetype].selected =true
$("#entryamiga").val data.amiga
$("#entryamiga").children()[1].textContent = ansiorascii data.amiga
$("#entryfont").val data.font
$("#entrycolor").val fcol
$("#entrycolor").children()[colorindex fcol ].selected = true
$("#entrybg").val bcol
$("#entrybg").children()[colorindex bcol ].selected = true
$("#entrywidth").val data.width
$("#entryline1").val data.line1
$("#entryline2").val data.line2
$("#entrytext").val data.text
$("#entryfile").val data.file
sahlicolor()
###
A Helper function to dump json out of an object as text:
###
dumpjson = (obj) ->
JSON.stringify obj,null,"\t"
###
Boolean / integer Helpers
###
booltoint = (bool) ->
bool + 1 - 1
inttobool = (intstr) ->
(intstr == 1).toString()
statustobool = (status) ->
if status is 'Ascii' then true else false
###
Resolve filetype offset in array:
###
resolvefiletype = (filetype) ->
options = {
"plain":0
"ansi":1
"xbin":2
"ice":3
"adf":4
"avatar":5
"bin":6
"idf":7
"pcboard":8
"tundra":9
}
options[filetype]
###
Resolve ansi or ascii status
###
ansiorascii = (status) ->
if status is 0 then "Ansi" else "Ascii"
###
Color conversion from array to color item:
This decimal to hex conversion only handles 00-FF but it's fine for this
purpose; we actually _want_ that limitation in the output.
###
dec2hex = (num) ->
"#{('000'+num.toString 16).slice -2}"
hex2dec = (num) ->
parseInt num,16
arraytocolor = (array) ->
c = (dec2hex x for x in array)[0..2].join ''
"##{c}"
colortoarray = (color) ->
color = color.slice(1)
c1 = [ color[0..1], color[2..3], color[4..5] ]
x = (hex2dec i for i in c1)
x.push 255
x
###
Need a way to convert the array back to the color name.
###
colortoname = (color) ->
names = {
"#E0E0E0":"Light Grey"
"#A0A0E0":"Light Blue"
"#9AFE2E":"Light Green"
"#FF0000":"Red"
"#FF8000":"Orange"
"#FFFF00":"Yellow"
"#00F000":"Green"
"#2EFEF7":"Cyan"
"#002EF7":"Blue"
"#0B0B3B":"Navy"
"#FF00FF":"Magenta"
"#8000FF":"Purple"
"#0A2A0A":"Dark Green"
"#3B3B3B":"Dark Grey"
"#FFFFFF":"White"
"#000000":"Black"
}
color = color.toUpperCase()
colorname = names[color]
bw = if hex2dec(color.slice(1)) > 8421504 then 'White' else "Black"
ret = if colorname? then colorname else bw
###
Similarly, need to be able to get the color index.
###
colorindex = (colorname) ->
names = {
"Light Grey":0
"Light Blue":1
"Light Green":2
"Red":3
"Orange":4
"Yellow":5
"Green":6
"Cyan":7
"Blue":8
"Navy":9
"Magenta":10
"Purple":11
"Dark Green":12
"Dark Grey":13
"White":14
"Black":15
}
names[colorname]
###
A function for changing the fore and background colors of the sahli ascii
example
###
sahlicolor = ->
fg = $('#entrycolor').val()
bg = $('#entrybg').val()
console.log 'sahlicolor',fg,bg
$('#sahliascii').css {'color':fg,'background':bg}
###
Function for loading the filelist from the specified directory on the
server/filesystem.
Needs to be made into an actual real thing.
###
getfilelist = ->
location = $("#dirlocation").val()
$.get("../#{location}", (listing) ->
console.log listing
)
###
When clicking 'New' we want to make a brand new Sahli, and then clear out
the buttons and create the editor bit as blank.
###
newsahli = ->
sahli = new Sahli
sahli.data = sahli.empty
newentry = new Emptyfiledef
sahli.data.filedata.push newentry
sahli.edit()
###
And when clicking 'load' we want to load the existing sahli file.
###
loadsahli = ->
sahli = new Sahli
sahli.loader 'list.sahli'

231
editor/sahliedit.css Normal file
View file

@ -0,0 +1,231 @@
label, input, textarea{
display: block;
}
input, textarea {
width: 90%;
}
textarea {
height: 8em;
}
.colorbox {
border: 0;
padding: 0;
height: 2em;
}
pre {
font-family: 'topaz500','mosoul',Monaco,monospace;
font-size: 16;
border: 2px inset #808080;
}
label {
font-size: 10;
}
div ul li{
list-style-type: none
}
#list {
border: 1px solid black;
}
#formica {
font-size: medium;
}
.entry {
margin: 0 3px 3px 3px;
padding: 0.4em;
padding-left: 1.5em;
width: 95%;
height: 18px;
border: 2px outset rgb(255,228,196);
background-color: rgb(245,245,220);
}
.righty {
float: right;
}
.entry span {
display: inline-block;
}
/* this does not actually change anything because the design is set by */
/* the jquery-ui bit. Altering in the script does work. Here for reference. */
/*
.45box {
width: 45%;
display: inline-block;
}
*/
div.groupbox p {
margin: .25 ex;
}
#dumparea {
position: absolute;
top: 1em;
padding: 1em;
margin: 1em;
width: 90%;
height: 90%;
background: floralwhite;
border: inset 2px royalblue;
}
#sahlioutput {
font-family: 'microknight','P0t-nOodle',Monaco,monospace;
font-size: 16;
background: aliceblue;
overflow-wrap: break-word;
word-wrap: break-word;
width: 100%;
height: 95%;
padding: 1ex;
border: inset 2px royalblue;
}
#closespan {
float: right;
margin: 0px 0px 1ex;
padding: 0px;
}
@font-face {
font-family: 'P0t-nOodle';
src: url('fonts/P0T-NOoDLE_v1.0.woff2') format('woff2');
src: url('fonts/P0T-NOoDLE_v1.0.woff') format('woff');
}
@font-face {
font-family: 'MicroKnight';
src: url('fonts/MicroKnightPlus_v1.0.woff2') format('woff2');
src: url('fonts/MicroKnightPlus_v1.0.woff') format('woff');
}
@font-face {
font-family: 'mOsOul';
src: url('fonts/mOsOul_v1.0.woff2') format('woff2');
src: url('fonts/mOsOul_v1.0.woff') format('woff');
}
@font-face {
font-family: 'Topaz1200';
src: url('fonts/TopazPlusA1200.woff') format('woff2');
src: url('fonts/TopazPlusA1200.woff') format('woff');
}
@font-face {
font-family: 'Topaz500';
src: url('fonts/TopazPlusA500.woff2') format('woff2');
src: url('fonts/TopazPlusA500.woff') format('woff');
}
@font-face {
font-family: 'blockzone';
src: url('fonts/BlockZone.woff2') format('woff2');
src: url('fonts/BlockZone.woff') format('woff');
}
.mosoul {
font-family: mOsOul;
text-align: left;
}
.pot-noodle , .p0t-noodle {
font-family: 'P0t-nOodle';
text-align: left;
}
.topaz, .topaz500 {
font-family: 'Topaz500';
text-align: left;
}
.propaz, .topaz1200 {
font-family: 'Topaz1200';
text-align: left;
}
.microknight, .microknightplus {
font-family: 'MicroKnight';
text-align: left;
}
.blockzone, .pcansifont {
font-family: 'blockzone';
text-align: left;
}
@font-face {
font-family: 'P0t-nOodle';
src: url('../fonts/P0T-NOoDLE_v1.0.woff2') format('woff2');
src: url('../fonts/P0T-NOoDLE_v1.0.woff') format('woff');
}
@font-face {
font-family: 'MicroKnight';
src: url('../fonts/MicroKnightPlus_v1.0.woff2') format('woff2');
src: url('../fonts/MicroKnightPlus_v1.0.woff') format('woff');
}
@font-face {
font-family: 'mOsOul';
src: url('../fonts/mOsOul_v1.0.woff2') format('woff2');
src: url('../fonts/mOsOul_v1.0.woff') format('woff');
}
@font-face {
font-family: 'Topaz1200';
src: url('../fonts/TopazPlusA1200.woff') format('woff2');
src: url('../fonts/TopazPlusA1200.woff') format('woff');
}
@font-face {
font-family: 'Topaz500';
src: url('../fonts/TopazPlusA500.woff2') format('woff2');
src: url('../fonts/TopazPlusA500.woff') format('woff');
}
@font-face {
font-family: 'blockzone';
src: url('../fonts/BlockZone.woff2') format('woff2');
src: url('../fonts/BlockZone.woff') format('woff');
}
.mosoul {
font-family: mOsOul;
text-align: left;
}
.pot-noodle , .p0t-noodle {
font-family: 'P0t-nOodle';
text-align: left;
}
.topaz, .topaz500 {
font-family: 'Topaz500';
text-align: left;
}
.propaz, .topaz1200 {
font-family: 'Topaz1200';
text-align: left;
}
.microknight, .microknightplus {
font-family: 'MicroKnight';
text-align: left;
}
.blockzone, .pcansifont {
font-family: 'blockzone';
text-align: left;
}

413
editor/sahliedit.doclit Normal file
View file

@ -0,0 +1,413 @@
Sahli Editor
============
Editor for Sahli files.
- open existing file
- create new item
* get filename from dir
* insert SAUCE data if available
* use SAUCE data to find font
* allow Amiga choices
* colorpicker
- edit existing item
- remove item
- clear whole file
- copy/clone
- move items around
- sort items
- output to screen (copy into file)
* run from node - save filename dialog
***
It should be noted that this does not do bounds checking, and it would be very
possible to overflow this by using a debugger and such. As the purpose of this
is limited, and this should NOT be put on a live website, I feel that is ok for
now. Perhaps I will fix it after Revision.
***
== Create Initial crappage
We need to make a screen that has a few things in it for starters
Title, load existing, and new file options.
Silliness for checking that this works.
$(-> $("h1").hide().slideDown(500))
Create buttos to choose between the New and Load functionalities
(As we aren't going to ever load a file _and_ do a new file.)
(If someone wants to do that, they can restart with F5 or something.)
Also hide the editor until needed, and initialize some elements.
$(->
$("#newsahli")
.button { disabled: false}
.click -> newsahli()
)
$(->
$("#loadsahli")
.button { disabled: false}
.click -> loadsahli()
)
$(->
$(".hidden").hide()
$("#entryamiga").button {icons: {primary:"ui-icon-gear"}}
.click ->
stuff = $(@).children()
if @.value == "1"
stuff[1].textContent = 'Ansi'
@.value = "0"
else
stuff[1].textContent = 'Ascii'
@.value = "1"
$(".45box").css {width:'45%',display:'inline-block'}
$(".groupbox p").css {margin:"0 0 .25em 0"}
$(".colorbox").change ->
sahlicolor()
$("#entryfont").change ->
$('pre').css 'font-family',$('#entryfont').val()
$("#entryfilepick").change ->
if @.files[0]? then $("#entryfile").val @.files[0].name
$("#entryfile").click ->
$("#entryfilepick").click()
)
The sahli file definition format is as follows:
"file" - the actual filename on disk, "name" - the title of the piece,
the boolean 'amiga' indicates if it is ansi or ascii (True = ascii),
width is the width (widest point of the file), author the author of the piece,
the color and bg items define the color for amiga ascii, and the font
defines the font similarly. For PC ansi, this should be 'ansifont.'
The three remaining lines are informational and optional.
The slide format is currently unused, but consists of a background picture,
a html template, and a css file.
class Emptyfiledef
constructor: ->
@file = ""
@name = ""
@amiga = true
@filetype = 'plain'
@width = ""
@author = ""
@font = "Propaz"
@color = [ 255,255,255,255 ]
@bg = [ 0,0,0,0 ]
@line1 = ""
@line2 = ""
@text = ""
class Sahli
constructor: ->
@emptyfiledef = new Emptyfiledef
@emptyslidesdef = {
"background": "",
"template": "",
"css": ""
}
@empty = {
"location": "",
"slides": @emptyslidesdef,
"filedata": [ ]
}
loader: ->
$.ajax {
url: '../list.sahli',
dataType: "json",
success: (result) =>
@data = result
@.edit()
}
Editor functionality:
Close the new/load buttons - unneeded now.
list, and allow dragon-droppings for sorting. Doubleclick to edit, or use
edit button.
edit: ->
$('#buttonbox').hide()
$('#dirlocation').change (event) =>
@data.location = event.target.value
$('#listsave').button {icons: {primary:"ui-icon-disk"}}
.click =>
$('#sahlioutput').text dumpjson @data
$('#dumparea').show 100
console.log dumpjson @data
$('#listlist').button {icons: {primary:"ui-icon-folder-open"}}
.click ->
getfilelist()
$('#listappend').button {icons: {primary:"ui-icon-1-n"}}
.click (event) =>
newentry = new Emptyfiledef
@data.filedata.push newentry
@buildlist @data
$('#listdisplay').button {icons: {primary:"ui-icon-refresh"}}
.click =>
@buildlist @data
$('#closespan').click ->
$(@parentElement.parentElement).hide()
$('#sahlioutput').text ''
You need to save the order, and extract these in that order; moving around
does not alter the array. Alternately, _have_ it alter the array.
@buildlist @data
buildlist: (data) ->
$('#list').show 100
$('#list ol li').remove()
console.log i.author for i in @data.filedata
x = 0
$('#dirlocation').val @data.location
$('#sortlist').append @.additem item,x++ for item in @data.filedata
$('#sortlist').sortable
start: (event,ui) ->
ui.item.data {startpos:ui.item.index()}
stop: (event,ui) =>
s = ui.item.data().startpos
e = ui.item.index()
@data.filedata = @.rearrangearray s,e,@data.filedata
console.log name.author,name.name,name.file for name in @data.filedata
console.log '---'
@buildlist @data
Given a start and and end position, pop the array element at start off and
insert it into the array at end position. A la the draggon-dropping.
rearrangearray: (startpos,endpos,a) ->
moving = a[startpos]
alen = a.length
tarr = a[0...startpos].concat a[startpos+1..-1]
tarr[0...endpos].concat [moving].concat tarr[endpos..-1]
additem: (item,pos) ->
entry = @genentryline item,pos
entry.dblclick =>
@editline item,pos
genentryline: (item,pos) ->
arrows = "<span class='ui-icon ui-icon-arrowthick-2-n-s'></span>"
amigastatus = ansiorascii booltoint item.amiga
delbutton = $("<span class='righty' id=del-#{pos}>delete</span>")
.click (event) =>
pos = event.currentTarget.id.replace "del-",""
@data.filedata.splice pos,1
@buildlist @data
entry = $("<li class='entry' id='#{item.file}'>#{arrows}#{amigastatus} | #{item.author} : #{item.name} : #{item.file}</li>")
entry.append delbutton
save: ->
pos = $("#entryindex").val()
entry = @data.filedata[pos]
entry.name = $("#entryname").val()
entry.author = $("#entryauthor").val()
entry.amiga = statustobool $("#entryamiga").children()[1].textContent
console.log $("#entryamiga").children()[1].textContent,entry.amiga,entry.author
entry.color = colortoarray $("#entrycolor").val()
entry.bg = colortoarray $("#entrybg").val()
entry.width = $("#entrywidth").val()
entry.line1 = $("#entryline1").val()
entry.line2 = $("#entryline2").val()
entry.text = $("#entrytext").val()
entry.file = $("#entryfile").val()
entry.filetype = $("#entryfiletype").val()
@buildlist @data
editline: (data,pos) ->
$("#formica").dialog {
width:'800',
modal: false,
title:"Entry #{data.file} ",
buttons: [{
text: "Cancel",
icons: {primary: 'ui-icon-trash'},
click: ->
$(@).dialog "close"
},{
text: "Save",
icons: {primary: 'ui-icon-disk'},
click: (event) =>
event.preventDefault()
@save()
event.currentTarget.previousElementSibling.click()
}]
}
data.amiga = booltoint data.amiga
fcol = colortoname arraytocolor data.color
bcol = colortoname arraytocolor data.bg
$("#entryindex").val pos
$("#entryname").val data.name
$("#entryauthor").val data.author
$("#entryfiletpye").val data.filetype
$("#entryfiletype").children()[resolvefiletype data.filetype].selected = true
$("#entryamiga").val data.amiga
$("#entryamiga").children()[1].textContent = ansiorascii data.amiga
$("#entryfont").val data.font
$("#entrycolor").val fcol
$("#entrycolor").children()[colorindex fcol ].selected = true
$("#entrybg").val bcol
$("#entrybg").children()[colorindex bcol ].selected = true
$("#entrywidth").val data.width
$("#entryline1").val data.line1
$("#entryline2").val data.line2
$("#entrytext").val data.text
$("#entryfile").val data.file
sahlicolor()
A Helper function to dump json out of an object as text:
dumpjson = (obj) ->
JSON.stringify obj,null,"\t"
Boolean / integer Helpers
booltoint = (bool) ->
bool + 1 - 1
inttobool = (intstr) ->
(intstr == 1).toString()
statustobool = (status) ->
if status is 'Ascii' then true else false
Resolve filetype offset in array:
resolvefiletype = (filetype) ->
options = {
"plain":0
"ansi":1
"xbin":2
"ice":3
"adf":4
"avatar":5
"bin":6
"idf":7
"pcboard":8
"tundra":9
}
options[filetype]
Resolve ansi or ascii status
ansiorascii = (status) ->
if status is 0 then "Ansi" else "Ascii"
Color conversion from array to color item:
This decimal to hex conversion only handles 00-FF but it's fine for this
purpose; we actually _want_ that limitation in the output.
dec2hex = (num) ->
"#{('000'+num.toString 16).slice -2}"
hex2dec = (num) ->
parseInt num,16
arraytocolor = (array) ->
c = (dec2hex x for x in array)[0..2].join ''
"##{c}"
colortoarray = (color) ->
color = color.slice(1)
c1 = [ color[0..1], color[2..3], color[4..5] ]
x = (hex2dec i for i in c1)
x.push 255
x
Need a way to convert the array back to the color name.
colortoname = (color) ->
names = {
"#E0E0E0":"Light Grey"
"#A0A0E0":"Light Blue"
"#9AFE2E":"Light Green"
"#FF0000":"Red"
"#FF8000":"Orange"
"#FFFF00":"Yellow"
"#00F000":"Green"
"#2EFEF7":"Cyan"
"#002EF7":"Blue"
"#0B0B3B":"Navy"
"#FF00FF":"Magenta"
"#8000FF":"Purple"
"#0A2A0A":"Dark Green"
"#3B3B3B":"Dark Grey"
"#FFFFFF":"White"
"#000000":"Black"
}
color = color.toUpperCase()
colorname = names[color]
bw = if hex2dec(color.slice(1)) > 8421504 then 'White' else "Black"
ret = if colorname? then colorname else bw
Similarly, need to be able to get the color index.
colorindex = (colorname) ->
names = {
"Light Grey":0
"Light Blue":1
"Light Green":2
"Red":3
"Orange":4
"Yellow":5
"Green":6
"Cyan":7
"Blue":8
"Navy":9
"Magenta":10
"Purple":11
"Dark Green":12
"Dark Grey":13
"White":14
"Black":15
}
names[colorname]
A function for changing the fore and background colors of the sahli ascii
example
sahlicolor = ->
fg = $('#entrycolor').val()
bg = $('#entrybg').val()
console.log 'sahlicolor',fg,bg
$('#sahliascii').css {'color':fg,'background':bg}
Function for loading the filelist from the specified directory on the
server/filesystem.
getfilelist = ->
location = $("#dirlocation").val()
$.get("../#{location}", (listing) ->
console.log listing
)
When clicking 'New' we want to make a brand new Sahli, and then clear out
the buttons and create the editor bit as blank.
newsahli = ->
sahli = new Sahli
sahli.data = sahli.empty
newentry = new Emptyfiledef
sahli.data.filedata.push newentry
sahli.edit()
And when clicking 'load' we want to load the existing sahli file.
loadsahli = ->
sahli = new Sahli
sahli.loader 'list.sahli'

View file

@ -1,11 +1,57 @@
// Generated by CoffeeScript 1.11.0
// Generated by CoffeeScript 1.9.0
/*
Sahli Editor
============
Editor for Sahli files.
- open existing file
- create new item
* get filename from dir
* insert SAUCE data if available
* use SAUCE data to find font
* allow Amiga choices
* colorpicker
- edit existing item
- remove item
- clear whole file
- copy/clone
- move items around
- sort items
- output to screen (copy into file)
* run from node - save filename dialog
***
It should be noted that this does not do bounds checking, and it would be very
possible to overflow this by using a debugger and such. As the purpose of this
is limited, and this should NOT be put on a live website, I feel that is ok for
now. Perhaps I will fix it after Revision.
***
== Create Initial crappage
We need to make a screen that has a few things in it for starters
Title, load existing, and new file options.
Silliness for checking that this works.
*/
(function() {
var Sahli, ansiorascii, arraytocolor, booltoint, colorindex, colortoarray, colortoname, dec2hex, dumpjson, emptyfiledef, getfilelist, hex2dec, inttobool, loadsahli, newsahli, resolvefiletype, sahlicolor, statustobool;
var Emptyfiledef, Sahli, ansiorascii, arraytocolor, booltoint, colorindex, colortoarray, colortoname, dec2hex, dumpjson, getfilelist, hex2dec, inttobool, loadsahli, newsahli, resolvefiletype, sahlicolor, statustobool;
$(function() {
return $("h1").hide().slideDown(500);
});
/*
Create buttons to choose between the New and Load functionalities
(As we aren't going to ever load a file _and_ do a new file.)
(If someone wants to do that, they can restart with F5 or something.)
Also hide the editor until needed, and initialize some elements.
*/
$(function() {
return $("#newsahli").button({
disabled: false
@ -40,17 +86,23 @@
}
});
$(".45box").css({
width: '45%',
width: '40ex',
display: 'inline-block'
});
$(".groupbox p").css({
margin: "0 0 .25em 0"
});
$(".colorbox").change((function(_this) {
return function() {
return sahlicolor();
};
})(this));
$(".colorbox").change(function() {
return sahlicolor();
});
$("#entryfont").change(function() {
var font;
font = $("#entryfont").val();
if (font === "ansifont") {
font = "BlockZone";
}
return $('pre').css('font-family', font);
});
$("#entryfilepick").change(function() {
if (this.files[0] != null) {
return $("#entryfile").val(this.files[0].name);
@ -61,8 +113,22 @@
});
});
emptyfiledef = (function() {
function emptyfiledef() {
/*
The sahli file definition format is as follows:
"file" - the actual filename on disk, "name" - the title of the piece,
the boolean 'amiga' indicates if it is ansi or ascii (True = ascii),
width is the width (widest point of the file), author the author of the piece,
the color and bg items define the color for amiga ascii, and the font
defines the font similarly. For PC ansi, this should be 'ansifont.'
The three remaining lines are informational and optional.
The slide format is currently unused, but consists of a background picture,
a html template, and a css file.
*/
Emptyfiledef = (function() {
function Emptyfiledef() {
this.file = "";
this.name = "";
this.amiga = true;
@ -77,13 +143,13 @@
this.text = "";
}
return emptyfiledef;
return Emptyfiledef;
})();
Sahli = (function() {
function Sahli() {
this.emptyfiledef = new emptyfiledef;
this.emptyfiledef = new Emptyfiledef;
this.emptyslidesdef = {
"background": "",
"template": "",
@ -123,8 +189,7 @@
}).click((function(_this) {
return function() {
$('#sahlioutput').text(dumpjson(_this.data));
$('#dumparea').show(100);
return console.log(dumpjson(_this.data));
return $('#dumparea').show(100);
};
})(this));
$('#listlist').button({
@ -141,7 +206,7 @@
}).click((function(_this) {
return function(event) {
var newentry;
newentry = new emptyfiledef;
newentry = new Emptyfiledef;
_this.data.filedata.push(newentry);
return _this.buildlist(_this.data);
};
@ -163,19 +228,19 @@
};
Sahli.prototype.buildlist = function(data) {
var i, item, j, k, len, len1, ref, ref1, x;
var i, item, x, _i, _j, _len, _len1, _ref, _ref1;
$('#list').show(100);
$('#list ol li').remove();
ref = this.data.filedata;
for (j = 0, len = ref.length; j < len; j++) {
i = ref[j];
_ref = this.data.filedata;
for (_i = 0, _len = _ref.length; _i < _len; _i++) {
i = _ref[_i];
console.log(i.author);
}
x = 0;
$('#dirlocation').val(this.data.location);
ref1 = this.data.filedata;
for (k = 0, len1 = ref1.length; k < len1; k++) {
item = ref1[k];
_ref1 = this.data.filedata;
for (_j = 0, _len1 = _ref1.length; _j < _len1; _j++) {
item = _ref1[_j];
$('#sortlist').append(this.additem(item, x++));
}
return $('#sortlist').sortable({
@ -186,16 +251,11 @@
},
stop: (function(_this) {
return function(event, ui) {
var e, l, len2, name, ref2, s;
var a, e, s;
a = 2;
s = ui.item.data().startpos;
e = ui.item.index();
_this.data.filedata = _this.rearrangearray(s, e, _this.data.filedata);
ref2 = _this.data.filedata;
for (l = 0, len2 = ref2.length; l < len2; l++) {
name = ref2[l];
console.log(name.author, name.name, name.file);
}
console.log('---');
return _this.buildlist(_this.data);
};
})(this)
@ -221,7 +281,7 @@
};
Sahli.prototype.genentryline = function(item, pos) {
var amigastatus, arrows, delbutton, entry;
var amigastatus, arrows, delbutton, entry, whichone;
arrows = "<span class='ui-icon ui-icon-arrowthick-2-n-s'></span>";
amigastatus = ansiorascii(booltoint(item.amiga));
delbutton = $("<span class='righty' id=del-" + pos + ">delete</span>").click((function(_this) {
@ -231,7 +291,9 @@
return _this.buildlist(_this.data);
};
})(this));
entry = $("<li class='entry' id='" + item.file + "'>" + arrows + amigastatus + " | " + item.author + " : " + item.name + " : " + item.file + "</li>");
whichone = "<li class='entry' id='" + item.file + "'>" + arrows + amigastatus + " |";
whichone += " " + item.author + " : " + item.name + " : " + item.file + "</li>";
entry = $(whichone);
return entry.append(delbutton);
};
@ -242,13 +304,15 @@
entry.name = $("#entryname").val();
entry.author = $("#entryauthor").val();
entry.amiga = statustobool($("#entryamiga").children()[1].textContent);
console.log($("#entryamiga").children()[1].textContent, entry.amiga, entry.author);
console.log($("#entryamiga").children()[1].textContent);
console.log(entry.amiga, entry.author);
entry.color = colortoarray($("#entrycolor").val());
entry.bg = colortoarray($("#entrybg").val());
entry.width = $("#entrywidth").val();
entry.line1 = $("#entryline1").val();
entry.line2 = $("#entryline2").val();
entry.text = $("#entrytext").val();
entry.font = $("#entryfont").val();
entry.file = $("#entryfile").val();
entry.filetype = $("#entryfiletype").val();
return this.buildlist(this.data);
@ -311,10 +375,20 @@
})();
/*
A Helper function to dump json out of an object as text:
*/
dumpjson = function(obj) {
return JSON.stringify(obj, null, "\t");
};
/*
Boolean / integer Helpers
*/
booltoint = function(bool) {
return bool + 1 - 1;
};
@ -331,6 +405,11 @@
}
};
/*
Resolve filetype offset in array:
*/
resolvefiletype = function(filetype) {
var options;
options = {
@ -348,6 +427,11 @@
return options[filetype];
};
/*
Resolve ansi or ascii status
*/
ansiorascii = function(status) {
if (status === 0) {
return "Ansi";
@ -356,6 +440,14 @@
}
};
/*
Color conversion from array to color item:
This decimal to hex conversion only handles 00-FF but it's fine for this
purpose; we actually _want_ that limitation in the output.
*/
dec2hex = function(num) {
return "" + (('000' + num.toString(16)).slice(-2));
};
@ -367,13 +459,13 @@
arraytocolor = function(array) {
var c, x;
c = ((function() {
var j, len, results;
results = [];
for (j = 0, len = array.length; j < len; j++) {
x = array[j];
results.push(dec2hex(x));
var _i, _len, _results;
_results = [];
for (_i = 0, _len = array.length; _i < _len; _i++) {
x = array[_i];
_results.push(dec2hex(x));
}
return results;
return _results;
})()).slice(0, 3).join('');
return "#" + c;
};
@ -383,18 +475,23 @@
color = color.slice(1);
c1 = [color.slice(0, 2), color.slice(2, 4), color.slice(4, 6)];
x = (function() {
var j, len, results;
results = [];
for (j = 0, len = c1.length; j < len; j++) {
i = c1[j];
results.push(hex2dec(i));
var _i, _len, _results;
_results = [];
for (_i = 0, _len = c1.length; _i < _len; _i++) {
i = c1[_i];
_results.push(hex2dec(i));
}
return results;
return _results;
})();
x.push(255);
return x;
};
/*
Need a way to convert the array back to the color name.
*/
colortoname = function(color) {
var bw, colorname, names, ret;
names = {
@ -421,6 +518,11 @@
return ret = colorname != null ? colorname : bw;
};
/*
Similarly, need to be able to get the color index.
*/
colorindex = function(colorname) {
var names;
names = {
@ -444,6 +546,12 @@
return names[colorname];
};
/*
A function for changing the fore and background colors of the sahli ascii
example
*/
sahlicolor = function() {
var bg, fg;
fg = $('#entrycolor').val();
@ -455,6 +563,13 @@
});
};
/*
Function for loading the filelist from the specified directory on the
server/filesystem.
Needs to be made into an actual real thing.
*/
getfilelist = function() {
var location;
location = $("#dirlocation").val();
@ -463,15 +578,26 @@
});
};
/*
When clicking 'New' we want to make a brand new Sahli, and then clear out
the buttons and create the editor bit as blank.
*/
newsahli = function() {
var newentry, sahli;
sahli = new Sahli;
sahli.data = sahli.empty;
newentry = new emptyfiledef;
newentry = new Emptyfiledef;
sahli.data.filedata.push(newentry);
return sahli.edit();
};
/*
And when clicking 'load' we want to load the existing sahli file.
*/
loadsahli = function() {
var sahli;
sahli = new Sahli;

View file

@ -83,12 +83,12 @@
<li>
<label for="font">Font:</label>
<select id='entryfont' name="font">
<option value="propaz">Topaz 2.0</option>
<option value="topaz">Topaz 1.3</option>
<option value="mosoul">mOsOul</option>
<option value="microknight">Microknight</option>
<option value="pot-noodle">P0t-nOodle</option>
<option value="blockzone">BlockZone</option>
<option value="Topaz1200">Topaz1200</option>
<option value="Topaz500">Topaz500</option>
<option value="mOsOul">mOsOul</option>
<option value="MicroKnight">Microknight</option>
<option value="P0t-nOodle">P0t-nOodle</option>
<option value="BlockZone">BlockZone</option>
<option value="ansifont">Ansifont</option>
</select>
</li>
@ -138,10 +138,11 @@
</div>
<div class='45box'>
<pre id='sahliascii'>
________________________, ._____,
| ___|___ | | | | |_____|
|_____ | _ | | |__| |
|_____|_____|___|__|______|___|
ABCDEFGHIJKLMNOPQRSTUVWXYZ&gt;&lt;~`'"^&amp;eXmPl
abcdefghijklmnopqrstuvwxyz1234567890?+@
* | ___)___ 7 I | | \-~=#/ $
: |_____ | {~} ! | \__[ : #~%
; (_____j__"__l___|__|______]___; 0Oo
</pre>
</div>
</div>

View file

@ -41,8 +41,6 @@
<li><span class="key">W</span> Begin scrolling upward</li>
<li><span class="key">A</span> Stop scrolling</li>
<li><span class="key">P</span> Previous Picture</li>
<li><span class="key">T</span> Jump to Top of picture (resets zoom)</li>
<li><span class="key">B</span> Jump to Bottom</li>
<li><span class="key">1</span><span class="key">2</span><span class="key">3</span><span class="key">4</span><span class="key">5</span> Alter scrollspeed (fast -> slow)</li>

View file

@ -14,104 +14,100 @@
"filedata": [
{
"file": "AD - Green Beam.scaled.png",
"name": "Green Beam",
"amiga": false,
"filetype": "image",
"width": "1600",
"author": "AD",
"font": "Propaz",
"color": [
0,
0,
0,
255
],
"bg": [
255,
255,
255,
255
],
"line1": "",
"line2": "",
"text": ""
},
{
"file": "om-boss.png",
"name": "Green Beam",
"amiga": false,
"filetype": "image",
"width": "640",
"author": "AD",
"font": "Propaz",
"color": [
0,
0,
0,
255
],
"bg": [
255,
255,
255,
255
],
"line1": "",
"line2": "",
"text": ""
},
{
"file": "spaceflight.asc",
"name": "Spaceflight",
"amiga": true,
"filetype": "plain",
"width": "80",
"author": "Urs",
"font": "pot-noodle",
"color": [
255,
128,
0,
255
],
"bg": [
0,
0,
0,
255
],
"line1": "Orange on Dark Grey",
"line2": "Test for plain files",
"text": ""
},
{
"file": "az0!-revi510n.txt",
"name": "Revision",
"amiga": true,
"filetype": "plain",
"width": "80",
"author": "Azzarro/Madwizards",
"font": "mosoul",
"color": [
"font": "Propaz",
"color": [
0,
240,
0,
255
],
"bg": [
255,
0,
0,
255
],
"line1": "Azzaro Returns",
"line2": "Revision ansi/ascii compo 2013",
"text": "Color test as well as mOsOul test."
"text": "Color test as well as Microknight test."
},
{
"file": "dS!-JUFV.txt",

View file

@ -14,7 +14,6 @@ l__________/__________|___|______l__________j_____j
class @Sahli
constructor: () ->
$('body').css('cursor', 'none');
# I don't think we actually are going to have one, as we don't
# need instance variables (things used outside the function)
@ -40,8 +39,6 @@ class @Sahli
@loadhugeansi picdata, inserthere
when 'tundra'
@loadhugeansi picdata, inserthere
when 'image'
@loadpicture picdata, inserthere
else
@loadplain picdata, inserthere
@ -52,7 +49,6 @@ class @Sahli
buf = $('<span>')
buf.css {'margin':'0 auto'}
ptxt = $('<pre>')
ptxt.addClass 'plaintext'
color = @calccolor(picdata.color)
bgcolor = @calccolor(picdata.bg)
pdiv.addClass 'scrolly'
@ -62,9 +58,9 @@ class @Sahli
'background-color': bgcolor
'margin': 'auto'
'display': 'inline-block'
#ptxt.width picdata.width * 8
#@origwidth = ptxt.width
#pdiv.width ptxt.width
ptxt.width picdata.width * 8
@origwidth = ptxt.width
pdiv.width ptxt.width
pdiv.prepend buf.clone()
pdiv.append ptxt
pdiv.append buf
@ -82,72 +78,6 @@ class @Sahli
req.open 'GET', fname, true
req.send null
@increaseFont = (node, increaseBy=5) ->
current_size = parseInt($(node).css("font-size"));
$(node).css("font-size", current_size + increaseBy);
@loadpicture = (picdata, inserthere) ->
fname = @location + '/' + picdata.file
pdiv = $('<div>')
pdiv.addClass 'scrolly'
pdiv.addClass 'image'
pdiv.width window.innerWidth
pdiv.css 'display', 'inline-block'
pimg = $('<img src="' + fname + '" />')
pimg.addClass 'fullwidth'
pdiv.append pimg
inserthere.after pdiv
$('h6').hide()
$('body').scrollTop 0
@origwidth = picdata.width
@origheight = picdata.height
@bestfit()
@fullwidthplain = =>
if ($('pre').css("font-size") == "16px")
$('pre').css("font-size", "2.5vw");
else
$('pre').css("font-size", "16px");
@togglefullwidthmode = =>
if ($('pre').hasClass('plaintext'))
@fullwidthplain()
else
if $('div.scrolly').hasClass('image')
@bestfit()
else
@zoom()
@zoomin = =>
if ($('pre').hasClass('plaintext'))
@increaseFont($('pre'), 2)
else
@zoom(100);
@zoomout = =>
if ($('pre').hasClass('plaintext'))
@increaseFont($('pre'), -2)
else
@zoom(-100);
@bestfit = =>
if $('div.scrolly').hasClass('image')
if $('div.scrolly').hasClass('bestfitMode')
$('div.scrolly').removeClass 'bestfitMode'
$('div.scrolly').addClass 'fullwidthMode'
$('div.scrolly').width window.innerWidth
$('div.scrolly').height("")
$('img.bestfit').addClass 'fullwidth'
$('img.bestfit').removeClass 'bestfit'
else
$('h6').hide()
$('div.scrolly').addClass 'bestfitMode'
$('div.scrolly').removeClass 'fullwidthMode'
$('div.scrolly').width window.innerWidth
$('div.scrolly').height window.innerHeight
$('img.fullwidth').addClass 'bestfit'
$('img.fullwidth').removeClass 'fullwidth'
@loadhugeansi = (picdata, inserthere) ->
fname = @location + '/' + picdata.file
pdiv = $('<div>')
@ -167,7 +97,7 @@ class @Sahli
@origwidth = canvwidth
@origheight = calcheight
pdiv.width canvwidth
), 30, {'bits': '8', "font": picdata.font}
), 30, 'bits': '8'
@loadavatar = (picdata, inserthere) ->
console.log 'avatar', picdata, inserthere
@ -207,7 +137,6 @@ class @Sahli
filedata = @filedata
filedata[i].pic = $('<h6>' + filedata[i].file + '</h6>')
viewbox.append filedata[i].pic
$('h6').show()
@loadpic filedata[i], filedata[i].pic
@currentpic += 1
if @currentpic > filedata.length - 1
@ -217,13 +146,6 @@ class @Sahli
$('body').stop()
@loadinfopanel i
@prevpic = =>
i = @currentpic-2
if i < 0
i = i + @filedata.length
@currentpic = i
@nextpic()
@togglefullscreen = ->
docElm = document.documentElement
if @fullscreen
@ -308,7 +230,7 @@ class @Sahli
zoomee.width newwidth
$('canvas').width newwidth
else
if parseInt( zoomee.width(), 10 ) != parseInt( @origwidth, 10)
if zoomee.width() != @origwidth
zoomee.width @origwidth
$('canvas').width '100%'
else
@ -393,26 +315,24 @@ class @Sahli
switch ev.which
when @keycode ' '
@nextpic()
when @keycode 'p'
@prevpic()
when @keycode 'f'
@togglefullscreen()
when @keycode 's'
@setscroll()
when @keycode 't'
$('body').scrollTop 0
@togglefullwidthmode()
@zoom 0
when @keycode 'b'
$('body').scrollTop $('body').height()
when @keycode 'a'
$('body').stop()
@scroll_direction = - @scroll_direction
when @keycode 'z'
@togglefullwidthmode()
@zoom()
when @keycode 'e'
@zoomin()
@zoom 100
when @keycode 'r'
@zoomout()
@zoom -100
when @keycode 'w'
@changescrolldirection -1
when @keycode 'x'
@ -422,7 +342,6 @@ class @Sahli
when @keycode 'i'
$('div.infobox').toggle()
when @keycode 'v'
$('h6').show()
$('h6').height( (window.innerHeight - $('.scrolly').height()) / 2 )
when @keycode '1'
@changespeed 1
@ -437,10 +356,6 @@ class @Sahli
@scroll_speed = 4
when @keycode '5'
@changespeed 5
when @keycode '8'
@increaseFont($('pre'), -2)
when @keycode '9'
@increaseFont($('pre'), 2)
when 40 # down
@moveline 1
when 38 # up

View file

@ -10,10 +10,6 @@ body {
border: none;
}
pre {
line-height: 100%;
}
#top {
border: 1px solid green;
color: green;
@ -44,7 +40,7 @@ h6 {
.help {
position: fixed;
top: 1em;
left: 25%;
left: 33%;
text-align: left;
background-color: lightgrey;
border: outset darkgray;
@ -53,19 +49,7 @@ h6 {
border-radius: 8px;
font-family: topaz1200,mOsOul, Consolas, monospace;
opacity: .85;
width: 50%;
}
.fullwidth {
width: 100%;
}
.bestfit {
padding: 0;
display: block;
margin: 0 auto;
max-width: 100%;
max-height: 100%;
width: 33%;
}
.keylist {

148
sahli.js
View file

@ -1,4 +1,4 @@
// Generated by CoffeeScript 1.9.3
// Generated by CoffeeScript 1.9.0
/*
.___________________________________, ___
@ -16,9 +16,7 @@ l__________/__________|___|______l__________j_____j
(function() {
this.Sahli = (function() {
function Sahli() {
$('body').css('cursor', 'none');
}
function Sahli() {}
Sahli.loadpic = function(picdata, inserthere) {
switch (picdata.filetype) {
@ -42,8 +40,6 @@ l__________/__________|___|______l__________j_____j
return this.loadhugeansi(picdata, inserthere);
case 'tundra':
return this.loadhugeansi(picdata, inserthere);
case 'image':
return this.loadpicture(picdata, inserthere);
default:
return this.loadplain(picdata, inserthere);
}
@ -59,7 +55,6 @@ l__________/__________|___|______l__________j_____j
'margin': '0 auto'
});
ptxt = $('<pre>');
ptxt.addClass('plaintext');
color = this.calccolor(picdata.color);
bgcolor = this.calccolor(picdata.bg);
pdiv.addClass('scrolly');
@ -70,6 +65,9 @@ l__________/__________|___|______l__________j_____j
'margin': 'auto',
'display': 'inline-block'
});
ptxt.width(picdata.width * 8);
this.origwidth = ptxt.width;
pdiv.width(ptxt.width);
pdiv.prepend(buf.clone());
pdiv.append(ptxt);
pdiv.append(buf);
@ -89,91 +87,6 @@ l__________/__________|___|______l__________j_____j
return req.send(null);
};
Sahli.increaseFont = function(node, increaseBy) {
var current_size;
if (increaseBy == null) {
increaseBy = 5;
}
current_size = parseInt($(node).css("font-size"));
return $(node).css("font-size", current_size + increaseBy);
};
Sahli.loadpicture = function(picdata, inserthere) {
var fname, pdiv, pimg;
fname = this.location + '/' + picdata.file;
pdiv = $('<div>');
pdiv.addClass('scrolly');
pdiv.addClass('image');
pdiv.width(window.innerWidth);
pdiv.css('display', 'inline-block');
pimg = $('<img src="' + fname + '" />');
pimg.addClass('fullwidth');
pdiv.append(pimg);
inserthere.after(pdiv);
$('h6').hide();
$('body').scrollTop(0);
this.origwidth = picdata.width;
this.origheight = picdata.height;
return this.bestfit();
};
Sahli.fullwidthplain = function() {
if ($('pre').css("font-size") === "16px") {
return $('pre').css("font-size", "2.5vw");
} else {
return $('pre').css("font-size", "16px");
}
};
Sahli.togglefullwidthmode = function() {
if ($('pre').hasClass('plaintext')) {
return Sahli.fullwidthplain();
} else {
if ($('div.scrolly').hasClass('image')) {
return Sahli.bestfit();
} else {
return Sahli.zoom();
}
}
};
Sahli.zoomin = function() {
if ($('pre').hasClass('plaintext')) {
return Sahli.increaseFont($('pre'), 2);
} else {
return Sahli.zoom(100);
}
};
Sahli.zoomout = function() {
if ($('pre').hasClass('plaintext')) {
return Sahli.increaseFont($('pre'), -2);
} else {
return Sahli.zoom(-100);
}
};
Sahli.bestfit = function() {
if ($('div.scrolly').hasClass('image')) {
if ($('div.scrolly').hasClass('bestfitMode')) {
$('div.scrolly').removeClass('bestfitMode');
$('div.scrolly').addClass('fullwidthMode');
$('div.scrolly').width(window.innerWidth);
$('div.scrolly').height("");
$('img.bestfit').addClass('fullwidth');
return $('img.bestfit').removeClass('bestfit');
} else {
$('h6').hide();
$('div.scrolly').addClass('bestfitMode');
$('div.scrolly').removeClass('fullwidthMode');
$('div.scrolly').width(window.innerWidth);
$('div.scrolly').height(window.innerHeight);
$('img.fullwidth').addClass('bestfit');
return $('img.fullwidth').removeClass('fullwidth');
}
}
};
Sahli.loadhugeansi = function(picdata, inserthere) {
var calcheight, canvwidth, fname, pdiv;
fname = this.location + '/' + picdata.file;
@ -198,8 +111,7 @@ l__________/__________|___|______l__________j_____j
return pdiv.width(canvwidth);
};
})(this)), 30, {
'bits': '8',
"font": picdata.font
'bits': '8'
});
};
@ -249,7 +161,6 @@ l__________/__________|___|______l__________j_____j
filedata = Sahli.filedata;
filedata[i].pic = $('<h6>' + filedata[i].file + '</h6>');
viewbox.append(filedata[i].pic);
$('h6').show();
Sahli.loadpic(filedata[i], filedata[i].pic);
Sahli.currentpic += 1;
if (Sahli.currentpic > filedata.length - 1) {
@ -261,16 +172,6 @@ l__________/__________|___|______l__________j_____j
return Sahli.loadinfopanel(i);
};
Sahli.prevpic = function() {
var i;
i = Sahli.currentpic - 2;
if (i < 0) {
i = i + Sahli.filedata.length;
}
Sahli.currentpic = i;
return Sahli.nextpic();
};
Sahli.togglefullscreen = function() {
var docElm;
docElm = document.documentElement;
@ -369,7 +270,7 @@ l__________/__________|___|______l__________j_____j
zoomee.width(newwidth);
return $('canvas').width(newwidth);
} else {
if (parseInt(zoomee.width(), 10) !== parseInt(this.origwidth, 10)) {
if (zoomee.width() !== this.origwidth) {
zoomee.width(this.origwidth);
return $('canvas').width('100%');
} else {
@ -380,7 +281,7 @@ l__________/__________|___|______l__________j_____j
};
Sahli.panelmode = function() {
var canvs, ct, drawcol, j, k, len, len1, level, newheight, newwidth, numcols, numpanels, outer, panelratio, panelslotheight, panelsperslot, pic, picdpercol, results, screenratio, wh, ww, x;
var canvs, ct, drawcol, level, newheight, newwidth, numcols, numpanels, outer, panelratio, panelslotheight, panelsperslot, pic, picdpercol, screenratio, wh, ww, x, _i, _j, _len, _len1, _results;
$('#panel').toggle();
canvs = $('canvas');
$('.scrolly').width(this.origwidth);
@ -408,9 +309,9 @@ l__________/__________|___|______l__________j_____j
drawcol = 1;
ct = 0;
outer.append(this.createpanel(1, newwidth - 6));
results = [];
for (j = 0, len = canvs.length; j < len; j++) {
pic = canvs[j];
_results = [];
for (_i = 0, _len = canvs.length; _i < _len; _i++) {
pic = canvs[_i];
$("#column" + drawcol).append(pic);
level += 1;
ct += 1;
@ -418,19 +319,19 @@ l__________/__________|___|______l__________j_____j
level = 0;
drawcol = drawcol + 1;
if (ct < numpanels) {
results.push(outer.append(this.createpanel(drawcol, newwidth - 6)));
_results.push(outer.append(this.createpanel(drawcol, newwidth - 6)));
} else {
results.push(void 0);
_results.push(void 0);
}
} else {
results.push(void 0);
_results.push(void 0);
}
}
return results;
return _results;
} else {
$('#outbox').show();
for (k = 0, len1 = canvs.length; k < len1; k++) {
pic = canvs[k];
for (_j = 0, _len1 = canvs.length; _j < _len1; _j++) {
pic = canvs[_j];
$('.scrolly').append(pic);
}
canvs.width(this.origwidth);
@ -473,26 +374,24 @@ l__________/__________|___|______l__________j_____j
switch (ev.which) {
case _this.keycode(' '):
return _this.nextpic();
case _this.keycode('p'):
return _this.prevpic();
case _this.keycode('f'):
return _this.togglefullscreen();
case _this.keycode('s'):
return _this.setscroll();
case _this.keycode('t'):
$('body').scrollTop(0);
return _this.togglefullwidthmode();
return _this.zoom(0);
case _this.keycode('b'):
return $('body').scrollTop($('body').height());
case _this.keycode('a'):
$('body').stop();
return _this.scroll_direction = -_this.scroll_direction;
case _this.keycode('z'):
return _this.togglefullwidthmode();
return _this.zoom();
case _this.keycode('e'):
return _this.zoomin();
return _this.zoom(100);
case _this.keycode('r'):
return _this.zoomout();
return _this.zoom(-100);
case _this.keycode('w'):
return _this.changescrolldirection(-1);
case _this.keycode('x'):
@ -502,7 +401,6 @@ l__________/__________|___|______l__________j_____j
case _this.keycode('i'):
return $('div.infobox').toggle();
case _this.keycode('v'):
$('h6').show();
return $('h6').height((window.innerHeight - $('.scrolly').height()) / 2);
case _this.keycode('1'):
return _this.changespeed(1);
@ -517,10 +415,6 @@ l__________/__________|___|______l__________j_____j
return _this.scroll_speed = 4;
case _this.keycode('5'):
return _this.changespeed(5);
case _this.keycode('8'):
return _this.increaseFont($('pre'), -2);
case _this.keycode('9'):
return _this.increaseFont($('pre'), 2);
case 40:
return _this.moveline(1);
case 38:

View file

@ -1,130 +0,0 @@
#!/usr/bin/env python
# coding:utf-8
"""
Author: Sir Garbagetruck --<truck@whatever>
Purpose: base class for Sahli file
Created: 2020/04/09
"""
import json
########################################################################
class sahlifile:
"""the Sahli file structure and classes to futz with"""
# ----------------------------------------------------------------------
def __init__(self, filename):
"""Constructor"""
self.valid_filetypes = [
"plain",
"ansi",
"xbin",
"ice",
"adf",
"avatar",
"bin",
"idf",
"pcboard",
"tundra"
]
self.valid_fonts = [
'Propaz', 'ansifont', 'mOsOul', 'Microknight', 'p0t-nOodle'
]
if filename is not None:
with open(filename) as f:
self.sahli = json.load(f)
else:
location = self.blank_location()
slides = self.blank_slides()
filedata = []
self.sahli = {
'location': location,
'slides': slides,
'filedata': filedata
}
def blank_slides(self):
"""blank slide structure"""
slides = {
'background': '',
'template': '',
'css': ''
}
return slides
def blank_location(self):
"""blank location structure"""
return ''
def blank_picture(self):
"""Blank picture structure"""
return {
'file': '',
'name': '',
'amiga': False,
'filetype': 'image',
'width': '1600',
'author': '',
'font': 'Propaz',
'color': [0, 0, 0, 255],
'bg': [255, 255, 255, 255],
'line1': '',
'line2': '',
'text': ''
}
# ----------------------------------------------------------------------
def blank_amiga_ascii(self):
"""blank amiga ascii"""
return {
'file': '',
'name': '',
'amiga': True,
'filetype': 'plain',
'width': '80',
'author': '',
'font': 'Propaz',
'color': [250, 250, 250, 255],
'bg': [0, 0, 0, 255],
'line1': '',
'line2': '',
'text': ''
}
# ----------------------------------------------------------------------
def blank_ansi(self):
"""blank PC Ansi"""
return {
'file': '',
'name': '',
'amiga': False,
'filetype': 'ansi',
'width': '80',
'author': '',
'font': 'Propaz',
'color': [255, 255, 255, 255],
'bg': [0, 0, 0, 255],
'line1': '',
'line2': '',
'text': ''
}
def blank_filedata(self):
"""Blank filedata structure"""
filedata = {
'file': '',
'name': '',
'amiga': False,
'filetype': 'image',
'width': '',
'author': '',
'font': 'Propaz',
'color': [0, 0, 0, 255],
'bg': [255, 255, 255, 255],
'line1': '',
'line2': '',
'text': ''
}
return filedata

510
sauce.py
View file

@ -1,510 +0,0 @@
#! /usr/bin/env python
#
# _______
# ____________ _______ _\__ /_________ ___ _____
# | _ _ \ _ | ____\ _ / | |/ _ \
# | / / / / | | | /___/ _ | | / /
# |___/___/ /___/____|________|___ | |_| |___|_____/
# \__/ |___|
#
# (c) 2006-2012 Wijnand Modderman-Lenstra - https://maze.io/
#
'''
Parser for SAUCE or Standard Architecture for Universal Comment Extensions.
'''
__author__ = 'Wijnand Modderman-Lenstra <maze@pyth0n.org>'
__copyright__ = '(C) 2006-2012 Wijnand Modderman-Lenstra'
__license__ = 'LGPL'
__version__ = '1.2'
__url__ = 'https://github.com/tehmaze/sauce'
import datetime
import os
import struct
try:
from io import StringIO
except ImportError:
from io import StringIO
class SAUCE(object):
'''
Parser for SAUCE or Standard Architecture for Universal Comment Extensions,
as defined in http://www.acid.org/info/sauce/s_spec.htm.
:param filename: file name or file handle
:property author: Name or 'handle' of the creator of the file
:property datatype: Type of data
:property date: Date the file was created
:property filesize: Original filesize NOT including any information of
SAUCE
:property group: Name of the group/company the creator is employed by
:property title: Title of the file
Example::
>>> art = open('31337.ANS', 'rb')
>>> nfo = sauce.SAUCE(art)
>>> nfo.author
'maze'
...
>>> nfo.group
''
>>> nfo.group = 'mononoke'
>>> raw = str(nfo)
Saving the new file::
>>> sav = open('31337.NEW', 'wb')
>>> nfo.write(sav)
>>> # OR you can do:
>>> sav = nfo.write('31337.NEW')
'''
# template
template = (
# name default size type
('SAUCE', 'SAUCE', 5, '5s'),
('SAUCEVersion', '00', 2, '2s'),
('Title', '\x00' * 35, 35, '35s'),
('Author', '\x00' * 20, 20, '20s'),
('Group', '\x00' * 20, 20, '20s'),
('Date', '\x00' * 8, 8, '8s'),
('FileSize', [0], 4, 'I'),
('DataType', [0], 1, 'B'),
('FileType', [0], 1, 'B'),
('TInfo1', [0], 2, 'H'),
('TInfo2', [0], 2, 'H'),
('TInfo3', [0], 2, 'H'),
('TInfo4', [0], 2, 'H'),
('Comments', [0], 1, 'B'),
('Flags', [0], 1, 'B'),
('Filler', ['\x00'] * 22, 22, '22c'),
)
templates = [t[0] for t in template]
datatypes = ['None', 'Character', 'Graphics', 'Vector', 'Sound',
'BinaryText', 'XBin', 'Archive', 'Executable']
filetypes = {
'None': {
'filetype': ['Undefined'],
},
'Character': {
'filetype': ['ASCII', 'ANSi', 'ANSiMation', 'RIP', 'PCBoard',
'Avatar', 'HTML', 'Source'],
'flags': {0: 'None', 1: 'iCE Color'},
'tinfo': (
('width', 'height', None, None),
('width', 'height', None, None),
('width', 'height', None, None),
('width', 'height', 'colors', None),
('width', 'height', None, None),
('width', 'height', None, None),
(None, None, None, None),
),
},
'Graphics': {
'filetype': ['GIF', 'PCX', 'LBM/IFF', 'TGA', 'FLI', 'FLC',
'BMP', 'GL', 'DL', 'WPG', 'PNG', 'JPG', 'MPG',
'AVI'],
'tinfo': (('width', 'height', 'bpp')) * 14,
},
'Vector': {
'filetype': ['DX', 'DWG', 'WPG', '3DS'],
},
'Sound': {
'filetype': ['MOD', '669', 'STM', 'S3M', 'MTM', 'FAR', 'ULT',
'AMF', 'DMF', 'OKT', 'ROL', 'CMF', 'MIDI', 'SADT',
'VOC', 'WAV', 'SMP8', 'SMP8S', 'SMP16', 'SMP16S',
'PATCH8', 'PATCH16', 'XM', 'HSC', 'IT'],
'tinfo': ((None,)) * 16 + (('Sampling Rate',)) * 4,
},
'BinaryText': {
'flags': {0: 'None', 1: 'iCE Color'},
},
'XBin': {
'tinfo': (('width', 'height'),),
},
'Archive': {
'filetype': ['ZIP', 'ARJ', 'LZH', 'ARC', 'TAR', 'ZOO', 'RAR',
'UC2', 'PAK', 'SQZ'],
},
}
def __init__(self, filename='', data=''):
assert (filename or data), 'Need either filename or record'
if filename:
# if type(filename) == file:
# self.filehand = filename
# else:
self.filehand = open(filename, 'rb')
self._size = os.path.getsize(self.filehand.name)
else:
self._size = len(data)
self.filehand = StringIO(data)
self.record, self.data = self._read()
def __str__(self):
return ''.join(list(self._read_file()))
def _read_file(self):
# Buffered reader (generator), reads the original file without SAUCE
# record.
self.filehand.seek(0)
# Check if we have SAUCE data
if self.record:
reads, rest = divmod(self._size - 128, 1024)
else:
reads, rest = divmod(self._size, 1024)
for x in range(0, reads):
yield self.filehand.read(1024)
if rest:
yield self.filehand.read(rest)
def _read(self):
if self._size >= 128:
self.filehand.seek(self._size - 128)
record = self.filehand.read(128)
if record.startswith(b'SAUCE'):
self.filehand.seek(0)
return record, self.filehand.read(self._size - 128)
self.filehand.seek(0)
return None, self.filehand.read()
def _gets(self, key):
if self.record is None:
return None
name, default, offset, size, stype = self._template(key)
data = self.record[offset:offset + size]
data = struct.unpack(stype, data)
if stype[-1] in 'cs':
# return ''.join(data)
return data[0].decode()
elif stype[-1] in 'BI' and len(stype) == 1:
return data[0]
else:
return data
def _puts(self, key, data):
name, default, offset, size, stype = self._template(key)
#print offset, size, data, repr(struct.pack(stype, data))
if self.record is None:
self.record = self.sauce()
self.record = ''.join([
self.record[:offset],
struct.pack(stype, data),
self.record[offset + size:]
])
return self.record
def _template(self, key):
index = self.templates.index(key)
name, default, size, stype = self.template[index]
offset = sum([self.template[x][2] for x in range(0, index)])
return name, default, offset, size, stype
def sauce(self):
'''
Get the raw SAUCE record.
'''
if self.record:
return self.record
else:
data = 'SAUCE'
for name, default, size, stype in self.template[1:]:
#print stype, default
if stype[-1] in 's':
data += struct.pack(stype, default)
else:
data += struct.pack(stype, *default)
return data
def write(self, filename):
'''
Save the file including SAUCE data to the given file(handle).
'''
filename = type(filename) == file and filename or open(
filename, 'wb')
for part in self._read_file():
filename.write(part)
filename.write(self.sauce())
return filename
# SAUCE meta data
def get_author(self):
astr = self._gets('Author')
if astr is not None:
return astr.strip()
else:
return ''
def set_author(self, author):
self._puts('Author', author)
return self
def get_comments(self):
return self._gets('Comments')
def set_comments(self, comments):
self._puts('Comments', comments)
return self
def get_datatype(self):
return self._gets('DataType')
def get_datatype_str(self):
datatype = self.datatype
if datatype is None:
return None
if datatype < len(self.datatypes):
return self.datatypes[datatype]
else:
return None
def set_datatype(self, datatype):
if type(datatype) == str:
datatype = datatype.lower().title() # fOoBAR -> Foobar
datatype = self.datatypes.index(datatype)
self._puts('DataType', datatype)
return self
def get_date(self):
return self._gets('Date')
def get_date_str(self, format='%Y%m%d'):
return datetime.datetime.strptime(self.date, format)
def set_date(self, date=None, format='%Y%m%d'):
if date is None:
date = datetime.datetime.now().strftime(format)
elif type(date) in [datetime.date, datetime.datetime]:
date = date.strftime(format)
elif type(date) in [int, int, float]:
date = datetime.datetime.fromtimestamp(date).strftime(format)
self._puts('Date', date)
return self
def get_filesize(self):
return self._gets('FileSize')
def set_filesize(self, size):
self._puts('FileSize', size)
def get_filler(self):
return self._gets('Filler')
def get_filler_str(self):
filler = self._gets('Filler')
if filler is None:
return ''
else:
return filler.rstrip('\x00')
def get_filetype(self):
return self._gets('FileType')
def get_filetype_str(self):
datatype = self.datatype_str
filetype = self.filetype
if datatype is None or filetype is None:
return None
if datatype in self.filetypes and \
'filetype' in self.filetypes[datatype] and \
filetype < len(self.filetypes[datatype]['filetype']):
return self.filetypes[datatype]['filetype'][filetype]
else:
return None
def set_filetype(self, filetype):
datatype = self.datatype_str
if type(filetype) == str:
filetype = filetype.lower().title() # fOoBAR -> Foobar
filetype = [name.lower().title()
for name in self.filetypes[datatype]['filetype']].index(filetype)
self._puts('FileType', filetype)
return self
def get_flags(self):
return self._gets('Flags')
def set_flags(self, flags):
self._puts('Flags', flags)
return self
def get_flags_str(self):
datatype = self.datatype_str
filetype = self.filetype
if datatype is None or filetype is None:
return None
if datatype in self.filetypes and \
'flags' in self.filetypes[datatype] and \
filetype < len(self.filetypes[datatype]['filetype']):
return self.filetypes[datatype]['filetype'][filetype]
else:
return None
def get_group(self):
gstr = self._gets('Group')
if gstr is not None:
return gstr.strip()
else:
return ''
# return self._gets('Group').strip()
def set_group(self, group):
self._puts('Group', group)
return self
def _get_tinfo_name(self, i):
datatype = self.datatype_str
filetype = self.filetype
if datatype is None or filetype is None:
return None
try:
return self.filetypes[datatype]['tinfo'][filetype][i - 1]
except (KeyError, IndexError):
return ''
def get_tinfo1(self):
tinfo = self._gets('TInfo1')
if tinfo is not None:
return tinfo[0]
else:
return ''
def get_tinfo1_name(self):
return self._get_tinfo_name(1)
def set_tinfo1(self, tinfo):
self._puts('TInfo1', tinfo)
return self
def get_tinfo2(self):
tinfo = self._gets('TInfo2')
if tinfo is not None:
return tinfo[0]
else:
return ''
def get_tinfo2_name(self):
return self._get_tinfo_name(2)
def set_tinfo2(self, tinfo):
self._puts('TInfo2', tinfo)
return self
def get_tinfo3(self):
tinfo = self._gets('TInfo3')
if tinfo is not None:
return tinfo[0]
return ''
def get_tinfo3_name(self):
return self._get_tinfo_name(3)
def set_tinfo3(self, tinfo):
self._puts('TInfo3', tinfo)
return self
def get_tinfo4(self):
tinfo = self._gets('TInfo4')
if tinfo is not None:
return tinfo[0]
return ''
def get_tinfo4_name(self):
return self._get_tinfo_name(4)
def set_tinfo4(self, tinfo):
self._puts('TInfo4', tinfo)
return self
def get_title(self):
tstr = self._gets('Title')
if tstr is not None:
return tstr.strip()
else:
return ''
# return self._gets('Title').strip()
def set_title(self, title):
self._puts('Title', title)
return self
def get_version(self):
return self._gets('SAUCEVersion')
def set_version(self, version):
self._puts('SAUCEVersion', version)
return self
# properties
author = property(get_author, set_author)
comments = property(get_comments, set_comments)
datatype = property(get_datatype, set_datatype)
datatype_str = property(get_datatype_str)
date = property(get_date, set_date)
filesize = property(get_filesize, set_filesize)
filetype = property(get_filetype, set_filetype)
filetype_str = property(get_filetype_str)
filler = property(get_filler)
filler_str = property(get_filler_str)
flags = property(get_flags, set_flags)
flags_str = property(get_flags_str)
group = property(get_group, set_group)
tinfo1 = property(get_tinfo1, set_tinfo1)
tinfo1_name = property(get_tinfo1_name)
tinfo2 = property(get_tinfo2, set_tinfo2)
tinfo2_name = property(get_tinfo2_name)
tinfo3 = property(get_tinfo3, set_tinfo3)
tinfo3_name = property(get_tinfo3_name)
tinfo4 = property(get_tinfo4, set_tinfo4)
tinfo4_name = property(get_tinfo4_name)
title = property(get_title, set_title)
version = property(get_version)
if __name__ == '__main__':
import sys
if len(sys.argv) != 2:
print('%s <file>' % (sys.argv[0],), file=sys.stderr)
sys.exit(1)
else:
test = SAUCE(sys.argv[1])
def show(sauce):
print('Version.:', sauce.version)
print('Title...:', sauce.title)
print('Author..:', sauce.author)
print('Group...:', sauce.group)
print('Date....:', sauce.date)
print('FileSize:', sauce.filesize)
print('DataType:', sauce.datatype, sauce.datatype_str)
print('FileType:', sauce.filetype, sauce.filetype_str)
print('TInfo1..:', sauce.tinfo1)
print('TInfo2..:', sauce.tinfo2)
print('TInfo3..:', sauce.tinfo3)
print('TInfo4..:', sauce.tinfo4)
print('Flags...:', sauce.flags, sauce.flags_str)
print('Record..:', len(sauce.record), repr(sauce.record))
print('Filler..:', sauce.filler_str)
if test.record:
show(test)
else:
print('No SAUCE record found')
test = SAUCE(data=test.sauce())
show(test)

View file

@ -1,15 +0,0 @@
#!/usr/bin/env python
# coding:utf-8
"""
Author: Sir Garbagetruck --<truck@whatever>
Purpose: setup script for Sahli editor tools
Created: 2020/04/09
"""
from setuptools import setup, find_packages
setup(
name="SahliEditor",
version="0.1",
packages=find_packages()
)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

View file

@ -7,4 +7,3 @@
(C) SAHLI ansi & ascii for load page
(C) redo load page (not popup, info)
(B) Preload
(C) Chrome app