"strings"
"strings.private"
"system"
+ "system.private"
"threads.private"
"tools.profiler.private"
"tuples"
}
{
{ "object" "kernel" }
- "?"
+ "compiled?"
{ "compiled?" "words" }
f
}
{ "fopen" "io.streams.c" }
{ "fgetc" "io.streams.c" }
{ "fread" "io.streams.c" }
+ { "fputc" "io.streams.c" }
{ "fwrite" "io.streams.c" }
{ "fflush" "io.streams.c" }
{ "fclose" "io.streams.c" }
{ "innermost-frame-scan" "kernel.private" }
{ "set-innermost-frame-quot" "kernel.private" }
{ "call-clear" "kernel" }
- { "(os-envs)" "system" }
+ { "(os-envs)" "system.private" }
+ { "(set-os-envs)" "system.private" }
{ "resize-byte-array" "byte-arrays" }
{ "resize-bit-array" "bit-arrays" }
{ "resize-float-array" "float-arrays" }
! Copyright (C) 2005, 2007 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: arrays kernel kernel.private slots.private math assocs
-math.private sequences sequences.private vectors ;
+ math.private sequences sequences.private vectors ;
IN: hashtables
<PRIVATE
2 fixnum+fast over wrap ; inline
: (key@) ( key keys i -- array n ? )
- 3dup swap array-nth dup ((tombstone)) eq? [
- 2drop probe (key@)
- ] [
- dup ((empty)) eq? [
- 3drop nip f f
- ] [
- = [ rot drop t ] [ probe (key@) ] if
- ] if
- ] if ; inline
+ 3dup swap array-nth
+ dup ((empty)) eq?
+ [ 3drop nip f f ]
+ [
+ =
+ [ rot drop t ]
+ [ probe (key@) ]
+ if
+ ]
+ if ; inline
: key@ ( key hash -- array n ? )
hash-array 2dup hash@ (key@) ; inline
quotations.private sbufs sbufs.private sequences
sequences.private slots.private strings strings.private system
threads.private tuples tuples.private vectors vectors.private
-words words.private assocs inspector compiler.units ;
+words words.private assocs inspector compiler.units
+system.private ;
IN: inference.known-words
! Shuffle words
\ fwrite { string alien } { } <effect> set-primitive-effect
+\ fputc { object alien } { } <effect> set-primitive-effect
+
\ fread { integer string } { object } <effect> set-primitive-effect
\ fflush { alien } { } <effect> set-primitive-effect
\ (os-envs) { } { array } <effect> set-primitive-effect
+\ (set-os-envs) { array } { } <effect> set-primitive-effect
+
\ do-primitive [ \ do-primitive no-effect ] "infer" set-word-prop
\ dll-valid? { object } { object } <effect> set-primitive-effect
: nth-byte ( x n -- b ) -8 * shift mask-byte ; inline
-: >le ( x n -- str ) [ nth-byte ] with "" map-as ;
+: >le ( x n -- str ) [ nth-byte ] with B{ } map-as ;
: >be ( x n -- str ) >le dup reverse-here ;
: d>w/w ( d -- w1 w2 )
{ $subsection "fs-meta" }
{ $subsection "directories" }
{ $subsection "delete-move-copy" }
-{ $subsection "unique" }
{ $see-also "os" } ;
ABOUT: "io.files"
TUPLE: file-info type size permissions modified ;
HOOK: file-info io-backend ( path -- info )
+HOOK: link-info io-backend ( path -- info )
SYMBOL: +regular-file+
SYMBOL: +directory+
USING: arrays io io.files kernel math parser strings system
-tools.test words namespaces io.encodings.ascii io.encodings.binary ;
+tools.test words namespaces io.encodings.latin1
+io.encodings.binary ;
IN: io.tests
[ f ] [
] unit-test
: <resource-reader> ( resource -- stream )
- resource-path binary <file-reader> ;
+ resource-path latin1 <file-reader> ;
[
"This is a line.\rThis is another line.\r"
! [ ] [ "123" write 9000 CHAR: x <string> write flush ] unit-test
-[ "" ] [
+[
"/core/io/test/binary.txt" <resource-reader>
[ 0.2 read ] with-stream
-] unit-test
+] must-fail
[
{
-USING: tools.test io.files io io.streams.c io.encodings.ascii ;
+USING: tools.test io.files io io.streams.c
+io.encodings.ascii strings ;
IN: io.streams.c.tests
[ "hello world" ] [
] with-file-writer
"test.txt" temp-file "rb" fopen <c-reader> contents
+ >string
] unit-test
! Copyright (C) 2004, 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: kernel kernel.private namespaces io io.encodings
-strings sequences math generic threads.private classes
-io.backend io.streams.duplex io.files continuations
-io.encodings.utf8 ;
+sequences math generic threads.private classes io.backend
+io.streams.duplex io.files continuations byte-arrays ;
IN: io.streams.c
TUPLE: c-writer handle ;
C: <c-writer> c-writer
M: c-writer stream-write1
- >r 1string r> stream-write ;
+ c-writer-handle fputc ;
M: c-writer stream-write
- >r >string r> c-writer-handle fwrite ;
+ c-writer-handle fwrite ;
M: c-writer stream-flush
c-writer-handle fflush ;
C: <c-reader> c-reader
M: c-reader stream-read
- >r >fixnum r> c-reader-handle fread ;
+ c-reader-handle fread ;
M: c-reader stream-read-partial
stream-read ;
] if ;
M: c-reader stream-read-until
- [ swap read-until-loop ] "" make swap
+ [ swap read-until-loop ] B{ } make swap
over empty? over not and [ 2drop f f ] when ;
M: c-reader dispose
#! print stuff from contexts where the I/O system would
#! otherwise not work (tools.deploy.shaker, the I/O
#! multiplexer thread).
- "\r\n" append stdout-handle fwrite stdout-handle fflush ;
+ "\r\n" append >byte-array
+ stdout-handle fwrite
+ stdout-handle fflush ;
HELP: mirror
{ $class-description "An associative structure which wraps an object and presents itself as a mapping from slot names to the object's slot values. Mirrors are used to build reflective developer tools."
$nl
-"Mirrors are mutable, however new keys cannot be inserted and keys cannot be deleted, only values of existing keys can be changed."
+"Mirrors are mutable, however new keys cannot be inserted, only values of existing keys can be changed. Deleting a key has the effect of setting its value to " { $link f } "."
$nl
"Mirrors are created by calling " { $link <mirror> } " or " { $link make-mirror } "." } ;
"TUPLE: circle center radius ;"
"C: <circle> circle"
"{ 100 50 } 15 <circle> <mirror> >alist ."
- "{ { circle-center { 100 50 } } { circle-radius 15 } }"
+ "{ { \"center\" { 100 50 } } { \"radius\" 15 } }"
}
} ;
"Enumerations are mutable; note that deleting a key calls " { $link delete-nth } ", which results in all subsequent elements being shifted down." } ;
HELP: make-mirror
-{ $values { "obj" object } { "assoc" "an assoc" } }
+{ $values { "obj" object } { "assoc" assoc } }
{ $description "Creates an assoc which reflects the internal structure of the object." } ;
C: <foo> foo
-[ { foo-bar foo-baz } ] [ 1 2 <foo> <mirror> keys ] unit-test
+[ { "bar" "baz" } ] [ 1 2 <foo> <mirror> keys ] unit-test
-[ 1 t ] [ \ foo-bar 1 2 <foo> <mirror> at* ] unit-test
+[ 1 t ] [ "bar" 1 2 <foo> <mirror> at* ] unit-test
[ f f ] [ "hi" 1 2 <foo> <mirror> at* ] unit-test
[ 3 ] [
- 3 \ foo-baz 1 2 <foo> [ <mirror> set-at ] keep foo-baz
+ 3 "baz" 1 2 <foo> [ <mirror> set-at ] keep foo-baz
] unit-test
: >mirror< ( mirror -- obj slots )
dup mirror-object swap mirror-slots ;
+: mirror@ ( slot-name mirror -- obj slot-spec )
+ >mirror< swapd slot-named ;
+
M: mirror at*
- >mirror< swapd slot-of-reader
- dup [ slot-spec-offset slot t ] [ 2drop f f ] if ;
+ mirror@ dup [ slot-spec-offset slot t ] [ 2drop f f ] if ;
M: mirror set-at ( val key mirror -- )
- >mirror< swapd slot-of-reader dup [
+ mirror@ dup [
dup slot-spec-writer [
slot-spec-offset set-slot
] [
M: mirror >alist ( mirror -- alist )
>mirror<
[ [ slot-spec-offset slot ] with map ] keep
- [ slot-spec-reader ] map swap 2array flip ;
+ [ slot-spec-name ] map swap 2array flip ;
M: mirror assoc-size mirror-slots length ;
: slot-of-writer ( writer specs -- spec/f )
[ slot-spec-writer eq? ] with find nip ;
+
+: slot-named ( string specs -- spec/f )
+ [ slot-spec-name = ] with find nip ;
-USING: math tools.test system prettyprint ;
+USING: math tools.test system prettyprint namespaces kernel ;
IN: system.tests
[ t ] [ cell integer? ] unit-test
[ t ] [ bootstrap-cell integer? ] unit-test
-[ ] [ os-envs . ] unit-test
+
+wince? [
+ [ ] [ os-envs . ] unit-test
+] unless
+
+unix? [
+ [ ] [ os-envs "envs" set ] unit-test
+ [ ] [ { { "A" "B" } } set-os-envs ] unit-test
+ [ "B" ] [ "A" os-env ] unit-test
+ [ ] [ "envs" get set-os-envs ] unit-test
+ [ t ] [ os-envs "envs" get = ] unit-test
+] when
! See http://factorcode.org/license.txt for BSD license.
IN: system
USING: kernel kernel.private sequences math namespaces
-splitting assocs ;
+splitting assocs system.private ;
: cell ( -- n ) 7 getenv ; foldable
: os-envs ( -- assoc )
(os-envs) [ "=" split1 ] H{ } map>assoc ;
+
+: set-os-envs ( assoc -- )
+ [ "=" swap 3append ] { } assoc>map (set-os-envs) ;
[ { + } ] [ \ quot-uses-b uses ] unit-test
-[ "IN: words.tests FORGET: undef-test : undef-test ; << undef-test >>" eval ]
+"undef-test" "words.tests" lookup [
+ [ forget ] with-compilation-unit
+] when*
+
+[ "IN: words.tests : undef-test ; << undef-test >>" eval ]
[ [ undefined? ] is? ] must-fail-with
[ ] [
: later ( quot dt -- alarm )
from-now f add-alarm ;
+: every ( quot dt -- alarm )
+ [ from-now ] keep add-alarm ;
+
: cancel-alarm ( alarm -- )
alarm-entry [ alarms get-global heap-delete ] if-box? ;
USING: kernel namespaces sequences splitting system combinators continuations
parser io io.files io.launcher io.sockets prettyprint threads
bootstrap.image benchmark vars bake smtp builder.util accessors
+ io.encodings.utf8
calendar
builder.common
builder.benchmark
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: git-id ( -- id )
- { "git" "show" } <process-stream> [ readln ] with-stream " " split second ;
+ { "git" "show" } utf8 <process-stream>
+ [ readln ] with-stream " " split second ;
-: record-git-id ( -- ) git-id "../git-id" [ . ] with-file-writer ;
+: record-git-id ( -- ) git-id "../git-id" utf8 [ . ] with-file-writer ;
-: do-make-clean ( -- desc ) { "make" "clean" } try-process ;
+: do-make-clean ( -- ) { "make" "clean" } try-process ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: make-vm ( -- desc )
- <process*>
- { "make" } >>arguments
+ <process>
+ { "make" } >>command
"../compile-log" >>stdout
- +stdout+ >>stderr
- >desc ;
+ +stdout+ >>stderr ;
: do-make-vm ( -- )
make-vm [ "vm compile error" print "../compile-log" cat ] run-or-bail ;
{ "./factor" { "-i=" my-boot-image-name } "-no-user-init" } to-strings ;
: bootstrap ( -- desc )
- <process*>
- bootstrap-cmd >>arguments
+ <process>
+ bootstrap-cmd >>command
+closed+ >>stdin
"../boot-log" >>stdout
+stdout+ >>stderr
- 20 minutes >>timeout
- >desc ;
+ 20 minutes >>timeout ;
: do-bootstrap ( -- )
bootstrap [ "Bootstrap error" print "../boot-log" cat ] run-or-bail ;
{ "./factor" "-run=builder.test" } to-strings ;
: builder-test ( -- desc )
- <process*>
- builder-test-cmd >>arguments
+ <process>
+ builder-test-cmd >>command
+closed+ >>stdin
"../test-log" >>stdout
+stdout+ >>stderr
- 45 minutes >>timeout
- >desc ;
+ 45 minutes >>timeout ;
: do-builder-test ( -- )
builder-test [ "Test error" print "../test-log" 100 cat-n ] run-or-bail ;
enter-build-dir
- "report"
+ "report" utf8
[
"Build machine: " write host-name print
"CPU: " write cpu print
{
"boot.x86.32.image"
"boot.x86.64.image"
- "boot.macosx-ppc.boot"
+ "boot.macosx-ppc.image"
"vm"
"temp"
"logs"
prettyprint
tools.browser
tools.test
+ io.encodings.utf8
bootstrap.stage2 benchmark builder.util ;
IN: builder.test
: do-load ( -- )
- try-everything keys "../load-everything-vocabs" [ . ] with-file-writer ;
+ try-everything keys "../load-everything-vocabs" utf8 [ . ] with-file-writer ;
: do-tests ( -- )
- run-all-tests keys "../test-all-vocabs" [ . ] with-file-writer ;
+ run-all-tests keys "../test-all-vocabs" utf8 [ . ] with-file-writer ;
-: do-benchmarks ( -- ) run-benchmarks "../benchmarks" [ . ] with-file-writer ;
+: do-benchmarks ( -- )
+ run-benchmarks "../benchmarks" utf8 [ . ] with-file-writer ;
: do-all ( -- )
- bootstrap-time get "../boot-time" [ . ] with-file-writer
- [ do-load ] runtime "../load-time" [ . ] with-file-writer
- [ do-tests ] runtime "../test-time" [ . ] with-file-writer
+ bootstrap-time get "../boot-time" utf8 [ . ] with-file-writer
+ [ do-load ] runtime "../load-time" utf8 [ . ] with-file-writer
+ [ do-tests ] runtime "../test-time" utf8 [ . ] with-file-writer
do-benchmarks ;
MAIN: do-all
\ No newline at end of file
math math.parser
combinators sequences splitting quotations arrays strings tools.time
sequences.deep new-slots accessors assocs.lib
+ io.encodings.utf8
combinators.cleave bake calendar calendar.format ;
IN: builder.util
: minutes>ms ( min -- ms ) 60 * 1000 * ;
-: file>string ( file -- string ) [ stdio get contents ] with-file-reader ;
+: file>string ( file -- string ) utf8 [ stdio get contents ] with-file-reader ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-TUPLE: process* arguments stdin stdout stderr timeout ;
+! TUPLE: process* arguments stdin stdout stderr timeout ;
-: <process*> process* construct-empty ;
+! : <process*> process* construct-empty ;
-: >desc ( process* -- desc )
- H{ } clone
- over arguments>> [ +arguments+ swap put-at ] when*
- over stdin>> [ +stdin+ swap put-at ] when*
- over stdout>> [ +stdout+ swap put-at ] when*
- over stderr>> [ +stderr+ swap put-at ] when*
- over timeout>> [ +timeout+ swap put-at ] when*
- nip ;
+! : >desc ( process* -- desc )
+! H{ } clone
+! over arguments>> [ +arguments+ swap put-at ] when*
+! over stdin>> [ +stdin+ swap put-at ] when*
+! over stdout>> [ +stdout+ swap put-at ] when*
+! over stderr>> [ +stderr+ swap put-at ] when*
+! over timeout>> [ +timeout+ swap put-at ] when*
+! nip ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: milli-seconds>time ( n -- string )
1000 /i 60 /mod >r 60 /mod r> 3array [ pad-00 ] map ":" join ;
-: eval-file ( file -- obj ) file-contents eval ;
+: eval-file ( file -- obj ) utf8 file-contents eval ;
-: cat ( file -- ) file-contents print ;
+: cat ( file -- ) utf8 file-contents print ;
: run-or-bail ( desc quot -- )
[ [ try-process ] curry ]
if ;
: cat-n ( file n -- )
- [ file-lines ] [ ] bi*
+ [ utf8 file-lines ] [ ] bi*
maybe-tail*
[ print ] each ;
USE: prettyprint
-: to-file ( object file -- ) [ . ] with-file-writer ;
+: to-file ( object file -- ) utf8 [ . ] with-file-writer ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
\r
: pad-00 number>string 2 CHAR: 0 pad-left ;\r
\r
+: pad-0000 number>string 4 CHAR: 0 pad-left ;\r
+\r
: write-00 pad-00 write ;\r
\r
+: write-0000 pad-0000 write ;\r
+\r
: (timestamp>string) ( timestamp -- )\r
dup day-of-week day-abbreviations3 nth write ", " write\r
dup day>> number>string write bl\r
60 / + *\r
] if ;\r
\r
+: read-ymd ( -- y m d )\r
+ read-0000 "-" expect read-00 "-" expect read-00 ;\r
+\r
+: read-hms ( -- h m s )\r
+ read-00 ":" expect read-00 ":" expect read-00 ;\r
+\r
: (rfc3339>timestamp) ( -- timestamp )\r
- read-0000 ! year\r
- "-" expect\r
- read-00 ! month\r
- "-" expect\r
- read-00 ! day\r
+ read-ymd\r
"Tt" expect\r
- read-00 ! hour\r
- ":" expect\r
- read-00 ! minute\r
- ":" expect\r
- read-00 ! second\r
+ read-hms\r
read-rfc3339-gmt-offset ! timezone\r
<timestamp> ;\r
\r
: rfc3339>timestamp ( str -- timestamp )\r
[ (rfc3339>timestamp) ] with-string-reader ;\r
\r
+: (ymdhms>timestamp) ( -- timestamp )\r
+ read-ymd " " expect read-hms 0 <timestamp> ;\r
+\r
+: ymdhms>timestamp ( str -- timestamp )\r
+ [ (ymdhms>timestamp) ] with-string-reader ;\r
+\r
+: (hms>timestamp) ( -- timestamp )\r
+ f f f read-hms f <timestamp> ;\r
+\r
+: hms>timestamp ( str -- timestamp )\r
+ [ (hms>timestamp) ] with-string-reader ;\r
+\r
+: (ymd>timestamp) ( -- timestamp )\r
+ read-ymd f f f f <timestamp> ;\r
+\r
+: ymd>timestamp ( str -- timestamp )\r
+ [ (ymd>timestamp) ] with-string-reader ;\r
+\r
+: (timestamp>ymd) ( timestamp -- )\r
+ dup timestamp-year write-0000\r
+ "-" write\r
+ dup timestamp-month write-00\r
+ "-" write\r
+ timestamp-day write-00 ;\r
+\r
+: timestamp>ymd ( timestamp -- str )\r
+ [ (timestamp>ymd) ] with-string-writer ;\r
+\r
+: (timestamp>hms)\r
+ dup timestamp-hour write-00\r
+ ":" write\r
+ dup timestamp-minute write-00\r
+ ":" write\r
+ timestamp-second >integer write-00 ;\r
+\r
+: timestamp>hms ( timestamp -- str )\r
+ [ (timestamp>hms) ] with-string-writer ;\r
+\r
+: timestamp>ymdhms ( timestamp -- str )\r
+ >gmt\r
+ [\r
+ dup (timestamp>ymd)\r
+ " " write\r
+ (timestamp>hms)\r
+ ] with-string-writer ;\r
+\r
: file-time-string ( timestamp -- string )\r
[\r
[ month>> month-abbreviations nth write ] keep bl\r
TUPLE: statement handle sql in-params out-params bind-params bound? ;
TUPLE: simple-statement ;
TUPLE: prepared-statement ;
-TUPLE: result-set sql params handle n max ;
+TUPLE: result-set sql in-params out-params handle n max ;
: <statement> ( sql in out -- statement )
{ (>>sql) (>>in-params) (>>out-params) } statement construct ;
GENERIC: #rows ( result-set -- n )
GENERIC: #columns ( result-set -- n )
GENERIC# row-column 1 ( result-set n -- obj )
+GENERIC# row-column-typed 1 ( result-set n -- sql )
GENERIC: advance-row ( result-set -- )
GENERIC: more-rows? ( result-set -- ? )
0 >>n drop ;
: <result-set> ( query handle tuple -- result-set )
- >r >r { sql>> in-params>> } get-slots r>
- { (>>sql) (>>params) (>>handle) } result-set
+ >r >r { sql>> in-params>> out-params>> } get-slots r>
+ { (>>sql) (>>in-params) (>>out-params) (>>handle) } result-set
construct r> construct-delegate ;
: sql-row ( result-set -- seq )
dup #columns [ row-column ] with map ;
+: sql-row-typed ( result-set -- seq )
+ dup #columns [ row-column-typed ] with map ;
+
: query-each ( statement quot -- )
over more-rows? [
[ call ] 2keep over advance-row query-each
! See http://factorcode.org/license.txt for BSD license.
USING: alien.c-types arrays assocs kernel math math.parser
namespaces sequences db.sqlite.ffi db combinators
-continuations db.types ;
+continuations db.types calendar.format serialize
+io.streams.string byte-arrays ;
+USE: tools.walker
IN: db.sqlite.lib
: sqlite-error ( n -- * )
: sqlite-bind-null ( handle i -- )
sqlite3_bind_null sqlite-check-result ;
+: sqlite-bind-blob ( handle i byte-array -- )
+ dup length SQLITE_TRANSIENT
+ sqlite3_bind_blob sqlite-check-result ;
+
: sqlite-bind-text-by-name ( handle name text -- )
parameter-index sqlite-bind-text ;
: sqlite-bind-double-by-name ( handle name double -- )
parameter-index sqlite-bind-double ;
+: sqlite-bind-blob-by-name ( handle name blob -- )
+ parameter-index sqlite-bind-blob ;
+
: sqlite-bind-null-by-name ( handle name obj -- )
parameter-index drop sqlite-bind-null ;
: sqlite-bind-type ( handle key value type -- )
+ over [ drop NULL ] unless
dup array? [ first ] when
{
{ INTEGER [ sqlite-bind-int-by-name ] }
- { BIG_INTEGER [ sqlite-bind-int64-by-name ] }
+ { BIG-INTEGER [ sqlite-bind-int64-by-name ] }
{ TEXT [ sqlite-bind-text-by-name ] }
{ VARCHAR [ sqlite-bind-text-by-name ] }
{ DOUBLE [ sqlite-bind-double-by-name ] }
- { TIMESTAMP [ sqlite-bind-double-by-name ] }
+ { DATE [ sqlite-bind-text-by-name ] }
+ { TIME [ sqlite-bind-text-by-name ] }
+ { DATETIME [ sqlite-bind-text-by-name ] }
+ { TIMESTAMP [ sqlite-bind-text-by-name ] }
+ { BLOB [ sqlite-bind-blob-by-name ] }
+ { FACTOR-BLOB [
+ [ serialize ] with-string-writer >byte-array
+ sqlite-bind-blob-by-name
+ ] }
{ +native-id+ [ sqlite-bind-int-by-name ] }
- ! { NULL [ sqlite-bind-null-by-name ] }
+ { NULL [ sqlite-bind-null-by-name ] }
[ no-sql-type ]
} case ;
: sqlite-#columns ( query -- int )
sqlite3_column_count ;
-! TODO
: sqlite-column ( handle index -- string )
sqlite3_column_text ;
+: sqlite-column-blob ( handle index -- byte-array/f )
+ [ sqlite3_column_bytes ] 2keep
+ pick zero? [
+ 3drop f
+ ] [
+ sqlite3_column_blob swap memory>byte-array
+ ] if ;
+
: sqlite-column-typed ( handle index type -- obj )
+ dup array? [ first ] when
{
+ { +native-id+ [ sqlite3_column_int64 ] }
{ INTEGER [ sqlite3_column_int ] }
- { BIG_INTEGER [ sqlite3_column_int64 ] }
+ { BIG-INTEGER [ sqlite3_column_int64 ] }
{ TEXT [ sqlite3_column_text ] }
+ { VARCHAR [ sqlite3_column_text ] }
{ DOUBLE [ sqlite3_column_double ] }
- { TIMESTAMP [ sqlite3_column_double ] }
+ { DATE [ sqlite3_column_text dup [ ymd>timestamp ] when ] }
+ { TIME [ sqlite3_column_text dup [ hms>timestamp ] when ] }
+ { TIMESTAMP [ sqlite3_column_text dup [ ymdhms>timestamp ] when ] }
+ { DATETIME [ sqlite3_column_text dup [ ymdhms>timestamp ] when ] }
+ { BLOB [ sqlite-column-blob ] }
+ { FACTOR-BLOB [
+ sqlite-column-blob [ deserialize ] with-string-reader
+ ] }
+ ! { NULL [ 2drop f ] }
[ no-sql-type ]
} case ;
-! TODO
: sqlite-row ( handle -- seq )
dup sqlite-#columns [ sqlite-column ] with map ;
continuations db.types db.tuples unicode.case ;
IN: db.sqlite.tests
-: db-path "extra/db/sqlite/test.db" resource-path ;
+: db-path "test.db" temp-file ;
: test.db db-path sqlite-db ;
[ ] [ [ db-path delete-file ] ignore-errors ] unit-test
prettyprint sequences strings tuples alien.c-types
continuations db.sqlite.lib db.sqlite.ffi db.tuples
words combinators.lib db.types combinators tools.walker
-combinators.cleave io ;
+combinators.cleave io namespaces.lib ;
IN: db.sqlite
TUPLE: sqlite-db path ;
M: sqlite-result-set row-column ( result-set n -- obj )
>r result-set-handle r> sqlite-column ;
-M: sqlite-result-set row-column-typed ( result-set n type -- obj )
- >r result-set-handle r> sqlite-column-typed ;
+M: sqlite-result-set row-column-typed ( result-set n -- obj )
+ dup pick result-set-out-params nth sql-spec-type
+ >r >r result-set-handle r> r> sqlite-column-typed ;
M: sqlite-result-set advance-row ( result-set -- )
[ result-set-handle sqlite-next ] keep
" where " 0%
find-primary-key dup sql-spec-column-name 0% " = " 0% bind% ;
+: where-clause ( specs -- )
+ " where " 0%
+ [ " and " 0% ] [ dup sql-spec-column-name 0% " = " 0% bind% ] interleave ;
+
M: sqlite-db <update-tuple-statement> ( class -- statement )
[
"update " 0%
" from " 0% 0%
[ sql-spec-slot-name swap get-slot-named ] with subset
- dup empty? [
- drop
- ] [
- " where " 0%
- [ ", " 0% ]
- [ dup sql-spec-column-name 0% " = " 0% bind% ] interleave
- ] if
- ";" 0%
+ dup empty? [ drop ] [ where-clause ] if ";" 0%
] sqlite-make ;
M: sqlite-db modifier-table ( -- hashtable )
{ INTEGER "integer" }
{ TEXT "text" }
{ VARCHAR "text" }
+ { DATE "date" }
+ { TIME "time" }
+ { DATETIME "datetime" }
{ TIMESTAMP "timestamp" }
{ DOUBLE "real" }
+ { BLOB "blob" }
+ { FACTOR-BLOB "blob" }
} ;
M: sqlite-db create-type-table
! Copyright (C) 2008 Doug Coleman.
! See http://factorcode.org/license.txt for BSD license.
USING: io.files kernel tools.test db db.tuples
-db.types continuations namespaces db.postgresql math
-prettyprint tools.walker db.sqlite ;
+db.types continuations namespaces math
+prettyprint tools.walker db.sqlite calendar
+math.intervals ;
IN: db.tuples.tests
-TUPLE: person the-id the-name the-number the-real ;
-: <person> ( name age real -- person )
+TUPLE: person the-id the-name the-number the-real ts date time blob ;
+: <person> ( name age real ts date time blob -- person )
{
set-person-the-name
set-person-the-number
set-person-the-real
+ set-person-ts
+ set-person-date
+ set-person-time
+ set-person-blob
} person construct ;
-: <assigned-person> ( id name number the-real -- obj )
+: <assigned-person> ( id name age real ts date time blob -- person )
<person> [ set-person-the-id ] keep ;
-SYMBOL: the-person1
-SYMBOL: the-person2
+SYMBOL: person1
+SYMBOL: person2
+SYMBOL: person3
+SYMBOL: person4
: test-tuples ( -- )
[ person drop-table ] [ drop ] recover
[ ] [ person create-table ] unit-test
[ person create-table ] must-fail
- [ ] [ the-person1 get insert-tuple ] unit-test
+ [ ] [ person1 get insert-tuple ] unit-test
- [ 1 ] [ the-person1 get person-the-id ] unit-test
+ [ 1 ] [ person1 get person-the-id ] unit-test
- 200 the-person1 get set-person-the-number
+ 200 person1 get set-person-the-number
- [ ] [ the-person1 get update-tuple ] unit-test
+ [ ] [ person1 get update-tuple ] unit-test
[ T{ person f 1 "billy" 200 3.14 } ]
[ T{ person f 1 } select-tuple ] unit-test
- [ ] [ the-person2 get insert-tuple ] unit-test
+ [ ] [ person2 get insert-tuple ] unit-test
[
{
T{ person f 1 "billy" 200 3.14 }
}
] [ T{ person f } select-tuples ] unit-test
+ [
+ {
+ T{ person f 2 "johnny" 10 3.14 }
+ }
+ ] [ T{ person f f f 10 3.14 } select-tuples ] unit-test
- [ ] [ the-person1 get delete-tuple ] unit-test
+
+ [ ] [ person1 get delete-tuple ] unit-test
[ f ] [ T{ person f 1 } select-tuple ] unit-test
+
+ [ ] [ person3 get insert-tuple ] unit-test
+
+ [
+ T{
+ person
+ f
+ 3
+ "teddy"
+ 10
+ 3.14
+ T{ timestamp f 2008 3 5 16 24 11 0 }
+ T{ timestamp f 2008 11 22 f f f f }
+ T{ timestamp f f f f 12 34 56 f }
+ B{ 115 116 111 114 101 105 110 97 98 108 111 98 }
+ }
+ ] [ T{ person f 3 } select-tuple ] unit-test
+
[ ] [ person drop-table ] unit-test ;
: make-native-person-table ( -- )
{ "the-name" "NAME" { VARCHAR 256 } +not-null+ }
{ "the-number" "AGE" INTEGER { +default+ 0 } }
{ "the-real" "REAL" DOUBLE { +default+ 0.3 } }
+ { "ts" "TS" TIMESTAMP }
+ { "date" "D" DATE }
+ { "time" "T" TIME }
+ { "blob" "B" BLOB }
} define-persistent
- "billy" 10 3.14 <person> the-person1 set
- "johnny" 10 3.14 <person> the-person2 set ;
+ "billy" 10 3.14 f f f f <person> person1 set
+ "johnny" 10 3.14 f f f f <person> person2 set
+ "teddy" 10 3.14 "2008-03-05 16:24:11" "2008-11-22" "12:34:56" B{ 115 116 111 114 101 105 110 97 98 108 111 98 } <person> person3 set ;
: assigned-person-schema ( -- )
person "PERSON"
{ "the-name" "NAME" { VARCHAR 256 } +not-null+ }
{ "the-number" "AGE" INTEGER { +default+ 0 } }
{ "the-real" "REAL" DOUBLE { +default+ 0.3 } }
+ { "ts" "TS" TIMESTAMP }
+ { "date" "D" DATE }
+ { "time" "T" TIME }
+ { "blob" "B" BLOB }
} define-persistent
- 1 "billy" 10 3.14 <assigned-person> the-person1 set
- 2 "johnny" 10 3.14 <assigned-person> the-person2 set ;
-
+ 1 "billy" 10 3.14 f f f f <assigned-person> person1 set
+ 2 "johnny" 10 3.14 f f f f <assigned-person> person2 set
+ 3 "teddy" 10 3.14 "2008-03-05 16:24:11" "2008-11-22" "12:34:56" B{ 115 116 111 114 101 105 110 97 98 108 111 98 } <assigned-person> person3 set ;
TUPLE: paste n summary author channel mode contents timestamp annotations ;
TUPLE: annotation n paste-id summary author mode contents ;
! [ ] [ annotation create-table ] unit-test
! ] with-db
-
: test-sqlite ( quot -- )
- >r "tuples-test.db" resource-path sqlite-db r> with-db ;
+ >r "tuples-test.db" temp-file sqlite-db r> with-db ;
-: test-postgresql ( -- )
- >r { "localhost" "postgres" "" "factor-test" } postgresql-db r> with-db ;
+! : test-postgresql ( -- )
+! >r { "localhost" "postgres" "" "factor-test" } postgresql-db r> with-db ;
[ native-person-schema test-tuples ] test-sqlite
[ assigned-person-schema test-tuples ] test-sqlite
-! [ make-native-person-table ] test-sqlite
+TUPLE: serialize-me id data ;
+
+: test-serialize ( -- )
+ serialize-me "SERIALIZED"
+ {
+ { "id" "ID" +native-id+ }
+ { "data" "DATA" FACTOR-BLOB }
+ } define-persistent
+ [ serialize-me drop-table ] [ drop ] recover
+ [ ] [ serialize-me create-table ] unit-test
+
+ [ ] [ T{ serialize-me f f H{ { 1 2 } } } insert-tuple ] unit-test
+ [
+ { T{ serialize-me f 1 H{ { 1 2 } } } }
+ ] [ T{ serialize-me f 1 } select-tuples ] unit-test ;
+
+! [ test-serialize ] test-sqlite
+
+TUPLE: exam id name score ;
+
+: test-ranges ( -- )
+ exam "EXAM"
+ {
+ { "id" "ID" +native-id+ }
+ { "name" "NAME" TEXT }
+ { "score" "SCORE" INTEGER }
+ } define-persistent
+ [ exam drop-table ] [ drop ] recover
+ [ ] [ exam create-table ] unit-test
+
+ [ ] [ T{ exam f f "Kyle" 100 } insert-tuple ] unit-test
+ [ ] [ T{ exam f f "Stan" 80 } insert-tuple ] unit-test
+ [ ] [ T{ exam f f "Kenny" 60 } insert-tuple ] unit-test
+ [ ] [ T{ exam f f "Cartman" 41 } insert-tuple ] unit-test
+
+ [
+ T{ exam f 3 "Kenny" 60 }
+ T{ exam f 4 "Cartman" 41 }
+ ] [ T{ exam f 4 f T{ interval f { 0 t } { 70 t } } } select-tuples ] unit-test
+ ;
+
+! [ test-ranges ] test-sqlite
HOOK: <select-by-slots-statement> db ( tuple -- tuple )
-HOOK: row-column-typed db ( result-set n type -- sql )
HOOK: insert-tuple* db ( tuple statement -- )
: resulting-tuple ( row out-params -- tuple )
dup first sql-spec-class construct-empty [
[
- >r [ sql-spec-type sql-type>factor-type ] keep
- sql-spec-slot-name r> set-slot-named
+ >r sql-spec-slot-name r> set-slot-named
] curry 2each
] keep ;
: query-tuples ( statement -- seq )
[ statement-out-params ] keep query-results [
- [ sql-row swap resulting-tuple ] with query-map
+ [ sql-row-typed swap resulting-tuple ] with query-map
] with-disposal ;
: query-modify-tuple ( tuple statement -- )
- [ query-results [ sql-row ] with-disposal ] keep
+ [ query-results [ sql-row-typed ] with-disposal ] keep
statement-out-params rot [
- >r [ sql-spec-type sql-type>factor-type ] keep
- sql-spec-slot-name r> set-slot-named
+ >r sql-spec-slot-name r> set-slot-named
] curry 2each ;
: sql-props ( class -- columns table )
USING: arrays assocs db kernel math math.parser
sequences continuations sequences.deep sequences.lib
words namespaces tools.walker slots slots.private classes
-mirrors tuples combinators ;
+mirrors tuples combinators calendar.format serialize
+io.streams.string ;
IN: db.types
HOOK: modifier-table db ( -- hash )
: relation? ( spec -- ? ) [ +has-many+ = ] deep-find ;
SYMBOL: INTEGER
-SYMBOL: BIG_INTEGER
+SYMBOL: BIG-INTEGER
SYMBOL: DOUBLE
SYMBOL: REAL
SYMBOL: BOOLEAN
SYMBOL: TEXT
SYMBOL: VARCHAR
-SYMBOL: TIMESTAMP
SYMBOL: DATE
+SYMBOL: TIME
+SYMBOL: DATETIME
+SYMBOL: TIMESTAMP
+SYMBOL: BLOB
+SYMBOL: FACTOR-BLOB
+SYMBOL: NULL
: spec>tuple ( class spec -- tuple )
[ ?first3 ] keep 3 ?tail*
} sql-spec construct
dup normalize-spec ;
-: sql-type-hash ( -- assoc )
- H{
- { INTEGER "integer" }
- { TEXT "text" }
- { VARCHAR "varchar" }
- { DOUBLE "real" }
- { TIMESTAMP "timestamp" }
- } ;
-
TUPLE: no-sql-type ;
: no-sql-type ( -- * ) T{ no-sql-type } throw ;
[ lookup-modifier ] map " " join
dup empty? [ " " swap append ] unless ;
-SYMBOL: building-seq
-: get-building-seq ( n -- seq )
- building-seq get nth ;
-
-: n, get-building-seq push ;
-: n% get-building-seq push-all ;
-: n# >r number>string r> n% ;
-
-: 0, 0 n, ;
-: 0% 0 n% ;
-: 0# 0 n# ;
-: 1, 1 n, ;
-: 1% 1 n% ;
-: 1# 1 n# ;
-: 2, 2 n, ;
-: 2% 2 n% ;
-: 2# 2 n# ;
-
-: nmake ( quot exemplars -- seqs )
- dup length dup zero? [ 1+ ] when
- [
- [
- [ drop 1024 swap new-resizable ] 2map
- [ building-seq set call ] keep
- ] 2keep >r [ like ] 2map r> firstn
- ] with-scope ;
-
HOOK: bind% db ( spec -- )
TUPLE: no-slot-named ;
>r dup sql-spec-type swap sql-spec-slot-name r>
get-slot-named swap
] curry { } map>assoc ;
-
-: sql-type>factor-type ( obj type -- obj )
- dup array? [ first ] when
- {
- { +native-id+ [ string>number ] }
- { INTEGER [ string>number ] }
- { DOUBLE [ string>number ] }
- { REAL [ string>number ] }
- { TEXT [ ] }
- { VARCHAR [ ] }
- [ "no conversion from sql type to factor type" throw ]
- } case ;
-USING: help.markup help.syntax libc kernel ;
+USING: help.markup help.syntax libc kernel continuations ;
IN: destructors
HELP: free-always
HELP: with-destructors
{ $values { "quot" "a quotation" } }
-{ $description "Calls a quotation within a new dynamic scope. This quotation may register destructors, on any object, by wrapping the object in a destructor and implementing " { $link destruct } " on that object type. After the quotation finishes, if an error was thrown, all destructors are called and the error is then rethrown. However, if the quotation was successful, only those destructors created with an 'always cleanup' flag will be destroyed." }
+{ $description "Calls a quotation within a new dynamic scope. This quotation may register destructors, on any object, by wrapping the object in a destructor and implementing " { $link dispose } " on that object type. After the quotation finishes, if an error was thrown, all destructors are called and the error is then rethrown. However, if the quotation was successful, only those destructors created with an 'always cleanup' flag will be destroyed." }
{ $notes "Destructors are not allowed to throw exceptions. No exceptions." }
{ $examples
{ $code "[ 10 malloc free-always ] with-destructors" }
C: <dummy-destructor> dummy-destructor
-M: dummy-destructor destruct ( obj -- )
+M: dummy-destructor dispose ( obj -- )
dummy-destructor-obj t swap set-dummy-obj-destroyed? ;
: destroy-always
sequences system vectors ;
IN: destructors
-GENERIC: destruct ( obj -- )
-
SYMBOL: error-destructors
SYMBOL: always-destructors
TUPLE: destructor object destroyed? ;
-M: destructor destruct
+M: destructor dispose
dup destructor-destroyed? [
drop
] [
- dup destructor-object destruct
+ dup destructor-object dispose
t swap set-destructor-destroyed?
] if ;
<destructor> always-destructors get push ;
: do-always-destructors ( -- )
- always-destructors get [ destruct ] each ;
+ always-destructors get [ dispose ] each ;
: do-error-destructors ( -- )
- error-destructors get [ destruct ] each ;
+ error-destructors get [ dispose ] each ;
: with-destructors ( quot -- )
[
C: <memory-destructor> memory-destructor
-M: memory-destructor destruct ( obj -- )
+M: memory-destructor dispose ( obj -- )
memory-destructor-alien free ;
: free-always ( alien -- )
HOOK: destruct-handle io-backend ( obj -- )
-M: handle-destructor destruct ( obj -- )
+M: handle-destructor dispose ( obj -- )
handle-destructor-alien destruct-handle ;
: close-always ( handle -- )
HOOK: destruct-socket io-backend ( obj -- )
-M: socket-destructor destruct ( obj -- )
+M: socket-destructor dispose ( obj -- )
socket-destructor-alien destruct-socket ;
: close-socket-always ( handle -- )
-! Copyright (C) 2004, 2007 Slava Pestov.
+! Copyright (C) 2004, 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: arrays definitions io kernel math
namespaces parser prettyprint sequences strings words
-editors io.files io.sockets io.streams.string io.binary
-math.parser io.encodings.ascii ;
+editors io.files io.sockets io.streams.byte-array io.binary
+math.parser io.encodings.ascii io.encodings.binary
+io.encodings.utf8 ;
IN: editors.jedit
: jedit-server-info ( -- port auth )
] with-file-reader ;
: make-jedit-request ( files -- code )
- [
+ utf8 [
"EditServer.handleClient(false,false,false," write
cwd pprint
"," write
"new String[] {" write
[ pprint "," write ] each
"null});\n" write
- ] with-string-writer ;
+ ] with-byte-writer ;
: send-jedit-request ( request -- )
- jedit-server-info swap "localhost" swap <inet> <client> [
+ jedit-server-info "localhost" rot <inet> binary <client> [
4 >be write
dup length 2 >be write
write
[ "<p><strong>foo</strong>\n</p><h1>aheading</h1>\n<p>adfasd</p>" ]
[ "*foo*\n=aheading=\nadfasd" convert-farkup ] unit-test
+
+[ "<p>=foo\n</p>" ] [ "=foo\n" convert-farkup ] unit-test
+[ "<h1>foo</h1>\n" ] [ "=foo=\n" convert-farkup ] unit-test
+[ "<p>lol</p><h1>foo</h1>\n" ] [ "lol=foo=\n" convert-farkup ] unit-test
--- /dev/null
+USING: help.markup help.syntax quotations kernel ;\r
+IN: fry\r
+\r
+HELP: ,\r
+{ $description "Fry specifier. Inserts a literal value into the fried quotation." } ;\r
+\r
+HELP: @\r
+{ $description "Fry specifier. Splices a quotation into the fried quotation." } ;\r
+\r
+HELP: _\r
+{ $description "Fry specifier. Shifts all fry specifiers to the left down by one stack position." } ;\r
+\r
+HELP: fry\r
+{ $values { "quot" quotation } { "quot'" quotation } }\r
+{ $description "Outputs a quotation that when called, fries " { $snippet "quot" } " by taking values from the stack and substituting them in." }\r
+{ $notes "This word is used to implement " { $link POSTPONE: '[ } "; the following two lines are equivalent:"\r
+ { $code "[ X ] fry call" "'[ X ]" }\r
+} ;\r
+\r
+HELP: '[\r
+{ $syntax "code... ]" }\r
+{ $description "Literal fried quotation. Expands into code which takes values from the stack and substituting them in." } ;\r
+\r
+ARTICLE: "fry.examples" "Examples of fried quotations"\r
+"Conceptually, " { $link fry } " is tricky however the general idea is easy to grasp once presented with examples."\r
+$nl\r
+"If a quotation does not contain any fry specifiers, then " { $link POSTPONE: '[ } " behaves just like " { $link POSTPONE: [ } ":"\r
+{ $code "{ 10 20 30 } '[ . ] each" }\r
+"Occurrences of " { $link , } " on the left map directly to " { $link curry } ". That is, the following three lines are equivalent:"\r
+{ $code \r
+ "{ 10 20 30 } 5 '[ , + ] map"\r
+ "{ 10 20 30 } 5 [ + ] curry map"\r
+ "{ 10 20 30 } [ 5 + ] map"\r
+}\r
+"Occurrences of " { $link , } " in the middle of a quotation map to more complex quotation composition patterns. The following three lines are equivalent:"\r
+{ $code \r
+ "{ 10 20 30 } 5 '[ 3 , / ] map"\r
+ "{ 10 20 30 } 5 [ 3 ] swap [ / ] curry compose map"\r
+ "{ 10 20 30 } [ 3 5 / ] map"\r
+}\r
+"Occurrences of " { $link @ } " are simply syntax sugar for " { $snippet ", call" } ". The following three lines are equivalent:"\r
+{ $code \r
+ "{ 10 20 30 } [ sq ] '[ @ . ] map"\r
+ "{ 10 20 30 } [ sq ] [ . ] compose map"\r
+ "{ 10 20 30 } [ sq . ] map"\r
+}\r
+"The " { $link , } " and " { $link @ } " specifiers may be freely mixed:"\r
+{ $code\r
+ "{ 8 13 14 27 } [ even? ] 5 [ @ dup , ? ] map"\r
+ "{ 8 13 14 27 } [ even? ] 5 [ dup ] swap [ ? ] curry 3compose map"\r
+ "{ 8 13 14 27 } [ even? dup 5 ? ] map"\r
+}\r
+"Occurrences of " { $link _ } " have the effect of enclosing all code to their left with " { $link >r } " and " { $link r> } ":"\r
+{ $code \r
+ "{ 10 20 30 } 1 '[ , _ / ] map"\r
+ "{ 10 20 30 } 1 [ swap / ] curry map"\r
+ "{ 10 20 30 } [ 1 swap / ] map"\r
+}\r
+"For any quotation body " { $snippet "X" } ", the following two are equivalent:"\r
+{ $code\r
+ "[ >r X r> ]"\r
+ "[ X _ ]"\r
+}\r
+"Here are some built-in combinators rewritten in terms of fried quotations:"\r
+{ $table\r
+ { { $link literalize } { $snippet ": literalize '[ , ] ;" } }\r
+ { { $link slip } { $snippet ": slip '[ @ , ] call ;" } }\r
+ { { $link dip } { $snippet ": dip '[ @ _ ] call ;" } }\r
+ { { $link curry } { $snippet ": curry '[ , @ ] ;" } }\r
+ { { $link with } { $snippet ": with swapd '[ , _ @ ] ;" } }\r
+ { { $link compose } { $snippet ": compose '[ @ @ ] ;" } }\r
+ { { $link 2apply } { $snippet ": 2apply tuck '[ , @ , @ ] call ;" } }\r
+} ;\r
+\r
+ARTICLE: "fry.philosophy" "Fried quotation philosophy"\r
+"Fried quotations generalize quotation-building words such as " { $link curry } " and " { $link compose } "."\r
+$nl\r
+"There is a mapping from fried quotations to lexical closures as defined in the " { $vocab-link "locals" } " vocabulary. Namely, a fried quotation is equivalent to a ``let'' form where each local binding is only used once, and bindings are used in the same order in which they are defined. The following two lines are equivalent:"\r
+{ $code\r
+ "'[ 3 , + 4 , / ]"\r
+ "[let | a [ ] b [ ] | [ 3 a + 4 b / ] ]"\r
+}\r
+"The " { $link _ } " fry specifier has no direct analogue in " { $vocab-link "locals" } ", however closure conversion together with the " { $link dip } " combinator achieve the same effect:"\r
+{ $code\r
+ "'[ , 2 + , * _ / ]"\r
+ "[let | a [ ] b [ ] | [ [ a 2 + b * ] dip / ] ]"\r
+} ;\r
+\r
+ARTICLE: "fry.limitations" "Fried quotation limitations"\r
+"As with " { $link "locals" } ", fried quotations cannot contain " { $link >r } " and " { $link r> } ". Unlike " { $link "locals" } ", using " { $link dip } " is not a suitable workaround since unlike closure conversion, fry conversion is not recursive, and so the quotation passed to " { $link dip } " cannot contain fry specifiers." ;\r
+\r
+ARTICLE: "fry" "Fried quotations"\r
+"A " { $emphasis "fried quotation" } " differs from a literal quotation in that when it is evaluated, instead of just pushing itself on the stack, it consumes zero or more stack values and inserts them into the quotation."\r
+$nl\r
+"Fried quotations are denoted with a special parsing word:"\r
+{ $subsection POSTPONE: '[ }\r
+"Fried quotations contain zero or more " { $emphasis "fry specifiers" } ":"\r
+{ $subsection , }\r
+{ $subsection @ }\r
+{ $subsection _ }\r
+"When a fried quotation is being evaluated, values are consumed from the stack and spliced into the quotation from right to left."\r
+{ $subsection "fry.examples" }\r
+{ $subsection "fry.philosophy" }\r
+{ $subsection "fry.limitations" }\r
+"Quotations can also be fried without using a parsing word:"\r
+{ $subsection fry } ;\r
+\r
+ABOUT: "fry"\r
+++ /dev/null
-Slava Pestov
-Doug Coleman
+++ /dev/null
-USING: kernel sequences namespaces math tools.test furnace furnace.validator ;
-IN: furnace.tests
-
-TUPLE: test-tuple m n ;
-
-[ H{ { "m" 3 } { "n" 2 } } ]
-[
- [ T{ test-tuple f 3 2 } explode-tuple ] H{ } make-assoc
-] unit-test
-
-[
- { 3 }
-] [
- H{ { "n" "3" } } { { "n" v-number } }
- [ action-param drop ] with map
-] unit-test
-
-: foo ;
-
-\ foo { { "foo" "2" v-default } { "bar" v-required } } define-action
-
-[ t ] [ [ 1 2 foo ] action-call? ] unit-test
-[ f ] [ [ 2 + ] action-call? ] unit-test
-
-[
- { "2" "hello" }
-] [
- [
- H{
- { "bar" "hello" }
- } \ foo query>seq
- ] with-scope
-] unit-test
-
-[
- H{ { "foo" "1" } { "bar" "2" } }
-] [
- { "1" "2" } \ foo quot>query
-] unit-test
-
-[
- "/responder/furnace.tests/foo?foo=3"
-] [
- [
- [ "3" foo ] quot-link
- ] with-scope
-] unit-test
+++ /dev/null
-! Copyright (C) 2006, 2008 Slava Pestov, Doug Coleman
-! See http://factorcode.org/license.txt for BSD license.
-USING: arrays assocs calendar debugger furnace.sessions
-furnace.validator hashtables heaps html.elements http
-http.server.responders http.server.templating io.files kernel
-math namespaces quotations sequences splitting words strings
-vectors webapps.callback continuations tuples classes vocabs
-html io io.encodings.binary ;
-IN: furnace
-
-: code>quotation ( word/quot -- quot )
- dup word? [ 1quotation ] when ;
-
-SYMBOL: default-action
-SYMBOL: template-path
-
-: render-template ( template -- )
- template-path get swap path+
- ".furnace" append resource-path
- run-template-file ;
-
-: define-action ( word hash -- )
- over t "action" set-word-prop
- "action-params" set-word-prop ;
-
-: define-form ( word1 word2 hash -- )
- dupd define-action
- swap code>quotation "form-failed" set-word-prop ;
-
-: default-values ( word hash -- )
- "default-values" set-word-prop ;
-
-SYMBOL: request-params
-SYMBOL: current-action
-SYMBOL: validators-errored
-SYMBOL: validation-errors
-
-: build-url ( str query-params -- newstr )
- [
- over %
- dup assoc-empty? [
- 2drop
- ] [
- CHAR: ? rot member? "&" "?" ? %
- assoc>query %
- ] if
- ] "" make ;
-
-: action-link ( query action -- url )
- [
- "/responder/" %
- dup word-vocabulary "webapps." ?head drop %
- "/" %
- word-name %
- ] "" make swap build-url ;
-
-: action-param ( hash paramsepc -- obj error/f )
- unclip rot at swap >quotation apply-validators ;
-
-: query>seq ( hash word -- seq )
- "action-params" word-prop [
- dup first -rot
- action-param [
- t validators-errored >session
- rot validation-errors session> set-at
- ] [
- nip
- ] if*
- ] with map ;
-
-: lookup-session ( hash -- session )
- "furnace-session-id" over at get-session
- [ ] [ new-session "furnace-session-id" roll set-at ] ?if ;
-
-: quot>query ( seq action -- hash )
- >r >array r> "action-params" word-prop
- [ first swap 2array ] 2map >hashtable ;
-
-PREDICATE: word action "action" word-prop ;
-
-: action-call? ( quot -- ? )
- >vector dup pop action? >r [ word? not ] all? r> and ;
-
-: unclip* dup 1 head* swap peek ;
-
-: quot-link ( quot -- url )
- dup action-call? [
- unclip* [ quot>query ] keep action-link
- ] [
- t register-html-callback
- ] if ;
-
-: replace-variables ( quot -- quot )
- [ dup string? [ request-params session> at ] when ] map ;
-
-: furnace-session-id ( -- hash )
- "furnace-session-id" request-params session> at
- "furnace-session-id" associate ;
-
-: redirect-to-action ( -- )
- current-action session>
- "form-failed" word-prop replace-variables
- quot-link furnace-session-id build-url permanent-redirect ;
-
-: if-form-page ( if then -- )
- current-action session> "form-failed" word-prop -rot if ;
-
-: do-action
- current-action session> [ query>seq ] keep add >quotation call ;
-
-: process-form ( -- )
- H{ } clone validation-errors >session
- request-params session> current-action session> query>seq
- validators-errored session> [
- drop redirect-to-action
- ] [
- current-action session> add >quotation call
- ] if ;
-
-: page-submitted ( -- )
- [ process-form ] [ request-params session> do-action ] if-form-page ;
-
-: action-first-time ( -- )
- request-params session> current-action session>
- [ "default-values" word-prop swap union request-params >session ] keep
- request-params session> do-action ;
-
-: page-not-submitted ( -- )
- [ redirect-to-action ] [ action-first-time ] if-form-page ;
-
-: setup-call-action ( hash word -- )
- over lookup-session session set
- current-action >session
- request-params session> swap union
- request-params >session
- f validators-errored >session ;
-
-: call-action ( hash word -- )
- setup-call-action
- "furnace-form-submitted" request-params session> at
- [ page-submitted ] [ page-not-submitted ] if ;
-
-: responder-vocab ( str -- newstr )
- "webapps." swap append ;
-
-: lookup-action ( str webapp -- word )
- responder-vocab lookup dup [
- dup "action" word-prop [ drop f ] unless
- ] when ;
-
-: truncate-url ( str -- newstr )
- CHAR: / over index [ head ] when* ;
-
-: parse-action ( str -- word/f )
- dup empty? [ drop default-action get ] when
- truncate-url "responder" get lookup-action ;
-
-: service-request ( hash str -- )
- parse-action [
- [ call-action ] [ <pre> print-error </pre> ] recover
- ] [
- "404 no such action: " "argument" get append httpd-error
- ] if* ;
-
-: service-get
- "query" get swap service-request ;
-
-: service-post
- "response" get swap service-request ;
-
-: web-app ( name defaul path -- )
- [
- template-path set
- default-action set
- "responder" set
- [ service-get ] "get" set
- [ service-post ] "post" set
- ] make-responder ;
-
-: explode-tuple ( tuple -- )
- dup tuple-slots swap class "slot-names" word-prop
- [ set ] 2each ;
-
-SYMBOL: model
-
-: with-slots ( model quot -- )
- [
- >r [ dup model set explode-tuple ] when* r> call
- ] with-scope ;
-
-: render-component ( model template -- )
- swap [ render-template ] with-slots ;
-
-: browse-webapp-source ( vocab -- )
- <a vocab browser-link-href =href a>
- "Browse source" write
- </a> ;
-
-: send-resource ( name -- )
- template-path get swap path+ resource-path binary <file-reader>
- stdio get stream-copy ;
-
-: render-link ( quot name -- )
- <a swap quot-link =href a> write </a> ;
-
-: session-var ( str -- newstr )
- request-params session> at ;
-
-: render ( str -- )
- request-params session> at [ write ] when* ;
-
-: render-error ( str error-str -- )
- swap validation-errors session> at validation-error? [
- write
- ] [
- drop
- ] if ;
+++ /dev/null
-Doug Coleman
+++ /dev/null
-USING: assocs calendar init kernel math.parser
-namespaces random boxes alarms combinators.lib ;
-IN: furnace.sessions
-
-SYMBOL: sessions
-
-: timeout ( -- dt ) 20 minutes ;
-
-[
- H{ } clone sessions set-global
-] "furnace.sessions" add-init-hook
-
-: new-session-id ( -- str )
- [ 4 big-random >hex ]
- [ sessions get-global key? not ] generate ;
-
-TUPLE: session id namespace alarm user-agent ;
-
-: cancel-timeout ( session -- )
- session-alarm ?box [ cancel-alarm ] [ drop ] if ;
-
-: delete-session ( session -- )
- sessions get-global delete-at*
- [ cancel-timeout ] [ drop ] if ;
-
-: touch-session ( session -- )
- dup cancel-timeout
- dup [ session-id delete-session ] curry timeout later
- swap session-alarm >box ;
-
-: <session> ( id -- session )
- H{ } clone <box> f session construct-boa ;
-
-: new-session ( -- session id )
- new-session-id [
- dup <session> [
- [ sessions get-global set-at ] keep
- touch-session
- ] keep
- ] keep ;
-
-: get-session ( id -- session/f )
- sessions get-global at*
- [ dup touch-session ] when ;
-
-: session> ( str -- obj )
- session get session-namespace at ;
-
-: >session ( value key -- )
- session get session-namespace set-at ;
+++ /dev/null
-Action-based web framework
+++ /dev/null
-enterprise
+++ /dev/null
-Doug Coleman
+++ /dev/null
-IN: furnace.validator.tests
-USING: kernel sequences tools.test furnace.validator furnace ;
-
-[
- 123 f
-] [
- H{ { "foo" "123" } } { "foo" v-number } action-param
-] unit-test
-
-: validation-fails
- [ action-param nip not ] append [ f ] swap unit-test ;
-
-[ H{ { "foo" "12X3" } } { "foo" v-number } ] validation-fails
-
-[ H{ { "foo" "" } } { "foo" 4 v-min-length } ] validation-fails
-
-[ "ABCD" f ]
-[ H{ { "foo" "ABCD" } } { "foo" 4 v-min-length } action-param ]
-unit-test
-
-[ H{ { "foo" "ABCD" } } { "foo" 2 v-max-length } ]
-validation-fails
-
-[ "AB" f ]
-[ H{ { "foo" "AB" } } { "foo" 2 v-max-length } action-param ]
-unit-test
-
-[ "AB" f ]
-[ H{ { "foo" f } } { "foo" "AB" v-default } action-param ]
-unit-test
+++ /dev/null
-! Copyright (C) 2006 Slava Pestov
-! See http://factorcode.org/license.txt for BSD license.
-USING: kernel continuations sequences math namespaces math.parser ;
-IN: furnace.validator
-
-TUPLE: validation-error reason ;
-
-: apply-validators ( string quot -- obj error/f )
- [
- call f
- ] [
- dup validation-error? [ >r 2drop f r> ] [ rethrow ] if
- ] recover ;
-
-: validation-error ( msg -- * )
- \ validation-error construct-boa throw ;
-
-: v-default ( obj value -- obj )
- over empty? [ nip ] [ drop ] if ;
-
-: v-required ( str -- str )
- dup empty? [ "required" validation-error ] when ;
-
-: v-min-length ( str n -- str )
- over length over < [
- [ "must be at least " % # " characters" % ] "" make
- validation-error
- ] [
- drop
- ] if ;
-
-: v-max-length ( str n -- str )
- over length over > [
- [ "must be no more than " % # " characters" % ] "" make
- validation-error
- ] [
- drop
- ] if ;
-
-: v-number ( str -- n )
- string>number [
- "must be a number" validation-error
- ] unless* ;
-! Copyright (C) 2005, 2007 Slava Pestov.
+! Copyright (C) 2005, 2008 Slava Pestov.
! See http://factorcode.org/license.txt for BSD license.
USING: arrays definitions generic io kernel assocs hashtables
namespaces parser prettyprint sequences strings io.styles
[ print-element ] with-style ;
: with-default-style ( quot -- )
- default-style get [
+ default-span-style get [
last-element off
- default-style get swap with-nesting
+ default-block-style get swap with-nesting
] with-style ; inline
: print-content ( element -- )
USING: io.styles namespaces ;
IN: help.stylesheet
-SYMBOL: default-style
+SYMBOL: default-span-style
H{
{ font "sans-serif" }
{ font-size 12 }
{ font-style plain }
+} default-span-style set-global
+
+SYMBOL: default-block-style
+H{
{ wrap-margin 500 }
-} default-style set-global
+} default-block-style set-global
SYMBOL: link-style
H{
USING: assocs html.parser kernel math sequences strings ascii
-arrays shuffle unicode.case namespaces splitting
-http.server.responders ;
+arrays shuffle unicode.case namespaces splitting http
+sequences.lib ;
IN: html.parser.analyzer
+: (find-relative)
+ [ >r + dup r> ?nth* [ 2drop f f ] unless ] [ 2drop f ] if ;
+
+: find-relative ( seq quot n -- i elt )
+ >r over [ find drop ] dip r> swap pick
+ (find-relative) ;
+
+: (find-all) ( n seq quot -- )
+ 2dup >r >r find* [
+ dupd 2array , 1+ r> r> (find-all)
+ ] [
+ r> r> 3drop
+ ] if* ;
+
+: find-all ( seq quot -- alist )
+ [ 0 -rot (find-all) ] { } make ;
+
+: (find-nth) ( offset seq quot n count -- obj )
+ >r >r [ find* ] 2keep 4 npick [
+ r> r> 1+ 2dup <= [
+ 4drop
+ ] [
+ >r >r >r >r drop 1+ r> r> r> r>
+ (find-nth)
+ ] if
+ ] [
+ 2drop r> r> 2drop
+ ] if ;
+
+: find-nth ( seq quot n -- i elt )
+ 0 -roll 0 (find-nth) ;
+
+: find-nth-relative ( seq quot n offest -- i elt )
+ >r [ find-nth ] 3keep 2drop nip r> swap pick
+ (find-relative) ;
+
: remove-blank-text ( vector -- vector' )
[
dup tag-name text = [
>r >lower r>
[ tag-attributes at over = ] with find rot drop ;
-: find-between ( i/f tag/f vector -- vector )
+: find-between* ( i/f tag/f vector -- vector )
pick integer? [
- rot 1+ tail-slice
+ rot tail-slice
>r tag-name r>
- [ find-matching-close drop ] keep swap head
+ [ find-matching-close drop 1+ ] keep swap head
] [
3drop V{ } clone
] if ;
+
+: find-between ( i/f tag/f vector -- vector )
+ find-between* dup length 3 >= [
+ [ 1 tail-slice 1 head-slice* ] keep like
+ ] when ;
+
+: find-between-first ( string vector -- vector' )
+ [ find-first-name ] keep find-between ;
+
+: tag-link ( tag -- link/f )
+ tag-attributes [ "href" swap at ] [ f ] if* ;
: find-links ( vector -- vector )
[ tag-name "a" = ] subset
- [ tag-attributes "href" swap at ] map
- [ ] subset ;
+ [ tag-link ] subset ;
-: (find-all) ( n seq quot -- )
- 2dup >r >r find* [
- dupd 2array , 1+ r> r> (find-all)
- ] [
- r> r> 3drop
- ] if* ;
-: find-all ( seq quot -- alist )
- [ 0 -rot (find-all) ] { } make ;
+: find-by-text ( seq quot -- tag )
+ [ dup tag-name text = ] swap compose find drop ;
: find-opening-tags-by-name ( name seq -- seq )
[ [ tag-name = ] keep tag-closing? not and ] with find-all ;
: href-contains? ( str tag -- ? )
tag-attributes "href" swap at* [ subseq? ] [ 2drop f ] if ;
-: query>hash* ( str -- hash )
- "?" split1 nip query>hash ;
+: query>assoc* ( str -- hash )
+ "?" split1 nip query>assoc ;
! clear "http://fark.com" http-get parse-html find-links [ "go.pl" swap start ] subset [ "=" split peek ] map
! "a" over find-opening-tags-by-name
! [ nip "shipposition.phtml?call=GBTT" swap href-contains? ] assoc-subset
! first first 8 + over nth
-! tag-attributes "href" swap at query>hash*
+! tag-attributes "href" swap at query>assoc*
! "lat" over at "lon" rot at
[
"http://www.apple.com/index.html"
<get-request>
- request-with-url
] with-scope
] unit-test
! See http://factorcode.org/license.txt for BSD license.
USING: assocs http kernel math math.parser namespaces sequences
io io.sockets io.streams.string io.files io.timeouts strings
-splitting calendar continuations accessors vectors io.encodings.binary ;
+splitting calendar continuations accessors vectors io.encodings.latin1
+io.encodings.binary ;
IN: http.client
+DEFER: http-request
+
+<PRIVATE
+
: parse-url ( url -- resource host port )
"http://" ?head [ "Only http:// supported" throw ] unless
"/" split1 [ "/" swap append ] [ "/" ] if*
swap parse-host ;
-<PRIVATE
-
: store-path ( request path -- request )
"?" split1 >r >>path r> dup [ query>assoc ] when >>query ;
-! This is all pretty complex because it needs to handle
-! HTTP redirects, which might be absolute or relative
: request-with-url ( url request -- request )
- clone dup "request" set
swap parse-url >r >r store-path r> >>host r> >>port ;
-DEFER: (http-request)
-
+! This is all pretty complex because it needs to handle
+! HTTP redirects, which might be absolute or relative
: absolute-redirect ( url -- request )
- "request" get request-with-url ;
+ request get request-with-url ;
: relative-redirect ( path -- request )
- "request" get swap store-path ;
+ request get swap store-path ;
: do-redirect ( response -- response stream )
dup response-code 300 399 between? [
+ stdio get dispose
header>> "location" swap at
dup "http://" head? [
absolute-redirect
] [
relative-redirect
- ] if "GET" >>method (http-request)
+ ] if "GET" >>method http-request
] [
stdio get
] if ;
-: (http-request) ( request -- response stream )
- dup host>> over port>> <inet> <client> stdio set
- dup "r" set-global write-request flush read-response
- do-redirect ;
+: request-addr ( request -- addr )
+ dup host>> swap port>> <inet> ;
+
+: close-on-error ( stream quot -- )
+ [ with-stream* ] curry [ ] pick [ dispose ] curry cleanup ;
+ inline
PRIVATE>
-: http-request ( url request -- response stream )
- [
- request-with-url
+: http-request ( request -- response stream )
+ dup request [
+ dup request-addr latin1 <client>
+ 1 minutes over set-timeout
[
- (http-request)
- 1 minutes over set-timeout
- ] [ ] [ stdio get dispose ] cleanup
- ] with-scope ;
+ write-request flush
+ read-response
+ do-redirect
+ ] close-on-error
+ ] with-variable ;
-: <get-request> ( -- request )
- <request> "GET" >>method ;
+: <get-request> ( url -- request )
+ <request> request-with-url "GET" >>method ;
: http-get-stream ( url -- response stream )
<get-request> http-request ;
: success? ( code -- ? ) 200 = ;
-: check-response ( response stream -- stream )
- swap code>> success?
- [ dispose "HTTP download failed" throw ] unless ;
+: check-response ( response -- )
+ code>> success?
+ [ "HTTP download failed" throw ] unless ;
: http-get ( url -- string )
- http-get-stream check-response contents ;
+ http-get-stream contents swap check-response ;
: download-name ( url -- name )
file-name "?" split1 drop "/" ?tail drop ;
: download ( url -- )
dup download-name download-to ;
-: <post-request> ( content-type content -- request )
+: <post-request> ( content-type content url -- request )
<request>
+ request-with-url
"POST" >>method
swap >>post-data
swap >>post-data-type ;
: http-post ( content-type content url -- response string )
#! The content is URL encoded for you.
- -rot url-encode <post-request> http-request contents ;
+ >r url-encode r> <post-request> http-request contents ;
"rmid=732423sdfs73242; path=/; domain=.example.net; expires=Fri, 31-Dec-2010 23:59:59 GMT"
dup parse-cookies unparse-cookies =
] unit-test
+
+! Live-fire exercise
+USING: http.server http.server.static http.server.actions
+http.client io.server io.files io accessors namespaces threads
+io.encodings.ascii ;
+
+[ ] [
+ [
+ <dispatcher>
+ <action>
+ [ stop-server "text/html" <content> [ "Goodbye" write ] >>body ] >>get
+ "quit" add-responder
+ "extra/http/test" resource-path <static> >>default
+ default-host set
+
+ [ 1237 httpd ] "HTTPD test" spawn drop
+ ] with-scope
+] unit-test
+
+[ t ] [
+ "extra/http/test/foo.html" resource-path ascii file-contents
+ "http://localhost:1237/foo.html" http-get =
+] unit-test
+
+[ "Goodbye" ] [
+ "http://localhost:1237/quit" http-get
+] unit-test
IN: http.server.actions.tests
USING: http.server.actions tools.test math math.parser
multiline namespaces http io.streams.string http.server
-sequences ;
+sequences accessors ;
-[ + ]
-{ { "a" [ string>number ] } { "b" [ string>number ] } }
-"GET" <action> "action-1" set
+<action>
+ [ "a" get "b" get + ] >>get
+ { { "a" [ string>number ] } { "b" [ string>number ] } } >>get-params
+"action-1" set
STRING: action-request-test-1
GET http://foo/bar?a=12&b=13 HTTP/1.1
"action-1" get call-responder
] unit-test
-[ "X" <repetition> concat append ]
-{ { +path+ [ ] } { "xxx" [ string>number ] } }
-"POST" <action> "action-2" set
+<action>
+ [ +path+ get "xxx" get "X" <repetition> concat append ] >>post
+ { { +path+ [ ] } { "xxx" [ string>number ] } } >>post-params
+"action-2" set
STRING: action-request-test-2
POST http://foo/bar/baz HTTP/1.1
! Copyright (C) 2008 Slava Pestov.\r
! See http://factorcode.org/license.txt for BSD license.\r
USING: accessors new-slots sequences kernel assocs combinators\r
-http.server http hashtables namespaces ;\r
+http.server http.server.validators http hashtables namespaces ;\r
IN: http.server.actions\r
\r
SYMBOL: +path+\r
\r
-TUPLE: action quot params method ;\r
+TUPLE: action get get-params post post-params revalidate ;\r
\r
-C: <action> action\r
+: <action>\r
+ action construct-empty\r
+ [ <400> ] >>get\r
+ [ <400> ] >>post\r
+ [ <400> ] >>revalidate ;\r
\r
: extract-params ( request path -- assoc )\r
>r dup method>> {\r
{ "POST" [ post-data>> query>assoc ] }\r
} case r> +path+ associate union ;\r
\r
-: push-params ( assoc action -- ... )\r
- params>> [ first2 >r swap at r> call ] with each ;\r
+: action-params ( request path param -- error? )\r
+ -rot extract-params validate-params ;\r
+\r
+: get-action ( request path -- response )\r
+ action get get-params>> action-params\r
+ [ <400> ] [ action get get>> call ] if ;\r
+\r
+: post-action ( request path -- response )\r
+ action get post-params>> action-params\r
+ [ action get revalidate>> ] [ action get post>> ] if call ;\r
\r
M: action call-responder ( request path action -- response )\r
- pick request set\r
- pick method>> over method>> = [\r
- >r extract-params r>\r
- [ push-params ] keep\r
- quot>> call\r
- ] [\r
- 3drop <400>\r
- ] if ;\r
+ action set\r
+ over request set\r
+ over method>>\r
+ {\r
+ { "GET" [ get-action ] }\r
+ { "POST" [ post-action ] }\r
+ } case ;\r
--- /dev/null
+! Copyright (c) 2007 Chris Double.\r
+! See http://factorcode.org/license.txt for BSD license.\r
+USING: accessors new-slots quotations assocs kernel splitting\r
+base64 html.elements io combinators http.server\r
+http.server.auth.providers http.server.auth.providers.null\r
+http sequences ;\r
+IN: http.server.auth.basic\r
+\r
+TUPLE: basic-auth responder realm provider ;\r
+\r
+C: <basic-auth> basic-auth\r
+\r
+: authorization-ok? ( provider header -- ? )\r
+ #! Given the realm and the 'Authorization' header,\r
+ #! authenticate the user.\r
+ dup [\r
+ " " split1 swap "Basic" = [\r
+ base64> ":" split1 spin check-login\r
+ ] [\r
+ 2drop f\r
+ ] if\r
+ ] [\r
+ 2drop f\r
+ ] if ;\r
+\r
+: <401> ( realm -- response )\r
+ 401 "Unauthorized" <trivial-response>\r
+ "Basic realm=\"" rot "\"" 3append\r
+ "WWW-Authenticate" set-header\r
+ [\r
+ <html> <body>\r
+ "Username or Password is invalid" write\r
+ </body> </html>\r
+ ] >>body ;\r
+\r
+: logged-in? ( request responder -- ? )\r
+ provider>> swap "authorization" header authorization-ok? ;\r
+\r
+M: basic-auth call-responder ( request path responder -- response )\r
+ pick over logged-in?\r
+ [ responder>> call-responder ] [ 2nip realm>> <401> ] if ;\r
--- /dev/null
+! Copyright (c) 2008 Slava Pestov\r
+! See http://factorcode.org/license.txt for BSD license.\r
+USING: accessors new-slots quotations assocs kernel splitting\r
+base64 html.elements io combinators http.server\r
+http.server.auth.providers http.server.actions\r
+http.server.sessions http.server.templating.fhtml http sequences\r
+io.files namespaces ;\r
+IN: http.server.auth.login\r
+\r
+TUPLE: login-auth responder provider ;\r
+\r
+C: (login-auth) login-auth\r
+\r
+SYMBOL: logged-in?\r
+SYMBOL: provider\r
+SYMBOL: post-login-url\r
+\r
+: login-page ( -- response )\r
+ "text/html" <content> [\r
+ "extra/http/server/auth/login/login.fhtml"\r
+ resource-path run-template-file\r
+ ] >>body ;\r
+\r
+: <login-action>\r
+ <action>\r
+ [ login-page ] >>get\r
+\r
+ {\r
+ { "name" [ ] }\r
+ { "password" [ ] }\r
+ } >>post-params\r
+ [\r
+ "password" get\r
+ "name" get\r
+ provider sget check-login [\r
+ t logged-in? sset\r
+ post-login-url sget <permanent-redirect>\r
+ ] [\r
+ login-page\r
+ ] if\r
+ ] >>post ;\r
+\r
+: <logout-action>\r
+ <action>\r
+ [\r
+ f logged-in? sset\r
+ request get "login" <permanent-redirect>\r
+ ] >>post ;\r
+\r
+M: login-auth call-responder ( request path responder -- response )\r
+ logged-in? sget\r
+ [ responder>> call-responder ] [\r
+ pick method>> "GET" = [\r
+ nip\r
+ provider>> provider sset\r
+ dup request-url post-login-url sset\r
+ "login" f session-link <permanent-redirect>\r
+ ] [\r
+ 3drop <400>\r
+ ] if\r
+ ] if ;\r
+\r
+: <login-auth> ( responder provider -- auth )\r
+ (login-auth)\r
+ <dispatcher>\r
+ swap >>default\r
+ <login-action> "login" add-responder\r
+ <logout-action> "logout" add-responder\r
+ <cookie-sessions> ;\r
--- /dev/null
+<html>\r
+<body>\r
+<h1>Login required</h1>\r
+\r
+<form method="POST" action="login">\r
+<table>\r
+\r
+<tr>\r
+<td>User name:</td>\r
+<td><input name="name" /></td>\r
+</tr>\r
+\r
+<tr>\r
+<td>Password:</td>\r
+<td><input type="password" name="password" /></td>\r
+</tr>\r
+\r
+</table>\r
+\r
+<input type="submit" value="Log in" />\r
+\r
+</form>\r
+\r
+</body>\r
+</html>\r
--- /dev/null
+IN: http.server.auth.providers.assoc.tests\r
+USING: http.server.auth.providers \r
+http.server.auth.providers.assoc tools.test\r
+namespaces ;\r
+\r
+<assoc-auth-provider> "provider" set\r
+\r
+"slava" "provider" get new-user\r
+\r
+[ "slava" "provider" get new-user ] [ user-exists? ] must-fail-with\r
+\r
+[ f ] [ "fdasf" "slava" "provider" get check-login ] unit-test\r
+\r
+[ "xx" "blah" "provider" get set-password ] [ no-such-user? ] must-fail-with\r
+\r
+"fdasf" "slava" "provider" get set-password\r
+\r
+[ t ] [ "fdasf" "slava" "provider" get check-login ] unit-test\r
--- /dev/null
+! Copyright (C) 2008 Slava Pestov.\r
+! See http://factorcode.org/license.txt for BSD license.\r
+IN: http.server.auth.providers.assoc\r
+USING: new-slots accessors assocs kernel\r
+http.server.auth.providers ;\r
+\r
+TUPLE: assoc-auth-provider assoc ;\r
+\r
+: <assoc-auth-provider> ( -- provider )\r
+ H{ } clone assoc-auth-provider construct-boa ;\r
+\r
+M: assoc-auth-provider check-login\r
+ assoc>> at = ;\r
+\r
+M: assoc-auth-provider new-user\r
+ assoc>>\r
+ 2dup key? [ drop user-exists ] when\r
+ t -rot set-at ;\r
+\r
+M: assoc-auth-provider set-password\r
+ assoc>>\r
+ 2dup key? [ drop no-such-user ] unless\r
+ set-at ;\r
--- /dev/null
+IN: http.server.auth.providers.db.tests\r
+USING: http.server.auth.providers\r
+http.server.auth.providers.db tools.test\r
+namespaces db db.sqlite db.tuples continuations\r
+io.files ;\r
+\r
+db-auth-provider "provider" set\r
+\r
+"auth-test.db" temp-file sqlite-db [\r
+ \r
+ [ user drop-table ] ignore-errors\r
+ [ user create-table ] ignore-errors\r
+\r
+ "slava" "provider" get new-user\r
+\r
+ [ "slava" "provider" get new-user ] [ user-exists? ] must-fail-with\r
+\r
+ [ f ] [ "fdasf" "slava" "provider" get check-login ] unit-test\r
+\r
+ [ "xx" "blah" "provider" get set-password ] [ no-such-user? ] must-fail-with\r
+\r
+ "fdasf" "slava" "provider" get set-password\r
+\r
+ [ t ] [ "fdasf" "slava" "provider" get check-login ] unit-test\r
+] with-db\r
--- /dev/null
+! Copyright (C) 2008 Slava Pestov.\r
+! See http://factorcode.org/license.txt for BSD license.\r
+USING: db db.tuples db.types new-slots accessors\r
+http.server.auth.providers kernel ;\r
+IN: http.server.auth.providers.db\r
+\r
+TUPLE: user name password ;\r
+\r
+: <user> user construct-empty ;\r
+\r
+user "USERS"\r
+{\r
+ { "name" "NAME" { VARCHAR 256 } +assigned-id+ }\r
+ { "password" "PASSWORD" { VARCHAR 256 } +not-null+ }\r
+} define-persistent\r
+\r
+: init-users-table ( -- )\r
+ user create-table ;\r
+\r
+TUPLE: db-auth-provider ;\r
+\r
+: db-auth-provider T{ db-auth-provider } ;\r
+\r
+M: db-auth-provider check-login\r
+ drop\r
+ <user>\r
+ swap >>name\r
+ swap >>password\r
+ select-tuple >boolean ;\r
+\r
+M: db-auth-provider new-user\r
+ drop\r
+ [\r
+ <user>\r
+ swap >>name\r
+\r
+ dup select-tuple [ name>> user-exists ] when\r
+\r
+ "unassigned" >>password\r
+\r
+ insert-tuple\r
+ ] with-transaction ;\r
+\r
+M: db-auth-provider set-password\r
+ drop\r
+ [\r
+ <user>\r
+ swap >>name\r
+\r
+ dup select-tuple [ ] [ no-such-user ] ?if\r
+\r
+ swap >>password update-tuple\r
+ ] with-transaction ;\r
--- /dev/null
+! Copyright (C) 2008 Slava Pestov.\r
+! See http://factorcode.org/license.txt for BSD license.\r
+USING: http.server.auth.providers kernel ;\r
+IN: http.server.auth.providers.null\r
+\r
+TUPLE: null-auth-provider ;\r
+\r
+: null-auth-provider T{ null-auth-provider } ;\r
+\r
+M: null-auth-provider check-login 3drop f ;\r
+\r
+M: null-auth-provider new-user 3drop f ;\r
+\r
+M: null-auth-provider set-password 3drop f ;\r
--- /dev/null
+! Copyright (C) 2008 Slava Pestov.\r
+! See http://factorcode.org/license.txt for BSD license.\r
+USING: kernel ;\r
+IN: http.server.auth.providers\r
+\r
+GENERIC: check-login ( password user provider -- ? )\r
+\r
+GENERIC: new-user ( user provider -- )\r
+\r
+GENERIC: set-password ( password user provider -- )\r
+\r
+TUPLE: user-exists name ;\r
+\r
+: user-exists ( name -- * ) \ user-exists construct-boa throw ;\r
+\r
+TUPLE: no-such-user name ;\r
+\r
+: no-such-user ( name -- * ) \ no-such-user construct-boa throw ;\r
] when\r
] H{ } make-assoc ;\r
\r
-: cgi-descriptor ( name -- desc )\r
- [\r
- dup 1array +arguments+ set\r
- cgi-variables +environment+ set\r
- ] H{ } make-assoc ;\r
+: <cgi-process> ( name -- desc )\r
+ <process>\r
+ over 1array >>command\r
+ swap cgi-variables >>environment ;\r
\r
: serve-cgi ( name -- response )\r
<raw-response>\r
200 >>code\r
"CGI output follows" >>message\r
swap [\r
- stdio get swap cgi-descriptor <process-stream> [\r
+ stdio get swap <cgi-process> <process-stream> [\r
post? [\r
request get post-data>> write flush\r
] when\r
--- /dev/null
+! Copyright (C) 2008 Slava Pestov
+! See http://factorcode.org/license.txt for BSD license.
+USING: new-slots html.elements http.server.validators
+accessors namespaces kernel io farkup math.parser assocs
+classes words tuples arrays sequences io.files
+http.server.templating.fhtml splitting mirrors ;
+IN: http.server.components
+
+SYMBOL: components
+
+TUPLE: component id ;
+
+: component ( name -- component )
+ dup components get at
+ [ ] [ "No such component: " swap append throw ] ?if ;
+
+GENERIC: validate* ( string component -- result )
+GENERIC: render-view* ( value component -- )
+GENERIC: render-edit* ( value component -- )
+GENERIC: render-error* ( reason value component -- )
+
+SYMBOL: values
+
+: value values get at ;
+
+: render-view ( component -- )
+ dup id>> value swap render-view* ;
+
+: render-error ( error -- )
+ <span "error" =class span> write </span> ;
+
+: render-edit ( component -- )
+ dup id>> value dup validation-error? [
+ dup reason>> swap value>> rot render-error*
+ ] [
+ swap render-edit*
+ ] if ;
+
+: <component> ( id string -- component )
+ >r \ component construct-boa r> construct-delegate ; inline
+
+TUPLE: string min max ;
+
+: <string> ( id -- component ) string <component> ;
+
+M: string validate*
+ [ min>> v-min-length ] keep max>> v-max-length ;
+
+M: string render-view*
+ drop write ;
+
+: render-input
+ <input "text" =type id>> dup =id =name =value input/> ;
+
+M: string render-edit*
+ render-input ;
+
+M: string render-error*
+ render-input render-error ;
+
+TUPLE: text ;
+
+: <text> ( id -- component ) <string> text construct-delegate ;
+
+: render-textarea
+ <textarea id>> dup =id =name textarea> write </textarea> ;
+
+M: text render-edit*
+ render-textarea ;
+
+M: text render-error*
+ render-textarea render-error ;
+
+TUPLE: farkup ;
+
+: <farkup> ( id -- component ) <text> farkup construct-delegate ;
+
+M: farkup render-view*
+ drop string-lines "\n" join convert-farkup write ;
+
+TUPLE: number min max ;
+
+: <number> ( id -- component ) number <component> ;
+
+M: number validate*
+ >r v-number r> [ min>> v-min-value ] keep max>> v-max-value ;
+
+M: number render-view*
+ drop number>string write ;
+
+M: number render-edit*
+ >r number>string r> render-input ;
+
+M: number render-error*
+ render-input render-error ;
+
+: with-components ( tuple components quot -- )
+ [
+ >r components set
+ dup make-mirror values set
+ tuple set
+ r> call
+ ] with-scope ; inline
+
+TUPLE: form view-template edit-template components ;
+
+: <form> ( id view-template edit-template -- form )
+ V{ } clone form construct-boa
+ swap \ component construct-boa
+ over set-delegate ;
+
+: add-field ( form component -- form )
+ dup id>> pick components>> set-at ;
+
+M: form render-view* ( value form -- )
+ dup components>>
+ swap view-template>>
+ [ resource-path run-template-file ] curry
+ with-components ;
+
+M: form render-edit* ( value form -- )
+ dup components>>
+ swap edit-template>>
+ [ resource-path run-template-file ] curry
+ with-components ;
--- /dev/null
+! Copyright (C) 2008 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+IN: http.server.crud
+USING: kernel namespaces db.tuples math.parser
+http.server.actions accessors ;
+
+: by-id ( class -- tuple )
+ construct-empty "id" get >>id ;
+
+: <delete-action> ( class -- action )
+ <action>
+ { { "id" [ string>number ] } } >>post-params
+ swap [ by-id delete-tuple f ] curry >>post ;
! Copyright (C) 2008 Slava Pestov.\r
! See http://factorcode.org/license.txt for BSD license.\r
USING: db http.server kernel new-slots accessors\r
-continuations namespaces ;\r
+continuations namespaces destructors ;\r
IN: http.server.db\r
\r
TUPLE: db-persistence responder db params ;\r
\r
C: <db-persistence> db-persistence\r
\r
+: connect-db ( db-persistence -- )\r
+ dup db>> swap params>> make-db\r
+ dup db set\r
+ dup db-open\r
+ add-always-destructor ;\r
+\r
M: db-persistence call-responder\r
- dup db>> over params>> make-db dup db-open [\r
- db set responder>> call-responder\r
- ] with-disposal ;\r
+ dup connect-db responder>> call-responder ;\r
threads http sequences prettyprint io.server logging calendar
new-slots html.elements accessors math.parser combinators.lib
vocabs.loader debugger html continuations random combinators
-io.encodings.latin1 ;
+destructors io.encodings.latin1 ;
IN: http.server
GENERIC: call-responder ( request path responder -- response )
swap method>> "HEAD" =
[ drop ] [ write-response-body ] if ;
-: do-request ( request -- request )
+: do-request ( request -- response )
[
dup dup path>> over host>>
find-virtual-host call-responder
: log-request ( request -- )
{ method>> host>> path>> } map-exec-with httpd-hit ;
-: handle-client ( -- )
- default-timeout
+: ?refresh-all ( -- )
development-mode get-global
- [ global [ refresh-all ] bind ] when
- read-request
- dup log-request
- do-request do-response ;
+ [ global [ refresh-all ] bind ] when ;
+
+: handle-client ( -- )
+ [
+ default-timeout
+ ?refresh-all
+ read-request
+ dup log-request
+ do-request do-response
+ ] with-destructors ;
: httpd ( port -- )
internet-server "http.server"
\r
: with-session \ session swap with-variable ; inline\r
\r
+TUPLE: foo ;\r
+\r
+C: <foo> foo\r
+\r
+M: foo init-session drop 0 "x" sset ;\r
+\r
"1234" f <session> [\r
[ ] [ 3 "x" sset ] unit-test\r
\r
[ t ] [ f <cookie-sessions> cookie-sessions? ] unit-test\r
\r
[ ] [\r
- f <url-sessions>\r
- [ 0 "x" sset ] >>init\r
+ <foo> <url-sessions>\r
"manager" set\r
] unit-test\r
\r
GENERIC: init-session ( responder -- )
+M: dispatcher init-session drop ;
+
TUPLE: session-manager responder sessions ;
: <session-manager> ( responder class -- responder' )
USING: calendar html io io.files kernel math math.parser http\r
http.server namespaces parser sequences strings assocs\r
hashtables debugger http.mime sorting html.elements logging\r
-calendar.format new-slots accessors ;\r
+calendar.format new-slots accessors io.encodings.binary ;\r
IN: http.server.static\r
\r
SYMBOL: responder\r
<content>\r
over file-length "content-length" set-header\r
over file-http-date "last-modified" set-header\r
- swap [ <file-reader> stdio get stream-copy ] curry >>body\r
+ swap [ binary <file-reader> stdio get stream-copy ] curry >>body\r
] <file-responder> ;\r
\r
: serve-static ( filename mime-type -- response )\r
+++ /dev/null
-Slava Pestov
-Matthew Willis
--- /dev/null
+Slava Pestov
+Matthew Willis
--- /dev/null
+USING: io io.files io.streams.string io.encodings.utf8
+http.server.templating.fhtml kernel tools.test sequences ;
+IN: http.server.templating.fhtml.tests
+
+: test-template ( path -- ? )
+ "extra/http/server/templating/fhtml/test/" swap append
+ [
+ ".fhtml" append resource-path
+ [ run-template-file ] with-string-writer
+ ] keep
+ ".html" append resource-path utf8 file-contents = ;
+
+[ t ] [ "example" test-template ] unit-test
+[ t ] [ "bug" test-template ] unit-test
+[ t ] [ "stack" test-template ] unit-test
+
+[ ] [ "<%\n%>" parse-template drop ] unit-test
--- /dev/null
+! Copyright (C) 2005 Alex Chapman
+! Copyright (C) 2006, 2007 Slava Pestov
+! See http://factorcode.org/license.txt for BSD license.
+USING: continuations sequences kernel parser namespaces io
+io.files io.streams.string html html.elements
+source-files debugger combinators math quotations generic
+strings splitting accessors http.server.static http.server
+assocs io.encodings.utf8 ;
+
+IN: http.server.templating.fhtml
+
+: templating-vocab ( -- vocab-name ) "http.server.templating.fhtml" ;
+
+! See apps/http-server/test/ or libs/furnace/ for template usage
+! examples
+
+! We use a custom lexer so that %> ends a token even if not
+! followed by whitespace
+TUPLE: template-lexer ;
+
+: <template-lexer> ( lines -- lexer )
+ <lexer> template-lexer construct-delegate ;
+
+M: template-lexer skip-word
+ [
+ {
+ { [ 2dup nth CHAR: " = ] [ drop 1+ ] }
+ { [ 2dup swap tail-slice "%>" head? ] [ drop 2 + ] }
+ { [ t ] [ f skip ] }
+ } cond
+ ] change-column ;
+
+DEFER: <% delimiter
+
+: check-<% ( lexer -- col )
+ "<%" over lexer-line-text rot lexer-column start* ;
+
+: found-<% ( accum lexer col -- accum )
+ [
+ over lexer-line-text
+ >r >r lexer-column r> r> subseq parsed
+ \ write-html parsed
+ ] 2keep 2 + swap set-lexer-column ;
+
+: still-looking ( accum lexer -- accum )
+ [
+ dup lexer-line-text swap lexer-column tail
+ parsed \ print-html parsed
+ ] keep next-line ;
+
+: parse-%> ( accum lexer -- accum )
+ dup still-parsing? [
+ dup check-<%
+ [ found-<% ] [ [ still-looking ] keep parse-%> ] if*
+ ] [
+ drop
+ ] if ;
+
+: %> lexer get parse-%> ; parsing
+
+: parse-template-lines ( lines -- quot )
+ <template-lexer> [
+ V{ } clone lexer get parse-%> f (parse-until)
+ ] with-parser ;
+
+: parse-template ( string -- quot )
+ [
+ use [ clone ] change
+ templating-vocab use+
+ string-lines parse-template-lines
+ ] with-scope ;
+
+: eval-template ( string -- ) parse-template call ;
+
+: html-error. ( error -- )
+ <pre> error. </pre> ;
+
+: run-template-file ( filename -- )
+ [
+ [
+ "quiet" on
+ parser-notes off
+ templating-vocab use+
+ ! so that reload works properly
+ dup source-file file set
+ ?resource-path utf8 file-contents
+ [ eval-template ] [ html-error. drop ] recover
+ ] with-file-vocabs
+ ] curry assert-depth ;
+
+: run-relative-template-file ( filename -- )
+ file get source-file-path parent-directory
+ swap path+ run-template-file ;
+
+: template-convert ( infile outfile -- )
+ utf8 [ run-template-file ] with-file-writer ;
+
+! file responder integration
+: serve-fhtml ( filename -- response )
+ "text/html" <content>
+ swap [ run-template-file ] curry >>body ;
+
+: enable-fhtml ( responder -- responder )
+ [ serve-fhtml ]
+ "application/x-factor-server-page"
+ pick special>> set-at ;
--- /dev/null
+<%
+ USING: prettyprint ;
+ ! Hello world
+ 5 pprint
+%>
--- /dev/null
+<% USING: math ; %>
+
+<html>
+ <head><title>Simple Embedded Factor Example</title></head>
+ <body>
+ <% 5 [ %><p>I like repetition</p><% ] times %>
+ </body>
+</html>
--- /dev/null
+
+
+<html>
+ <head><title>Simple Embedded Factor Example</title></head>
+ <body>
+ <p>I like repetition</p><p>I like repetition</p><p>I like repetition</p><p>I like repetition</p><p>I like repetition</p>
+ </body>
+</html>
+
--- /dev/null
+The stack: <% USING: prettyprint ; .s %>
--- /dev/null
+The stack:
+
+++ /dev/null
-USING: io io.files io.streams.string http.server.templating kernel tools.test
- sequences io.encodings.utf8 ;
-IN: http.server.templating.tests
-
-: test-template ( path -- ? )
- "extra/http/server/templating/test/" swap append
- [
- ".fhtml" append resource-path
- [ run-template-file ] with-string-writer
- ] keep
- ".html" append resource-path utf8 file-contents = ;
-
-[ t ] [ "example" test-template ] unit-test
-[ t ] [ "bug" test-template ] unit-test
-[ t ] [ "stack" test-template ] unit-test
-
-[ ] [ "<%\n%>" parse-template drop ] unit-test
+++ /dev/null
-! Copyright (C) 2005 Alex Chapman
-! Copyright (C) 2006, 2007 Slava Pestov
-! See http://factorcode.org/license.txt for BSD license.
-USING: continuations sequences kernel parser namespaces io
-io.files io.streams.string html html.elements
-source-files debugger combinators math quotations generic
-strings splitting accessors http.server.static http.server
-assocs io.encodings.utf8 ;
-
-IN: http.server.templating
-
-: templating-vocab ( -- vocab-name ) "http.server.templating" ;
-
-! See apps/http-server/test/ or libs/furnace/ for template usage
-! examples
-
-! We use a custom lexer so that %> ends a token even if not
-! followed by whitespace
-TUPLE: template-lexer ;
-
-: <template-lexer> ( lines -- lexer )
- <lexer> template-lexer construct-delegate ;
-
-M: template-lexer skip-word
- [
- {
- { [ 2dup nth CHAR: " = ] [ drop 1+ ] }
- { [ 2dup swap tail-slice "%>" head? ] [ drop 2 + ] }
- { [ t ] [ f skip ] }
- } cond
- ] change-column ;
-
-DEFER: <% delimiter
-
-: check-<% ( lexer -- col )
- "<%" over lexer-line-text rot lexer-column start* ;
-
-: found-<% ( accum lexer col -- accum )
- [
- over lexer-line-text
- >r >r lexer-column r> r> subseq parsed
- \ write-html parsed
- ] 2keep 2 + swap set-lexer-column ;
-
-: still-looking ( accum lexer -- accum )
- [
- dup lexer-line-text swap lexer-column tail
- parsed \ print-html parsed
- ] keep next-line ;
-
-: parse-%> ( accum lexer -- accum )
- dup still-parsing? [
- dup check-<%
- [ found-<% ] [ [ still-looking ] keep parse-%> ] if*
- ] [
- drop
- ] if ;
-
-: %> lexer get parse-%> ; parsing
-
-: parse-template-lines ( lines -- quot )
- <template-lexer> [
- V{ } clone lexer get parse-%> f (parse-until)
- ] with-parser ;
-
-: parse-template ( string -- quot )
- [
- use [ clone ] change
- templating-vocab use+
- string-lines parse-template-lines
- ] with-scope ;
-
-: eval-template ( string -- ) parse-template call ;
-
-: html-error. ( error -- )
- <pre> error. </pre> ;
-
-: run-template-file ( filename -- )
- [
- [
- "quiet" on
- parser-notes off
- templating-vocab use+
- ! so that reload works properly
- dup source-file file set
- ?resource-path utf8 file-contents
- [ eval-template ] [ html-error. drop ] recover
- ] with-file-vocabs
- ] curry assert-depth ;
-
-: run-relative-template-file ( filename -- )
- file get source-file-path parent-directory
- swap path+ run-template-file ;
-
-: template-convert ( infile outfile -- )
- utf8 [ run-template-file ] with-file-writer ;
-
-! file responder integration
-: serve-fhtml ( filename -- response )
- "text/html" <content>
- swap [ run-template-file ] curry >>body ;
-
-: enable-fhtml ( responder -- responder )
- [ serve-fhtml ]
- "application/x-factor-server-page"
- pick special>> set-at ;
+++ /dev/null
-<%
- USING: prettyprint ;
- ! Hello world
- 5 pprint
-%>
+++ /dev/null
-<% USING: math ; %>
-
-<html>
- <head><title>Simple Embedded Factor Example</title></head>
- <body>
- <% 5 [ %><p>I like repetition</p><% ] times %>
- </body>
-</html>
+++ /dev/null
-
-
-<html>
- <head><title>Simple Embedded Factor Example</title></head>
- <body>
- <p>I like repetition</p><p>I like repetition</p><p>I like repetition</p><p>I like repetition</p><p>I like repetition</p>
- </body>
-</html>
-
+++ /dev/null
-The stack: <% USING: prettyprint ; .s %>
+++ /dev/null
-The stack:
-
--- /dev/null
+IN: http.server.validators.tests
+USING: kernel sequences tools.test http.server.validators ;
+
+[ t t ] [ "foo" [ v-number ] with-validator >r validation-error? r> ] unit-test
--- /dev/null
+! Copyright (C) 2006, 2008 Slava Pestov
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel continuations sequences math namespaces
+math.parser assocs new-slots ;
+IN: http.server.validators
+
+TUPLE: validation-error value reason ;
+
+: validation-error ( value reason -- * )
+ \ validation-error construct-boa throw ;
+
+: with-validator ( string quot -- result error? )
+ [ f ] compose curry
+ [ dup validation-error? [ t ] [ rethrow ] if ] recover ; inline
+
+: validate-param ( name validator assoc -- error? )
+ swap pick
+ >r >r at r> with-validator swap r> set ;
+
+: validate-params ( validators assoc -- error? )
+ [ validate-param ] curry { } assoc>map [ ] contains? ;
+
+: v-default ( str def -- str )
+ over empty? spin ? ;
+
+: v-required ( str -- str )
+ dup empty? [ "required" validation-error ] when ;
+
+: v-min-length ( str n -- str )
+ over length over < [
+ [ "must be at least " % # " characters" % ] "" make
+ validation-error
+ ] [
+ drop
+ ] if ;
+
+: v-max-length ( str n -- str )
+ over length over > [
+ [ "must be no more than " % # " characters" % ] "" make
+ validation-error
+ ] [
+ drop
+ ] if ;
+
+: v-number ( str -- n )
+ dup string>number [ ] [
+ "must be a number" validation-error
+ ] ?if ;
+
+: v-min-value ( str n -- str )
+ 2dup < [
+ [ "must be at least " % # ] "" make
+ validation-error
+ ] [
+ drop
+ ] if ;
+
+: v-max-value ( str n -- str )
+ 2dup > [
+ [ "must be no more than " % # ] "" make
+ validation-error
+ ] [
+ drop
+ ] if ;
--- /dev/null
+<html><head><title>Hello</title></head><body>HTTPd test</body></html>
Doug Coleman
+Slava Pestov
calendar ;
IN: io.launcher
-HELP: +command+
-{ $description "Launch descriptor key. A command line string, to be processed by the system's shell." } ;
+ARTICLE: "io.launcher.command" "Specifying a command"
+"The " { $snippet "command" } " slot of a " { $link process } " can contain either a string or a sequence of strings. In the first case, the string is processed in an operating system-specific manner. In the second case, the first element is a program name and the remaining elements are passed to the program as command-line arguments." ;
-HELP: +arguments+
-{ $description "Launch descriptor key. A sequence of command line argument strings. The first element is the program to launch and the remaining arguments are passed to the program without further processing." } ;
+ARTICLE: "io.launcher.detached" "Running processes in the background"
+"By default, " { $link run-process } " waits for the process to complete. To run a process without waiting for it to finish, set the " { $snippet "detached" } " slot of a " { $link process } ", or use the following word:"
+{ $subsection run-detached } ;
-HELP: +detached+
-{ $description "Launch descriptor key. A boolean indicating whether " { $link run-process } " will return immediately, rather than wait for the program to complete."
+ARTICLE: "io.launcher.environment" "Setting environment variables"
+"The " { $snippet "environment" } " slot of a " { $link process } " contains an association mapping environment variable names to values. The interpretation of environment variables is operating system-specific."
$nl
-"Default value is " { $link f } "." }
-{ $notes "Cannot be used with " { $link <process-stream> } "." }
-{ $see-also run-detached } ;
-
-HELP: +environment+
-{ $description "Launch descriptor key. An association mapping strings to strings, specifying environment variables to set for the spawned process. The association is combined with the current environment using the operation specified by the " { $link +environment-mode+ } " launch descriptor key."
+"The " { $snippet "environment-mode" } " slot controls how the environment of the current Factor instance is composed with the value of the " { $snippet "environment" } " slot:"
+{ $subsection +prepend-environment+ }
+{ $subsection +replace-environment+ }
+{ $subsection +append-environment+ }
+"The default value is " { $link +append-environment+ } "." ;
+
+ARTICLE: "io.launcher.redirection" "Input/output redirection"
+"On all operating systems except for Windows CE, the default input/output/error streams can be redirected."
$nl
-"Default value is an empty association." } ;
-
-HELP: +environment-mode+
-{ $description "Launch descriptor key. Must equal of the following:"
- { $list
- { $link +prepend-environment+ }
- { $link +replace-environment+ }
- { $link +append-environment+ }
- }
-"Default value is " { $link +append-environment+ } "."
-} ;
-
-HELP: +stdin+
-{ $description "Launch descriptor key. Must equal one of the following:"
- { $list
- { { $link f } " - standard input is either inherited from the current process, or is a " { $link <process-stream> } " pipe" }
- { { $link +inherit+ } " - standard input is inherited from the current process" }
- { { $link +closed+ } " - standard input is closed" }
- { "a path name - standard input is read from the given file, which must exist" }
- { "a file stream or a socket - standard input is read from the given stream, which must be closed after the process has been started" }
- }
-} ;
-
-HELP: +stdout+
-{ $description "Launch descriptor key. Must equal one of the following:"
- { $list
- { { $link f } " - standard output is either inherited from the current process, or is a " { $link <process-stream> } " pipe" }
- { { $link +inherit+ } " - standard output is inherited from the current process" }
- { { $link +closed+ } " - standard output is closed" }
- { "a path name - standard output is written to the given file, which is overwritten if it already exists" }
- { "a file stream or a socket - standard output is written to the given stream, which must be closed after the process has been started" }
- }
-} ;
-
-HELP: +stderr+
-{ $description "Launch descriptor key. Must equal one of the following:"
- { $list
- { { $link f } " - standard error is inherited from the current process" }
- { { $link +inherit+ } " - same as above" }
- { { $link +stdout+ } " - standard error is merged with standard output" }
- { { $link +closed+ } " - standard error is closed" }
- { "a path name - standard error is written to the given file, which is overwritten if it already exists" }
- { "a file stream or a socket - standard error is written to the given stream, which must be closed after the process has been started" }
- }
+"To specify redirection, set the " { $snippet "stdin" } ", " { $snippet "stdout" } " and " { $snippet "stderr" } " slots of a " { $link process } " to one of the following values:"
+{ $list
+ { { $link f } " - default value; the stream is either inherited from the current process, or is a " { $link <process-stream> } " pipe" }
+ { { $link +inherit+ } " - the stream is inherited from the current process, overriding a " { $link <process-stream> } " pipe" }
+ { { $link +closed+ } " - the stream is closed; reads will return end of file and writes will fails" }
+ { { $link +stdout+ } " - a special value for the " { $snippet "stderr" } " slot only, indicating that the standard output and standard error streams should be merged" }
+ { "a path name - the stream is sent to the given file, which must exist for input and is created automatically on output" }
+ { "a file stream or a socket - the stream is connected to the given Factor stream, which cannot be used again from within Factor and must be closed after the process has been started" }
} ;
HELP: +closed+
-{ $description "Possible value for " { $link +stdin+ } ", " { $link +stdout+ } ", and " { $link +stderr+ } " launch descriptors." } ;
+{ $description "Possible value for the " { $snippet "stdin" } ", " { $snippet "stdout" } ", and " { $snippet "stderr" } " slots of a " { $link process } "." } ;
HELP: +inherit+
-{ $description "Possible value for " { $link +stdin+ } ", " { $link +stdout+ } ", and " { $link +stderr+ } " launch descriptors." } ;
+{ $description "Possible value for the " { $snippet "stdin" } ", " { $snippet "stdout" } ", and " { $snippet "stderr" } " slots of a " { $link process } "." } ;
+
+HELP: +stdout+
+{ $description "Possible value for the " { $snippet "stderr" } " slot of a " { $link process } "." } ;
HELP: +prepend-environment+
-{ $description "Possible value of " { $link +environment-mode+ } " launch descriptor key. The child process environment consists of the value of the " { $link +environment+ } " key together with the current environment, with entries from the current environment taking precedence."
+{ $description "Possible value of " { $snippet "environment-mode" } " slot of a " { $link process } "."
+$nl
+"If this value is set, the child process environment consists of the value of the " { $snippet "environment" } " slot together with the current environment, with entries from the current environment taking precedence."
$nl
"This is used in situations where you want to spawn a child process with some default environment variables set, but allowing the user to override these defaults by changing the environment before launching Factor." } ;
HELP: +replace-environment+
-{ $description "Possible value of " { $link +environment-mode+ } " launch descriptor key. The child process environment consists of the value of the " { $link +environment+ } " key."
+{ $description "Possible value of " { $snippet "environment-mode" } " slot of a " { $link process } "."
+$nl
+"The child process environment consists of the value of the " { $snippet "environment" } " slot."
$nl
"This is used in situations where you want full control over a child process environment, perhaps for security or testing." } ;
HELP: +append-environment+
-{ $description "Possible value of " { $link +environment-mode+ } " launch descriptor key. The child process environment consists of the current environment together with the value of the " { $link +environment+ } " key, with entries from the " { $link +environment+ } " key taking precedence."
+{ $description "Possible value of " { $snippet "environment-mode" } " slot of a " { $link process } "."
+$nl
+"The child process environment consists of the current environment together with the value of the " { $snippet "environment" } " key, with entries from the " { $snippet "environment" } " key taking precedence."
$nl
"This is used in situations where you want a spawn child process with some overridden environment variables." } ;
-HELP: +timeout+
-{ $description "Launch descriptor key. If set to a " { $link duration } ", specifies a maximum running time for the process. If the process runs longer than this time, it will be killed." } ;
-
-HELP: default-descriptor
-{ $description "Association storing default values for launch descriptor keys." } ;
-
-HELP: with-descriptor
-{ $values { "desc" "a launch descriptor" } { "quot" quotation } }
-{ $description "Calls the quotation in a dynamic scope where keys from " { $snippet "desc" } " can be read as variables, and any keys not supplied assume their default value as set in " { $link default-descriptor } "." } ;
+ARTICLE: "io.launcher.timeouts" "Process run-time timeouts"
+"The " { $snippet "timeout" } " slot of a " { $link process } " can be set to a " { $link duration } " specifying a maximum running time for the process. If " { $link wait-for-process } " is called and the process does not exit before the duration expires, it will be killed." ;
HELP: get-environment
-{ $values { "env" "an association" } }
-{ $description "Combines the current environment with the value of " { $link +environment+ } " using " { $link +environment-mode+ } "." } ;
+{ $values { "process" process } { "env" "an association" } }
+{ $description "Combines the current environment with the value of the " { $snippet "environment" } " slot of the " { $link process } " using the " { $snippet "environment-mode" } " slot." } ;
HELP: current-process-handle
{ $values { "handle" "a process handle" } }
{ $contract "Launches a process using the launch descriptor." }
{ $notes "User code should call " { $link run-process } " instead." } ;
-HELP: >descriptor
-{ $values { "desc" "a launch descriptor" } { "desc" "a launch descriptor" } }
-{ $description "Creates a launch descriptor from an object. See " { $link "io.launcher.descriptors" } " for details." } ;
-
HELP: run-process
{ $values { "desc" "a launch descriptor" } { "process" process } }
-{ $description "Launches a process. The object can either be a string, a sequence of strings or a launch descriptor. See " { $link "io.launcher.descriptors" } " for details." }
+{ $description "Launches a process. The object can either be a string, a sequence of strings or a " { $link process } ". See " { $link "io.launcher.descriptors" } " for details." }
{ $notes "The output value can be passed to " { $link wait-for-process } " to get an exit code." } ;
HELP: run-detached
{ $values { "desc" "a launch descriptor" } { "process" process } }
-{ $contract "Launches a process without waiting for it to complete. The object can either be a string, a sequence of strings or a launch descriptor. See " { $link "io.launcher.descriptors" } " for details." }
+{ $contract "Launches a process without waiting for it to complete. The object can either be a string, a sequence of strings or a " { $link process } ". See " { $link "io.launcher.descriptors" } " for details." }
{ $notes
- "This word is functionally identical to passing a launch descriptor to " { $link run-process } " having the " { $link +detached+ } " key set."
+ "This word is functionally identical to passing a " { $link process } " to " { $link run-process } " having the " { $snippet "detached" } " slot set."
$nl
"The output value can be passed to " { $link wait-for-process } " to get an exit code."
} ;
{ $notes "User code should call " { $link kill-process } " intead." } ;
HELP: process
-{ $class-description "A class representing an active or finished process."
-$nl
-"Processes are output by " { $link run-process } " and " { $link run-detached } ", and are stored in the " { $link process-stream-process } " slot of " { $link process-stream } " instances."
-$nl
-"Processes can be passed to " { $link wait-for-process } "." } ;
+{ $class-description "A class representing a process. Instances are created by calling " { $link <process> } "." } ;
+
+HELP: <process>
+{ $values { "process" process } }
+{ $description "Creates a new, empty process. It must be filled in before being passed to " { $link run-process } "." } ;
HELP: process-stream
{ $class-description "A bidirectional stream for interacting with a running process. Instances are created by calling " { $link <process-stream> } ". The " { $link process-stream-process } " slot holds a " { $link process } " instance." } ;
{ "desc" "a launch descriptor" }
{ "encoding" "an encoding descriptor" }
{ "stream" "a bidirectional stream" } }
-{ $description "Launches a process and redirects its input and output via a pair of pipes which may be read and written as a stream of the given encoding." }
-{ $notes "Closing the stream will block until the process exits." } ;
+{ $description "Launches a process and redirects its input and output via a pair of pipes which may be read and written as a stream of the given encoding." } ;
HELP: with-process-stream
{ $values
{ $description "If the process is still running, waits for it to exit, otherwise outputs the exit code immediately. Can be called multiple times on the same process." } ;
ARTICLE: "io.launcher.descriptors" "Launch descriptors"
-"Words which launch processes can take either a command line string, a sequence of command line arguments, or an assoc:"
-{ $list
- { "strings are wrapped in an assoc with a single " { $link +command+ } " key" }
- { "sequences of strings are wrapped in an assoc with a single " { $link +arguments+ } " key" }
- { "associations can be passed in, which allows finer control over launch parameters" }
-}
-"The associations can contain the following keys:"
-{ $subsection +command+ }
-{ $subsection +arguments+ }
-{ $subsection +detached+ }
-{ $subsection +environment+ }
-{ $subsection +environment-mode+ }
-{ $subsection +timeout+ }
-{ $subsection +stdin+ }
-{ $subsection +stdout+ }
-{ $subsection +stderr+ } ;
-
-ARTICLE: "io.launcher" "Launching OS processes"
-"The " { $vocab-link "io.launcher" } " vocabulary implements cross-platform process launching."
-{ $subsection "io.launcher.descriptors" }
-"The following words are used to launch processes:"
+"Words which launch processes can take either a command line string, a sequence of command line arguments, or a " { $link process } "."
+$nl
+"Strings and string arrays are wrapped in a new empty " { $link process } " with the " { $snippet "command" } " slot set. This covers basic use-cases where no launch parameters need to be set."
+$nl
+"A " { $link process } " instance can be created directly and passed to launching words for more control. It must be a fresh instance which has never been spawned before. To spawn a process several times from the same descriptor, " { $link clone } " the descriptor first." ;
+
+ARTICLE: "io.launcher.lifecycle" "The process lifecycle"
+"A freshly instantiated " { $link process } " represents a set of launch parameters."
+{ $subsection process }
+{ $subsection <process> }
+"Words for launching processes take a fresh process which has never been started before as input, and output a copy as output."
+{ $subsection process-started? }
+"The " { $link process } " instance output by launching words contains all original slot values in addition to the " { $snippet "handle" } " slot, which indicates the process is currently running."
+{ $subsection process-running? }
+"It is possible to wait for a process to exit:"
+{ $subsection wait-for-process }
+"A running process can also be killed:"
+{ $subsection kill-process } ;
+
+ARTICLE: "io.launcher.launch" "Launching processes"
+"Launching processes:"
{ $subsection run-process }
-{ $subsection run-detached }
{ $subsection try-process }
-"Stopping processes:"
-{ $subsection kill-process }
-"Finding the current process handle:"
-{ $subsection current-process-handle }
"Redirecting standard input and output to a pipe:"
{ $subsection <process-stream> }
-{ $subsection with-process-stream }
-"A class representing an active or finished process:"
-{ $subsection process }
-"Waiting for a process to end, or getting the exit code of a finished process:"
-{ $subsection wait-for-process }
-"Processes support the " { $link "io.timeouts" } "; the timeout specifies an upper bound on the running time of the process." ;
+{ $subsection with-process-stream } ;
+
+ARTICLE: "io.launcher.examples" "Launcher examples"
+"Starting a command and waiting for it to finish:"
+{ $code
+ "\"ls /etc\" run-process"
+}
+"Starting a program in the background:"
+{ $code
+ "{ \"emacs\" \"foo.txt\" } run-detached"
+}
+"Running a command, throwing an exception if it exits unsuccessfully:"
+{ $code
+ "\"make clean all\" try-process"
+}
+"Running a command, throwing an exception if it exits unsuccessfully or if it takes too long to run:"
+{ $code
+ "<process>"
+ " \"make test\" >>command"
+ " 5 minutes >>timeout"
+ "try-process"
+}
+"Running a command, throwing an exception if it exits unsuccessfully, and redirecting output and error messages to a log file:"
+{ $code
+ "<process>"
+ " \"make clean all\" >>command"
+ " \"log.txt\" >>stdout"
+ " +stdout+ >>stderr"
+ "try-process"
+}
+"Running a command, appending error messages to a log file, and reading the output for further processing:"
+{ $code
+ "\"log.txt\" <file-appender> ["
+ " <process>"
+ " swap >>stderr"
+ " \"report\" >>command"
+ " ascii <process-stream> lines sort reverse [ print ] each"
+ "] with-disposal"
+} ;
+
+ARTICLE: "io.launcher" "Operating system processes"
+"The " { $vocab-link "io.launcher" } " vocabulary implements cross-platform process launching."
+{ $subsection "io.launcher.examples" }
+{ $subsection "io.launcher.descriptors" }
+{ $subsection "io.launcher.launch" }
+"Advanced topics:"
+{ $subsection "io.launcher.lifecycle" }
+{ $subsection "io.launcher.command" }
+{ $subsection "io.launcher.detached" }
+{ $subsection "io.launcher.environment" }
+{ $subsection "io.launcher.redirection" }
+{ $subsection "io.launcher.timeouts" } ;
ABOUT: "io.launcher"
USING: io io.backend io.timeouts system kernel namespaces
strings hashtables sequences assocs combinators vocabs.loader
init threads continuations math io.encodings io.streams.duplex
-io.nonblocking ;
+io.nonblocking new-slots accessors ;
IN: io.launcher
+
+TUPLE: process
+
+command
+detached
+
+environment
+environment-mode
+
+stdin
+stdout
+stderr
+
+timeout
+
+handle status
+killed ;
+
+SYMBOL: +closed+
+SYMBOL: +inherit+
+SYMBOL: +stdout+
+
+SYMBOL: +prepend-environment+
+SYMBOL: +replace-environment+
+SYMBOL: +append-environment+
+
+: <process> ( -- process )
+ process construct-empty
+ H{ } clone >>environment
+ +append-environment+ >>environment-mode ;
+
+: process-started? ( process -- ? )
+ dup handle>> swap status>> or ;
+
+: process-running? ( process -- ? )
+ process-handle >boolean ;
+
! Non-blocking process exit notification facility
SYMBOL: processes
[ H{ } clone processes set-global ] "io.launcher" add-init-hook
-TUPLE: process handle status killed? timeout ;
-
HOOK: register-process io-backend ( process -- )
M: object register-process drop ;
-: <process> ( handle -- process )
- f f f process construct-boa
+: process-started ( process handle -- )
+ >>handle
V{ } clone over processes get set-at
- dup register-process ;
+ register-process ;
M: process equal? 2drop f ;
M: process hashcode* process-handle hashcode* ;
-: process-running? ( process -- ? ) process-status not ;
-
-SYMBOL: +command+
-SYMBOL: +arguments+
-SYMBOL: +detached+
-SYMBOL: +environment+
-SYMBOL: +environment-mode+
-SYMBOL: +stdin+
-SYMBOL: +stdout+
-SYMBOL: +stderr+
+: pass-environment? ( process -- ? )
+ dup environment>> assoc-empty? not
+ swap environment-mode>> +replace-environment+ eq? or ;
-SYMBOL: +timeout+
-
-SYMBOL: +prepend-environment+
-SYMBOL: +replace-environment+
-SYMBOL: +append-environment+
-
-SYMBOL: +closed+
-SYMBOL: +inherit+
-
-: default-descriptor
- H{
- { +command+ f }
- { +arguments+ f }
- { +detached+ f }
- { +environment+ H{ } }
- { +environment-mode+ +append-environment+ }
- } ;
-
-: with-descriptor ( desc quot -- )
- default-descriptor [ >r clone r> bind ] bind ; inline
-
-: pass-environment? ( -- ? )
- +environment+ get assoc-empty? not
- +environment-mode+ get +replace-environment+ eq? or ;
-
-: get-environment ( -- env )
- +environment+ get
- +environment-mode+ get {
+: get-environment ( process -- env )
+ dup environment>>
+ swap environment-mode>> {
{ +prepend-environment+ [ os-envs union ] }
{ +append-environment+ [ os-envs swap union ] }
{ +replace-environment+ [ ] }
: string-array? ( obj -- ? )
dup sequence? [ [ string? ] all? ] [ drop f ] if ;
-: >descriptor ( desc -- desc )
- {
- { [ dup string? ] [ +command+ associate ] }
- { [ dup string-array? ] [ +arguments+ associate ] }
- { [ dup assoc? ] [ >hashtable ] }
- } cond ;
+GENERIC: >process ( obj -- process )
+
+M: process >process
+ dup process-started? [
+ "Process has already been started once" throw
+ ] when
+ clone ;
+
+M: object >process <process> swap >>command ;
HOOK: current-process-handle io-backend ( -- handle )
-HOOK: run-process* io-backend ( desc -- handle )
+HOOK: run-process* io-backend ( process -- handle )
: wait-for-process ( process -- status )
[
- dup process-handle
+ dup handle>>
[
dup [ processes get at push ] curry
"process" suspend drop
] when
- dup process-killed?
- [ "Process was killed" throw ] [ process-status ] if
+ dup killed>>
+ [ "Process was killed" throw ] [ status>> ] if
] with-timeout ;
-: run-process ( desc -- process )
- >descriptor
- dup run-process*
- +timeout+ pick at [ over set-timeout ] when*
- +detached+ rot at [ dup wait-for-process drop ] unless ;
-
: run-detached ( desc -- process )
- >descriptor H{ { +detached+ t } } union run-process ;
+ >process
+ dup dup run-process* process-started
+ dup timeout>> [ over set-timeout ] when* ;
+
+: run-process ( desc -- process )
+ run-detached
+ dup detached>> [ dup wait-for-process drop ] unless ;
TUPLE: process-failed code ;
: process-failed ( code -- * )
\ process-failed construct-boa throw ;
-: try-process ( desc -- )
+: try-process ( command/process -- )
run-process wait-for-process dup zero?
[ drop ] [ process-failed ] if ;
HOOK: kill-process* io-backend ( handle -- )
: kill-process ( process -- )
- t over set-process-killed?
- process-handle [ kill-process* ] when* ;
+ t >>killed
+ handle>> [ kill-process* ] when* ;
-M: process timeout process-timeout ;
+M: process timeout timeout>> ;
M: process set-timeout set-process-timeout ;
M: process timed-out kill-process ;
-HOOK: (process-stream) io-backend ( desc -- in out process )
+HOOK: (process-stream) io-backend ( process -- handle in out )
TUPLE: process-stream process ;
: <process-stream> ( desc encoding -- stream )
- swap >descriptor
- [ (process-stream) >r rot <encoder-duplex> r> ] keep
- +timeout+ swap at [ over set-timeout ] when*
- { set-delegate set-process-stream-process }
- process-stream construct ;
+ >r >process dup dup (process-stream)
+ >r >r process-started process-stream construct-boa
+ r> r> <reader&writer> r> <encoder-duplex>
+ over set-delegate ;
: with-process-stream ( desc quot -- status )
swap <process-stream>
[ swap with-stream ] keep
- process-stream-process wait-for-process ; inline
+ process>> wait-for-process ; inline
-: notify-exit ( status process -- )
- [ set-process-status ] keep
+: notify-exit ( process status -- )
+ >>status
[ processes get delete-at* drop [ resume ] each ] keep
- f swap set-process-handle ;
+ f >>handle
+ drop ;
GENERIC: underlying-handle ( stream -- handle )
! See http://factorcode.org/license.txt for BSD license.
IN: io.nonblocking
USING: math kernel io sequences io.buffers io.timeouts generic
-sbufs system io.streams.duplex io.encodings
+byte-vectors system io.streams.duplex io.encodings
io.backend continuations debugger classes byte-arrays namespaces
splitting dlists assocs io.encodings.binary ;
M: input-port stream-read1
dup wait-to-read1 [ buffer-pop ] unless-eof ;
-: read-step ( count port -- string/f )
+: read-step ( count port -- byte-array/f )
[ wait-to-read ] 2keep
[ dupd buffer> ] unless-eof nip ;
>r 0 max >fixnum r>
2dup read-step dup [
pick over length > [
- pick <sbuf>
+ pick <byte-vector>
[ push-all ] keep
[ read-loop ] keep
- "" like
+ B{ } like
] [
2nip
] if
2nip
] if ;
-: read-until-step ( separators port -- string/f separator/f )
+: read-until-step ( separators port -- byte-array/f separator/f )
dup wait-to-read1
dup port-eof? [
f swap set-port-eof? drop f f
buffer-until
] if ;
-: read-until-loop ( seps port sbuf -- separator/f )
+: read-until-loop ( seps port byte-vector -- separator/f )
2over read-until-step over [
>r over push-all r> dup [
>r 3drop r>
>r 2drop 2drop r>
] if ;
-M: input-port stream-read-until ( seps port -- str/f sep/f )
+M: input-port stream-read-until ( seps port -- byte-array/f sep/f )
2dup read-until-step dup [
>r 2nip r>
] [
over [
- drop >sbuf [ read-until-loop ] keep "" like swap
+ drop >byte-vector
+ [ read-until-loop ] keep
+ B{ } like swap
] [
>r 2nip r>
] if
] if ;
-M: input-port stream-read-partial ( max stream -- string/f )
+M: input-port stream-read-partial ( max stream -- byte-array/f )
>r 0 max >fixnum r> read-step ;
: can-write? ( len writer -- ? )
[ dup port-type >r closed over set-port-type r> close-port ]
if ;
-TUPLE: server-port addr client encoding ;
+TUPLE: server-port addr client client-addr encoding ;
: <server-port> ( handle addr encoding -- server )
rot f server-port <port>
f swap t resolve-host ;
: with-server ( seq service encoding quot -- )
- V{ } clone [
- swap servers [
+ V{ } clone servers [
+ [
[ server-loop ] 2curry with-logging
- ] with-variable
- ] 3curry curry parallel-each ; inline
+ ] 3curry parallel-each
+ ] with-variable ; inline
: stop-server ( -- )
servers get [ dispose ] each ;
M: object client* (client) ;
: <client> ( addrspec encoding -- stream )
- over client* rot <encoder-duplex> <client-stream> ;
+ >r client* r> <encoder-duplex> ;
HOOK: (server) io-backend ( addrspec -- handle )
: <server> ( addrspec encoding -- server )
>r [ (server) ] keep r> <server-port> ;
-HOOK: (accept) io-backend ( server -- stream-in stream-out )
+HOOK: (accept) io-backend ( server -- addrspec handle )
: accept ( server -- client )
- [ (accept) ] keep server-port-encoding <encoder-duplex> ;
+ [ (accept) dup <reader&writer> ] keep
+ server-port-encoding <encoder-duplex>
+ <client-stream> ;
HOOK: <datagram> io-backend ( addrspec -- datagram )
[ stat-st_mtim timespec-sec seconds unix-1970 time+ ]
} cleave
\ file-info construct-boa ;
+
+M: unix-io link-info ( path -- info )
+ lstat* {
+ [ stat>type ]
+ [ stat-st_size ]
+ [ stat-st_mode ]
+ [ stat-st_mtim timespec-sec seconds unix-1970 time+ ]
+ } cleave
+ \ file-info construct-boa ;
swap io-task-filter over set-kevent-filter ;
: register-kevent ( kevent mx -- )
- mx-fd swap 1 f 0 f kevent io-error ;
+ mx-fd swap 1 f 0 f kevent
+ 0 < [ err_no ESRCH = [ (io-error) ] unless ] when ;
M: kqueue-mx register-io-task ( task mx -- )
over EV_ADD make-kevent over register-kevent
: kevent-proc-task ( pid -- )
dup wait-for-pid swap find-process
- dup [ notify-exit ] [ 2drop ] if ;
+ dup [ swap notify-exit ] [ 2drop ] if ;
: handle-kevent ( mx kevent -- )
dup kevent-ident swap kevent-filter {
IN: io.unix.launcher.tests
USING: io.files tools.test io.launcher arrays io namespaces
-continuations math io.encodings.ascii ;
+continuations math io.encodings.ascii io.encodings.latin1
+accessors kernel sequences ;
[ ] [
[ "launcher-test-1" temp-file delete-file ] ignore-errors
] unit-test
[ ] [
- [
- "echo Hello" +command+ set
- "launcher-test-1" temp-file +stdout+ set
- ] { } make-assoc try-process
+ <process>
+ "echo Hello" >>command
+ "launcher-test-1" temp-file >>stdout
+ try-process
] unit-test
[ "Hello\n" ] [
] unit-test
[ "" ] [
- [
+ <process>
"cat"
"launcher-test-1" temp-file
- 2array +arguments+ set
- +inherit+ +stdout+ set
- ] { } make-assoc ascii <process-stream> contents
+ 2array >>command
+ +inherit+ >>stdout
+ ascii <process-stream> contents
] unit-test
[ ] [
] unit-test
[ ] [
- [
- "cat" +command+ set
- +closed+ +stdin+ set
- "launcher-test-1" temp-file +stdout+ set
- ] { } make-assoc try-process
+ <process>
+ "cat" >>command
+ +closed+ >>stdin
+ "launcher-test-1" temp-file >>stdout
+ try-process
] unit-test
[ "" ] [
[ ] [
2 [
"launcher-test-1" temp-file ascii <file-appender> [
- [
- +stdout+ set
- "echo Hello" +command+ set
- ] { } make-assoc try-process
+ <process>
+ swap >>stdout
+ "echo Hello" >>command
+ try-process
] with-disposal
] times
] unit-test
2array
ascii <process-stream> contents
] unit-test
+
+[ t ] [
+ <process>
+ "env" >>command
+ { { "A" "B" } } >>environment
+ latin1 <process-stream> lines
+ "A=B" swap member?
+] unit-test
+
+[ { "A=B" } ] [
+ <process>
+ "env" >>command
+ { { "A" "B" } } >>environment
+ +replace-environment+ >>environment-mode
+ latin1 <process-stream> lines
+] unit-test
io.unix.files io.nonblocking sequences kernel namespaces math
system alien.c-types debugger continuations arrays assocs
combinators unix.process strings threads unix
-io.unix.launcher.parser io.encodings.latin1 ;
+io.unix.launcher.parser io.encodings.latin1 accessors new-slots ;
IN: io.unix.launcher
! Search unix first
USE: unix
-: get-arguments ( -- seq )
- +command+ get [ tokenize-command ] [ +arguments+ get ] if* ;
+: get-arguments ( process -- seq )
+ command>> dup string? [ tokenize-command ] when ;
: assoc>env ( assoc -- env )
[ "=" swap 3append ] { } assoc>map ;
: ?closed dup +closed+ eq? [ drop "/dev/null" ] when ;
-: setup-redirection ( -- )
- +stdin+ get ?closed read-flags 0 redirect
- +stdout+ get ?closed write-flags 1 redirect
- +stderr+ get dup +stdout+ eq?
+: setup-redirection ( process -- process )
+ dup stdin>> ?closed read-flags 0 redirect
+ dup stdout>> ?closed write-flags 1 redirect
+ dup stderr>> dup +stdout+ eq?
[ drop 1 2 dup2 io-error ] [ ?closed write-flags 2 redirect ] if ;
-: spawn-process ( -- )
+: spawn-process ( process -- * )
[
setup-redirection
- get-arguments
- pass-environment?
- [ get-environment assoc>env exec-args-with-env ]
- [ exec-args-with-path ] if
- io-error
- ] [ error. :c flush ] recover 1 exit ;
+ dup pass-environment? [
+ dup get-environment set-os-envs
+ ] when
+
+ get-arguments exec-args-with-path
+ (io-error)
+ ] [ 255 exit ] recover ;
M: unix-io current-process-handle ( -- handle ) getpid ;
-M: unix-io run-process* ( desc -- pid )
- [
- [ spawn-process ] [ ] with-fork <process>
- ] with-descriptor ;
+M: unix-io run-process* ( process -- pid )
+ [ spawn-process ] curry [ ] with-fork ;
M: unix-io kill-process* ( pid -- )
SIGTERM kill io-error ;
2dup first close second close
>r first 0 dup2 drop r> second 1 dup2 drop ;
-: spawn-process-stream ( -- in out pid )
- open-pipe open-pipe [
- setup-stdio-pipe
- spawn-process
- ] [
- -rot 2dup second close first close
- ] with-fork first swap second rot <process> ;
-
M: unix-io (process-stream)
- [
- spawn-process-stream >r <reader&writer> r>
- ] with-descriptor ;
+ >r open-pipe open-pipe r>
+ [ >r setup-stdio-pipe r> spawn-process ] curry
+ [ -rot 2dup second close first close ]
+ with-fork
+ first swap second ;
: find-process ( handle -- process )
- processes get swap [ nip swap process-handle = ] curry
+ processes get swap [ nip swap handle>> = ] curry
assoc-find 2drop ;
! Inefficient process wait polling, used on Linux and Solaris.
2drop t
] [
find-process dup [
- >r *int WEXITSTATUS r> notify-exit f
+ swap *int WEXITSTATUS notify-exit f
] [
2drop f
] if
: wait-to-connect ( port -- )
[ <connect-task> add-io-task ] with-port-continuation drop ;
-M: unix-io (client) ( addrspec -- stream )
+M: unix-io (client) ( addrspec -- client-in client-out )
dup make-sockaddr/size >r >r
protocol-family SOCK_STREAM socket-fd
dup r> r> connect
dup <c-object> [ swap heap-size <int> accept ] keep ; inline
: do-accept ( port fd sockaddr -- )
- rot [
- server-port-addr parse-sockaddr
- swap dup <reader&writer> <duplex-stream> <client-stream>
- ] keep set-server-port-client ;
+ rot
+ [ server-port-addr parse-sockaddr ] keep
+ [ set-server-port-client-addr ] keep
+ set-server-port-client ;
M: accept-task do-io-task
io-task-port dup accept-sockaddr
SOCK_STREAM server-fd
dup 10 listen zero? [ dup close (io-error) ] unless ;
-M: unix-io (accept) ( server -- client-in client-out )
+M: unix-io (accept) ( server -- addrspec handle )
#! Wait for a client connection.
dup check-server-port
dup wait-to-accept
dup pending-error
- server-port-client
- { duplex-stream-in duplex-stream-out } get-slots ;
+ dup server-port-client-addr
+ swap server-port-client ;
! Datagram sockets - UDP and Unix domain
M: unix-io <datagram>
namespaces io.windows.mmap ;
IN: io.windows.ce
+USE: io.windows.files
T{ windows-ce-io } set-io-backend
{ +encrypted+ FILE_ATTRIBUTE_ENCRYPTED }
} get-flags ;
+: win32-file-type ( n -- symbol )
+ FILE_ATTRIBUTE_DIRECTORY mask? +directory+ +regular-file+ ? ;
+
: WIN32_FIND_DATA>file-info
{
- [ WIN32_FIND_DATA-dwFileAttributes win32-file-attributes ]
+ [ WIN32_FIND_DATA-dwFileAttributes win32-file-type ]
[
[ WIN32_FIND_DATA-nFileSizeLow ]
[ WIN32_FIND_DATA-nFileSizeHigh ] bi >64bit
]
[ WIN32_FIND_DATA-dwFileAttributes ]
- [
- WIN32_FIND_DATA-ftLastWriteTime FILETIME>timestamp
- ]
+ ! [ WIN32_FIND_DATA-ftCreationTime FILETIME>timestamp ]
+ [ WIN32_FIND_DATA-ftLastWriteTime FILETIME>timestamp ]
+ ! [ WIN32_FIND_DATA-ftLastAccessTime FILETIME>timestamp ]
} cleave
\ file-info construct-boa ;
: BY_HANDLE_FILE_INFORMATION>file-info
{
- [ BY_HANDLE_FILE_INFORMATION-dwFileAttributes win32-file-attributes ]
+ [ BY_HANDLE_FILE_INFORMATION-dwFileAttributes win32-file-type ]
[
[ BY_HANDLE_FILE_INFORMATION-nFileSizeLow ]
[ BY_HANDLE_FILE_INFORMATION-nFileSizeHigh ] bi >64bit
]
[ BY_HANDLE_FILE_INFORMATION-dwFileAttributes ]
- [
- BY_HANDLE_FILE_INFORMATION-ftLastWriteTime
- FILETIME>timestamp
- ]
+ ! [ BY_HANDLE_FILE_INFORMATION-ftCreationTime FILETIME>timestamp ]
+ [ BY_HANDLE_FILE_INFORMATION-ftLastWriteTime FILETIME>timestamp ]
+ ! [ BY_HANDLE_FILE_INFORMATION-ftLastAccessTime FILETIME>timestamp ]
} cleave
\ file-info construct-boa ;
io.streams.duplex windows.types math windows.kernel32 windows
namespaces io.launcher kernel sequences windows.errors assocs
splitting system threads init strings combinators
-io.backend ;
+io.backend new-slots accessors ;
IN: io.windows.launcher
TUPLE: CreateProcess-args
stdout-pipe stdin-pipe ;
: default-CreateProcess-args ( -- obj )
- 0
+ CreateProcess-args construct-empty
+ 0 >>dwCreateFlags
"STARTUPINFO" <c-object>
- "STARTUPINFO" heap-size over set-STARTUPINFO-cb
- "PROCESS_INFORMATION" <c-object>
- TRUE
- {
- set-CreateProcess-args-dwCreateFlags
- set-CreateProcess-args-lpStartupInfo
- set-CreateProcess-args-lpProcessInformation
- set-CreateProcess-args-bInheritHandles
- } \ CreateProcess-args construct ;
+ "STARTUPINFO" heap-size over set-STARTUPINFO-cb >>lpStartupInfo
+ "PROCESS_INFORMATION" <c-object> >>lpProcessInformation
+ TRUE >>bInheritHandles ;
: call-CreateProcess ( CreateProcess-args -- )
{
- CreateProcess-args-lpApplicationName
- CreateProcess-args-lpCommandLine
- CreateProcess-args-lpProcessAttributes
- CreateProcess-args-lpThreadAttributes
- CreateProcess-args-bInheritHandles
- CreateProcess-args-dwCreateFlags
- CreateProcess-args-lpEnvironment
- CreateProcess-args-lpCurrentDirectory
- CreateProcess-args-lpStartupInfo
- CreateProcess-args-lpProcessInformation
+ lpApplicationName>>
+ lpCommandLine>>
+ lpProcessAttributes>>
+ lpThreadAttributes>>
+ bInheritHandles>>
+ dwCreateFlags>>
+ lpEnvironment>>
+ lpCurrentDirectory>>
+ lpStartupInfo>>
+ lpProcessInformation>>
} get-slots CreateProcess win32-error=0/f ;
: escape-argument ( str -- newstr )
: join-arguments ( args -- cmd-line )
[ escape-argument ] map " " join ;
-: app-name/cmd-line ( -- app-name cmd-line )
- +command+ get [
+: app-name/cmd-line ( process -- app-name cmd-line )
+ command>> dup string? [
" " split1
] [
- +arguments+ get unclip swap join-arguments
- ] if* ;
+ unclip swap join-arguments
+ ] if ;
-: cmd-line ( -- cmd-line )
- +command+ get [ +arguments+ get join-arguments ] unless* ;
+: cmd-line ( process -- cmd-line )
+ command>> dup string? [ join-arguments ] unless ;
-: fill-lpApplicationName
- app-name/cmd-line
- pick set-CreateProcess-args-lpCommandLine
- over set-CreateProcess-args-lpApplicationName ;
+: fill-lpApplicationName ( process args -- process args )
+ over app-name/cmd-line
+ >r >>lpApplicationName
+ r> >>lpCommandLine ;
-: fill-lpCommandLine
- cmd-line over set-CreateProcess-args-lpCommandLine ;
+: fill-lpCommandLine ( process args -- process args )
+ over cmd-line >>lpCommandLine ;
-: fill-dwCreateFlags
+: fill-dwCreateFlags ( process args -- process args )
0
- pass-environment? [ CREATE_UNICODE_ENVIRONMENT bitor ] when
- +detached+ get winnt? and [ DETACHED_PROCESS bitor ] when
- over set-CreateProcess-args-dwCreateFlags ;
+ pick pass-environment? [ CREATE_UNICODE_ENVIRONMENT bitor ] when
+ pick detached>> winnt? and [ DETACHED_PROCESS bitor ] when
+ >>dwCreateFlags ;
-: fill-lpEnvironment
- pass-environment? [
+: fill-lpEnvironment ( process args -- process args )
+ over pass-environment? [
[
- get-environment
- [ "=" swap 3append string>u16-alien % ] assoc-each
+ over get-environment
+ [ swap % "=" % % "\0" % ] assoc-each
"\0" %
- ] { } make >c-ushort-array
- over set-CreateProcess-args-lpEnvironment
+ ] "" make >c-ushort-array
+ >>lpEnvironment
] when ;
-: fill-startup-info
- dup CreateProcess-args-lpStartupInfo
- STARTF_USESTDHANDLES swap set-STARTUPINFO-dwFlags ;
+: fill-startup-info ( process args -- process args )
+ STARTF_USESTDHANDLES over lpStartupInfo>> set-STARTUPINFO-dwFlags ;
-HOOK: fill-redirection io-backend ( args -- args )
+HOOK: fill-redirection io-backend ( process args -- )
-M: windows-ce-io fill-redirection ;
+M: windows-ce-io fill-redirection 2drop ;
-: make-CreateProcess-args ( -- args )
+: make-CreateProcess-args ( process -- args )
default-CreateProcess-args
wince? [ fill-lpApplicationName ] [ fill-lpCommandLine ] if
fill-dwCreateFlags
fill-lpEnvironment
- fill-startup-info ;
+ fill-startup-info
+ nip ;
M: windows-io current-process-handle ( -- handle )
GetCurrentProcessId ;
-M: windows-io run-process* ( desc -- handle )
+M: windows-io run-process* ( process -- handle )
[
- [
- make-CreateProcess-args
- fill-redirection
- dup call-CreateProcess
- CreateProcess-args-lpProcessInformation <process>
- ] with-descriptor
+ dup make-CreateProcess-args
+ tuck fill-redirection
+ dup call-CreateProcess
+ lpProcessInformation>>
] with-destructors ;
M: windows-io kill-process* ( handle -- )
: process-exited ( process -- )
dup process-handle exit-code
over process-handle dispose-process
- swap notify-exit ;
+ notify-exit ;
: wait-for-processes ( processes -- ? )
keys dup
--- /dev/null
+IN: io.windows.launcher.nt.tests\r
+USING: io.launcher tools.test calendar accessors\r
+namespaces kernel system arrays io io.files io.encodings.ascii\r
+sequences parser assocs hashtables ;\r
+\r
+[ ] [\r
+ <process>\r
+ "notepad" >>command\r
+ 1/2 seconds >>timeout\r
+ "notepad" set\r
+] unit-test\r
+\r
+[ f ] [ "notepad" get process-running? ] unit-test\r
+\r
+[ f ] [ "notepad" get process-started? ] unit-test\r
+\r
+[ ] [ "notepad" [ run-detached ] change ] unit-test\r
+\r
+[ "notepad" get wait-for-process ] must-fail\r
+\r
+[ t ] [ "notepad" get killed>> ] unit-test\r
+\r
+[ f ] [ "notepad" get process-running? ] unit-test\r
+\r
+[ ] [\r
+ <process>\r
+ vm "-quiet" "-run=hello-world" 3array >>command\r
+ "out.txt" temp-file >>stdout\r
+ try-process\r
+] unit-test\r
+\r
+[ "Hello world" ] [\r
+ "out.txt" temp-file ascii file-lines first\r
+] unit-test\r
+\r
+[ ] [\r
+ <process>\r
+ vm "-run=listener" 2array >>command\r
+ +closed+ >>stdin\r
+ try-process\r
+] unit-test\r
+\r
+[ ] [\r
+ "extra/io/windows/nt/launcher/test" resource-path [\r
+ <process>\r
+ vm "-script" "stderr.factor" 3array >>command\r
+ "out.txt" temp-file >>stdout\r
+ "err.txt" temp-file >>stderr\r
+ try-process\r
+ ] with-directory\r
+] unit-test\r
+\r
+[ "output" ] [\r
+ "out.txt" temp-file ascii file-lines first\r
+] unit-test\r
+\r
+[ "error" ] [\r
+ "err.txt" temp-file ascii file-lines first\r
+] unit-test\r
+\r
+[ ] [\r
+ "extra/io/windows/nt/launcher/test" resource-path [\r
+ <process>\r
+ vm "-script" "stderr.factor" 3array >>command\r
+ "out.txt" temp-file >>stdout\r
+ +stdout+ >>stderr\r
+ try-process\r
+ ] with-directory\r
+] unit-test\r
+\r
+[ "outputerror" ] [\r
+ "out.txt" temp-file ascii file-lines first\r
+] unit-test\r
+\r
+[ "output" ] [\r
+ "extra/io/windows/nt/launcher/test" resource-path [\r
+ <process>\r
+ vm "-script" "stderr.factor" 3array >>command\r
+ "err2.txt" temp-file >>stderr\r
+ ascii <process-stream> lines first\r
+ ] with-directory\r
+] unit-test\r
+\r
+[ "error" ] [\r
+ "err2.txt" temp-file ascii file-lines first\r
+] unit-test\r
+\r
+[ t ] [\r
+ "extra/io/windows/nt/launcher/test" resource-path [\r
+ <process>\r
+ vm "-script" "env.factor" 3array >>command\r
+ ascii <process-stream> contents\r
+ ] with-directory eval\r
+\r
+ os-envs =\r
+] unit-test\r
+\r
+[ t ] [\r
+ "extra/io/windows/nt/launcher/test" resource-path [\r
+ <process>\r
+ vm "-script" "env.factor" 3array >>command\r
+ +replace-environment+ >>environment-mode\r
+ os-envs >>environment\r
+ ascii <process-stream> contents\r
+ ] with-directory eval\r
+ \r
+ os-envs =\r
+] unit-test\r
+\r
+[ "B" ] [\r
+ "extra/io/windows/nt/launcher/test" resource-path [\r
+ <process>\r
+ vm "-script" "env.factor" 3array >>command\r
+ { { "A" "B" } } >>environment\r
+ ascii <process-stream> contents\r
+ ] with-directory eval\r
+\r
+ "A" swap at\r
+] unit-test\r
+\r
+[ f ] [\r
+ "extra/io/windows/nt/launcher/test" resource-path [\r
+ <process>\r
+ vm "-script" "env.factor" 3array >>command\r
+ { { "HOME" "XXX" } } >>environment\r
+ +prepend-environment+ >>environment-mode\r
+ ascii <process-stream> contents\r
+ ] with-directory eval\r
+\r
+ "HOME" swap at "XXX" =\r
+] unit-test\r
math windows.kernel32 windows namespaces io.launcher kernel
sequences windows.errors assocs splitting system strings
io.windows.launcher io.windows.nt.pipes io.backend
-combinators shuffle ;
+combinators shuffle accessors locals ;
IN: io.windows.nt.launcher
: duplicate-handle ( handle -- handle' )
: redirect-closed ( default obj access-mode create-mode -- handle )
drop 2nip null-pipe ;
-: redirect-file ( default path access-mode create-mode -- handle )
- >r >r >r drop r>
- normalize-pathname
- r> ! access-mode
+:: redirect-file ( default path access-mode create-mode -- handle )
+ path normalize-pathname
+ access-mode
share-mode
security-attributes-inherit
- r> ! create-mode
+ create-mode
FILE_ATTRIBUTE_NORMAL ! flags and attributes
f ! template file
CreateFile dup invalid-handle? dup close-later ;
} cond ;
: default-stdout ( args -- handle )
- CreateProcess-args-stdout-pipe dup [ pipe-out ] when ;
+ stdout-pipe>> dup [ pipe-out ] when ;
-: redirect-stdout ( args -- handle )
+: redirect-stdout ( process args -- handle )
default-stdout
- +stdout+ get
+ swap stdout>>
GENERIC_WRITE
CREATE_ALWAYS
redirect
STD_OUTPUT_HANDLE GetStdHandle or ;
-: redirect-stderr ( args -- handle )
- +stderr+ get +stdout+ eq? [
- CreateProcess-args-lpStartupInfo
+: redirect-stderr ( process args -- handle )
+ over stderr>> +stdout+ eq? [
+ lpStartupInfo>>
STARTUPINFO-hStdOutput
+ nip
] [
drop
f
- +stderr+ get
+ swap stderr>>
GENERIC_WRITE
CREATE_ALWAYS
redirect
] if ;
: default-stdin ( args -- handle )
- CreateProcess-args-stdin-pipe dup [ pipe-in ] when ;
+ stdin-pipe>> dup [ pipe-in ] when ;
-: redirect-stdin ( args -- handle )
+: redirect-stdin ( process args -- handle )
default-stdin
- +stdin+ get
+ swap stdin>>
GENERIC_READ
OPEN_EXISTING
redirect
: add-pipe-dtors ( pipe -- )
dup
- pipe-in close-later
- pipe-out close-later ;
+ in>> close-later
+ out>> close-later ;
-: fill-stdout-pipe
+: fill-stdout-pipe ( args -- args )
<unique-incoming-pipe>
dup add-pipe-dtors
dup pipe-in f set-inherit
- over set-CreateProcess-args-stdout-pipe ;
+ >>stdout-pipe ;
-: fill-stdin-pipe
+: fill-stdin-pipe ( args -- args )
<unique-outgoing-pipe>
dup add-pipe-dtors
dup pipe-out f set-inherit
- over set-CreateProcess-args-stdin-pipe ;
+ >>stdin-pipe ;
-M: windows-nt-io fill-redirection
- dup CreateProcess-args-lpStartupInfo
- over redirect-stdout over set-STARTUPINFO-hStdOutput
- over redirect-stderr over set-STARTUPINFO-hStdError
- over redirect-stdin over set-STARTUPINFO-hStdInput
- drop ;
+M: windows-nt-io fill-redirection ( process args -- )
+ [ 2dup redirect-stdout ] keep lpStartupInfo>> set-STARTUPINFO-hStdOutput
+ [ 2dup redirect-stderr ] keep lpStartupInfo>> set-STARTUPINFO-hStdError
+ [ 2dup redirect-stdin ] keep lpStartupInfo>> set-STARTUPINFO-hStdInput
+ 2drop ;
M: windows-nt-io (process-stream)
[
- [
- make-CreateProcess-args
+ dup make-CreateProcess-args
- fill-stdout-pipe
- fill-stdin-pipe
+ fill-stdout-pipe
+ fill-stdin-pipe
- fill-redirection
+ tuck fill-redirection
- dup call-CreateProcess
+ dup call-CreateProcess
- dup CreateProcess-args-stdin-pipe pipe-in CloseHandle drop
- dup CreateProcess-args-stdout-pipe pipe-out CloseHandle drop
+ dup stdin-pipe>> pipe-in CloseHandle drop
+ dup stdout-pipe>> pipe-out CloseHandle drop
- dup CreateProcess-args-stdout-pipe pipe-in
- over CreateProcess-args-stdin-pipe pipe-out
-
- [ f <win32-file> ] 2apply <reader&writer>
-
- rot CreateProcess-args-lpProcessInformation <process>
- ] with-destructors
- ] with-descriptor ;
+ dup lpProcessInformation>>
+ over stdout-pipe>> in>> f <win32-file>
+ rot stdin-pipe>> out>> f <win32-file>
+ ] with-destructors ;
--- /dev/null
+USE: system\r
+USE: prettyprint\r
+os-envs .\r
--- /dev/null
+USE: io\r
+USE: namespaces\r
+\r
+"output" write flush\r
+"error" stderr get stream-write stderr get stream-flush\r
USE: io.windows.nt.monitors
USE: io.windows.nt.sockets
USE: io.windows.mmap
+USE: io.windows.files
USE: io.backend
T{ windows-nt-io } set-io-backend
USING: alien alien.c-types arrays destructors io io.windows libc
windows.types math windows.kernel32 windows namespaces kernel
sequences windows.errors assocs math.parser system random
-combinators ;
+combinators new-slots accessors ;
IN: io.windows.nt.pipes
! This code is based on
: close-pipe ( pipe -- )
dup
- pipe-in CloseHandle drop
- pipe-out CloseHandle drop ;
+ in>> CloseHandle drop
+ out>> CloseHandle drop ;
: <incoming-pipe> ( name -- pipe )
PIPE_ACCESS_INBOUND GENERIC_WRITE <pipe> ;
! /dev/null simulation
: null-input ( -- pipe )
<unique-outgoing-pipe>
- dup pipe-out CloseHandle drop
- pipe-in ;
+ dup out>> CloseHandle drop
+ in>> ;
: null-output ( -- pipe )
<unique-incoming-pipe>
- dup pipe-in CloseHandle drop
- pipe-out ;
+ dup in>> CloseHandle drop
+ out>> ;
: null-pipe ( mode -- pipe )
{
"stdcall" alien-indirect drop
winsock-error-string [ throw ] when* ;
-: connect-continuation ( ConnectEx -- )
- dup ConnectEx-args-lpOverlapped*
- swap ConnectEx-args-port duplex-stream-in
- [ save-callback ] 2keep
+: connect-continuation ( ConnectEx port -- )
+ >r ConnectEx-args-lpOverlapped* r>
+ 2dup save-callback
get-overlapped-result drop ;
-M: windows-nt-io (client) ( addrspec -- duplex-stream )
+M: windows-nt-io (client) ( addrspec -- client-in client-out )
[
\ ConnectEx-args construct-empty
over make-sockaddr/size pick init-connect
dup ConnectEx-args-s* INADDR_ANY roll bind-socket
dup (ConnectEx)
- dup ConnectEx-args-s* <win32-socket>
- dup <reader&writer> <duplex-stream>
- over set-ConnectEx-args-port
-
- dup connect-continuation
- ConnectEx-args-port
- [ duplex-stream-in pending-error ] keep
- [ duplex-stream-out pending-error ] keep
+ dup ConnectEx-args-s* <win32-socket> dup <reader&writer>
+ >r [ connect-continuation ] keep [ pending-error ] keep r>
] with-destructors ;
TUPLE: AcceptEx-args port
] keep *void*
] keep AcceptEx-args-port server-port-addr parse-sockaddr ;
-: accept-continuation ( AcceptEx -- client )
+: accept-continuation ( AcceptEx -- addrspec client )
[ make-accept-continuation ] keep
[ check-accept-error ] keep
[ extract-remote-host ] keep
! addrspec AcceptEx
- [
- AcceptEx-args-sAcceptSocket* add-completion
- ] keep
+ [ AcceptEx-args-sAcceptSocket* add-completion ] keep
AcceptEx-args-sAcceptSocket* <win32-socket> ;
-M: windows-nt-io (accept) ( server -- client-in client-out )
+M: windows-nt-io (accept) ( server -- addrspec handle )
[
[
dup check-server-port
[ ((accept)) ] keep
[ accept-continuation ] keep
AcceptEx-args-port pending-error
- dup duplex-stream-in pending-error
- dup duplex-stream-out pending-error
] with-timeout
] with-destructors ;
get-ldp LDAP_OPT_PROTOCOL_VERSION LDAP_VERSION3 <int> set-option
-[ B{ 0 0 0 3 } ] [
+[ 3 ] [
get-ldp LDAP_OPT_PROTOCOL_VERSION "int*" <c-object> [ get-option ] keep
+ *int
] unit-test
+[
get-ldp "cn=jimbob,dc=example,dc=com" "secret" [
! get-ldp "dc=example,dc=com" LDAP_SCOPE_ONELEVEL "(objectclass=*)" f 0
get-ldp get-message next-message msgtype result-type
] with-bind
+] drop
IN: ldap.libldap
-"libldap" {
+<< "libldap" {
{ [ win32? ] [ "libldap.dll" "stdcall" ] }
{ [ macosx? ] [ "libldap.dylib" "cdecl" ] }
{ [ unix? ] [ "$LD_LIBRARY_PATH/libldap.so" "cdecl" ] }
-} cond add-library
+} cond add-library >>
: LDAP_VERSION1 1 ; inline
: LDAP_VERSION2 2 ; inline
! Copyright (C) 2008 Slava Pestov.\r
! See http://factorcode.org/license.txt for BSD license.\r
USING: kernel sequences namespaces words assocs logging sorting\r
-prettyprint io io.styles strings logging.parser ;\r
+prettyprint io io.styles strings logging.parser calendar.format ;\r
IN: logging.analysis\r
\r
SYMBOL: word-names\r
] tabular-output ;\r
\r
: log-entry.\r
- [\r
- dup first [ write ] with-cell\r
- dup second [ pprint ] with-cell\r
- dup third [ write ] with-cell\r
- fourth "\n" join [ write ] with-cell\r
- ] with-row ;\r
+ "====== " write\r
+ dup first (timestamp>string) bl\r
+ dup second pprint bl\r
+ dup third write nl\r
+ fourth "\n" join print ;\r
\r
: errors. ( errors -- )\r
- standard-table-style\r
- [ [ log-entry. ] each ] tabular-output ;\r
+ [ log-entry. ] each ;\r
\r
: analysis. ( errors word-histogram message-histogram -- )\r
"==== INTERESTING MESSAGES:" print nl\r
! Copyright (C) 2008 Slava Pestov.\r
! See http://factorcode.org/license.txt for BSD license.\r
-USING: logging.analysis logging.server logging smtp io.sockets\r
-kernel io.files io.streams.string namespaces raptor.cron assocs\r
-io.encodings.utf8 ;\r
+USING: logging.analysis logging.server logging smtp kernel\r
+io.files io.streams.string namespaces alarms assocs\r
+io.encodings.utf8 accessors calendar qualified ;\r
+QUALIFIED: io.sockets\r
IN: logging.insomniac\r
\r
SYMBOL: insomniac-smtp-host\r
] with-scope ; inline\r
\r
: email-subject ( service -- string )\r
- [ "[INSOMNIAC] " % % " on " % host-name % ] "" make ;\r
+ [\r
+ "[INSOMNIAC] " % % " on " % io.sockets:host-name %\r
+ ] "" make ;\r
\r
: (email-log-report) ( service word-names -- )\r
[\r
- over >r\r
- ?analyze-log dup [\r
- r> email-subject\r
- insomniac-recipients get\r
- insomniac-sender get\r
- send-simple-message\r
- ] [ r> 2drop ] if\r
+ dupd ?analyze-log dup [\r
+ <email>\r
+ swap >>body\r
+ insomniac-recipients get >>to\r
+ insomniac-sender get >>from\r
+ swap email-subject >>subject\r
+ send\r
+ ] [ 2drop ] if\r
] with-insomniac-smtp ;\r
\r
\ (email-log-report) NOTICE add-error-logging\r
"logging.insomniac" [ (email-log-report) ] with-logging ;\r
\r
: schedule-insomniac ( service word-names -- )\r
- { 25 } { 6 } f f f <when> -rot [\r
- [ email-log-report ] assoc-each rotate-logs\r
- ] 2curry schedule ;\r
+ [ [ email-log-report ] assoc-each rotate-logs ] 2curry\r
+ 1 days every drop ;\r
! USING: kernel quotations namespaces sequences assocs.lib ;
USING: kernel namespaces namespaces.private quotations sequences
- assocs.lib ;
+ assocs.lib math.parser math sequences.lib ;
IN: namespaces.lib
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
: set* ( val var -- ) namestack* set-assoc-stack ;
+
+SYMBOL: building-seq
+: get-building-seq ( n -- seq )
+ building-seq get nth ;
+
+: n, get-building-seq push ;
+: n% get-building-seq push-all ;
+: n# >r number>string r> n% ;
+
+: 0, 0 n, ;
+: 0% 0 n% ;
+: 0# 0 n# ;
+: 1, 1 n, ;
+: 1% 1 n% ;
+: 1# 1 n# ;
+: 2, 2 n, ;
+: 2% 2 n% ;
+: 2# 2 n# ;
+
+: nmake ( quot exemplars -- seqs )
+ dup length dup zero? [ 1+ ] when
+ [
+ [
+ [ drop 1024 swap new-resizable ] 2map
+ [ building-seq set call ] keep
+ ] 2keep >r [ like ] 2map r> firstn
+ ] with-scope ;
IN: openssl.libssl
-"libssl" {
+<< "libssl" {
{ [ win32? ] [ "ssleay32.dll" "stdcall" ] }
{ [ macosx? ] [ "libssl.dylib" "cdecl" ] }
{ [ unix? ] [ "$LD_LIBRARY_PATH/libssl.so" "cdecl" ] }
-} cond add-library
+} cond add-library >>
: X509_FILETYPE_PEM 1 ; inline
: X509_FILETYPE_ASN1 2 ; inline
IN: pdf.libhpdf
-"libhpdf" {
+<< "libhpdf" {
{ [ win32? ] [ "libhpdf.dll" "stdcall" ] }
{ [ macosx? ] [ "libhpdf.dylib" "cdecl" ] }
{ [ unix? ] [ "$LD_LIBRARY_PATH/libhpdf.so" "cdecl" ] }
-} cond add-library
+} cond add-library >>
! compression mode
: HPDF_COMP_NONE HEX: 00 ; inline ! No contents are compressed
] with-text
- "extra/pdf/test/font_test.pdf" resource-path save-to-file
+ "font_test.pdf" temp-file save-to-file
] with-pdf
+++ /dev/null
-%PDF-1.3
-%·¾Âª
-1 0 obj
-<<
-/Type /Catalog
-/Pages 2 0 R
->>
-endobj
-2 0 obj
-<<
-/Type /Pages
-/Kids [ 4 0 R ]
-/Count 1
->>
-endobj
-3 0 obj
-<<
-/Producer (Haru\040Free\040PDF\040Library\0402.0.8)
->>
-endobj
-4 0 obj
-<<
-/Type /Page
-/MediaBox [ 0 0 595 841 ]
-/Contents 5 0 R
-/Resources <<
-/ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
-/Font <<
-/F1 7 0 R
-/F2 8 0 R
-/F3 9 0 R
-/F4 10 0 R
-/F5 11 0 R
-/F6 12 0 R
-/F7 13 0 R
-/F8 14 0 R
-/F9 15 0 R
-/F10 16 0 R
-/F11 17 0 R
-/F12 18 0 R
-/F13 19 0 R
-/F14 20 0 R
->>
->>
-/Parent 2 0 R
->>
-endobj
-5 0 obj
-<<
-/Length 6 0 R
->>
-stream\r
-1 w
-50 50 495 731 re
-S
-/F1 24 Tf
-BT
-238.148 791 Td
-(Font\040Demo) Tj
-ET
-BT
-/F1 16 Tf
-60 761 Td
-(\074Standard\040Type1\040font\040samples\076) Tj
-ET
-BT
-60 736 Td
-/F2 9 Tf
-(Courier) Tj
-0 -18 Td
-/F2 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-/F3 9 Tf
-(Courier-Bold) Tj
-0 -18 Td
-/F3 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-/F4 9 Tf
-(Courier-Oblique) Tj
-0 -18 Td
-/F4 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-/F5 9 Tf
-(Courier-BoldOblique) Tj
-0 -18 Td
-/F5 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-/F1 9 Tf
-(Helvetica) Tj
-0 -18 Td
-/F1 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-/F6 9 Tf
-(Helvetica-Bold) Tj
-0 -18 Td
-/F6 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-/F7 9 Tf
-(Helvetica-Oblique) Tj
-0 -18 Td
-/F7 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-/F8 9 Tf
-(Helvetica-BoldOblique) Tj
-0 -18 Td
-/F8 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-/F9 9 Tf
-(Times-Roman) Tj
-0 -18 Td
-/F9 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-/F10 9 Tf
-(Times-Bold) Tj
-0 -18 Td
-/F10 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-/F11 9 Tf
-(Times-Italic) Tj
-0 -18 Td
-/F11 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-/F12 9 Tf
-(Times-BoldItalic) Tj
-0 -18 Td
-/F12 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-/F13 9 Tf
-(Symbol) Tj
-0 -18 Td
-/F13 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-/F14 9 Tf
-(ZapfDingbats) Tj
-0 -18 Td
-/F14 20 Tf
-(abcdefgABCDEFG12345!\043$\045&+-@?) Tj
-0 -20 Td
-ET
-
-endstream
-endobj
-6 0 obj
-1517
-endobj
-7 0 obj
-<<
-/Type /Font
-/BaseFont /Helvetica
-/Subtype /Type1
-/Encoding /StandardEncoding
->>
-endobj
-8 0 obj
-<<
-/Type /Font
-/BaseFont /Courier
-/Subtype /Type1
-/Encoding /StandardEncoding
->>
-endobj
-9 0 obj
-<<
-/Type /Font
-/BaseFont /Courier-Bold
-/Subtype /Type1
-/Encoding /StandardEncoding
->>
-endobj
-10 0 obj
-<<
-/Type /Font
-/BaseFont /Courier-Oblique
-/Subtype /Type1
-/Encoding /StandardEncoding
->>
-endobj
-11 0 obj
-<<
-/Type /Font
-/BaseFont /Courier-BoldOblique
-/Subtype /Type1
-/Encoding /StandardEncoding
->>
-endobj
-12 0 obj
-<<
-/Type /Font
-/BaseFont /Helvetica-Bold
-/Subtype /Type1
-/Encoding /StandardEncoding
->>
-endobj
-13 0 obj
-<<
-/Type /Font
-/BaseFont /Helvetica-Oblique
-/Subtype /Type1
-/Encoding /StandardEncoding
->>
-endobj
-14 0 obj
-<<
-/Type /Font
-/BaseFont /Helvetica-BoldOblique
-/Subtype /Type1
-/Encoding /StandardEncoding
->>
-endobj
-15 0 obj
-<<
-/Type /Font
-/BaseFont /Times-Roman
-/Subtype /Type1
-/Encoding /StandardEncoding
->>
-endobj
-16 0 obj
-<<
-/Type /Font
-/BaseFont /Times-Bold
-/Subtype /Type1
-/Encoding /StandardEncoding
->>
-endobj
-17 0 obj
-<<
-/Type /Font
-/BaseFont /Times-Italic
-/Subtype /Type1
-/Encoding /StandardEncoding
->>
-endobj
-18 0 obj
-<<
-/Type /Font
-/BaseFont /Times-BoldItalic
-/Subtype /Type1
-/Encoding /StandardEncoding
->>
-endobj
-19 0 obj
-<<
-/Type /Font
-/BaseFont /Symbol
-/Subtype /Type1
->>
-endobj
-20 0 obj
-<<
-/Type /Font
-/BaseFont /ZapfDingbats
-/Subtype /Type1
->>
-endobj
-xref
-0 21
-0000000000 65535 f\r
-0000000015 00000 n\r
-0000000064 00000 n\r
-0000000123 00000 n\r
-0000000196 00000 n\r
-0000000518 00000 n\r
-0000002089 00000 n\r
-0000002109 00000 n\r
-0000002207 00000 n\r
-0000002303 00000 n\r
-0000002404 00000 n\r
-0000002509 00000 n\r
-0000002618 00000 n\r
-0000002722 00000 n\r
-0000002829 00000 n\r
-0000002940 00000 n\r
-0000003041 00000 n\r
-0000003141 00000 n\r
-0000003243 00000 n\r
-0000003349 00000 n\r
-0000003417 00000 n\r
-trailer
-<<
-/Root 1 0 R
-/Info 3 0 R
-/Size 21
->>
-startxref
-3491
-%%EOF
\r
HELP: delay\r
{ $values \r
+ { "quot" "a quotation" } \r
{ "parser" "a parser" } \r
}\r
{ $description \r
"Delays the construction of a parser until it is actually required to parse. This " \r
"allows for calling a parser that results in a recursive call to itself. The quotation "\r
- "should return the constructed parser." } ;
\ No newline at end of file
+ "should return the constructed parser." } ;\r
MEMO: hide ( parser -- parser )
[ drop ignore ] action ;
-MEMO: delay ( parser -- parser )
+MEMO: delay ( quot -- parser )
delay-parser construct-boa init-parser ;
: PEG:
! Copyright (C) 2007 Chris Double.
! See http://factorcode.org/license.txt for BSD license.
!
-USING: kernel math math.parser arrays tools.test peg peg.search ;
+USING: kernel math math.parser arrays tools.test peg peg.parsers
+peg.search ;
IN: peg.search.tests
{ V{ 123 456 } } [
: method-words
{
- method-def
forget-word
} ;
--- /dev/null
+USING: kernel peg regexp2 sequences tools.test ;
+IN: regexp2.tests
+
+[ T{ parse-result f T{ slice f 3 3 "056" } 46 } ]
+ [ "056" 'octal' parse ] unit-test
--- /dev/null
+USING: assocs combinators.lib kernel math math.parser
+namespaces peg unicode.case sequences unicode.categories
+memoize peg.parsers ;
+USE: io
+USE: tools.walker
+IN: regexp2
+
+<PRIVATE
+
+SYMBOL: ignore-case?
+
+: char=-quot ( ch -- quot )
+ ignore-case? get
+ [ ch>upper [ swap ch>upper = ] ] [ [ = ] ] if
+ curry ;
+
+: char-between?-quot ( ch1 ch2 -- quot )
+ ignore-case? get
+ [ [ ch>upper ] 2apply [ >r >r ch>upper r> r> between? ] ]
+ [ [ between? ] ]
+ if 2curry ;
+
+: or-predicates ( quots -- quot )
+ [ \ dup add* ] map [ [ t ] ] f short-circuit \ nip add ;
+
+: literal-action [ nip ] curry action ;
+
+: delay-action [ curry ] curry action ;
+
+PRIVATE>
+
+: ascii? ( n -- ? )
+ 0 HEX: 7f between? ;
+
+: octal-digit? ( n -- ? )
+ CHAR: 0 CHAR: 7 between? ;
+
+: hex-digit? ( n -- ? )
+ {
+ [ dup digit? ]
+ [ dup CHAR: a CHAR: f between? ]
+ [ dup CHAR: A CHAR: F between? ]
+ } || nip ;
+
+: control-char? ( n -- ? )
+ { [ dup 0 HEX: 1f between? ] [ dup HEX: 7f = ] } || nip ;
+
+: punct? ( n -- ? )
+ "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~" member? ;
+
+: c-identifier-char? ( ch -- ? )
+ { [ dup alpha? ] [ dup CHAR: _ = ] } || nip ;
+
+: java-blank? ( n -- ? )
+ {
+ CHAR: \s
+ CHAR: \t CHAR: \n CHAR: \r
+ HEX: c HEX: 7 HEX: 1b
+ } member? ;
+
+: java-printable? ( n -- ? )
+ { [ dup alpha? ] [ dup punct? ] } || nip ;
+
+MEMO: 'ordinary-char' ( -- parser )
+ [ "\\^*+?|(){}[$" member? not ] satisfy
+ [ char=-quot ] action ;
+
+MEMO: 'octal-digit' ( -- parser ) [ octal-digit? ] satisfy ;
+
+MEMO: 'octal' ( -- parser )
+ "0" token hide 'octal-digit' 1 3 from-m-to-n 2seq
+ [ first oct> ] action ;
+
+MEMO: 'hex-digit' ( -- parser ) [ hex-digit? ] satisfy ;
+
+MEMO: 'hex' ( -- parser )
+ "x" token hide 'hex-digit' 2 exactly-n 2seq
+ "u" token hide 'hex-digit' 6 exactly-n 2seq 2choice
+ [ first hex> ] action ;
+
+: satisfy-tokens ( assoc -- parser )
+ [ >r token r> literal-action ] { } assoc>map choice ;
+
+MEMO: 'simple-escape-char' ( -- parser )
+ {
+ { "\\" CHAR: \\ }
+ { "t" CHAR: \t }
+ { "n" CHAR: \n }
+ { "r" CHAR: \r }
+ { "f" HEX: c }
+ { "a" HEX: 7 }
+ { "e" HEX: 1b }
+ } [ char=-quot ] assoc-map satisfy-tokens ;
+
+MEMO: 'predefined-char-class' ( -- parser )
+ {
+ { "d" [ digit? ] }
+ { "D" [ digit? not ] }
+ { "s" [ java-blank? ] }
+ { "S" [ java-blank? not ] }
+ { "w" [ c-identifier-char? ] }
+ { "W" [ c-identifier-char? not ] }
+ } satisfy-tokens ;
+
+MEMO: 'posix-character-class' ( -- parser )
+ {
+ { "Lower" [ letter? ] }
+ { "Upper" [ LETTER? ] }
+ { "ASCII" [ ascii? ] }
+ { "Alpha" [ Letter? ] }
+ { "Digit" [ digit? ] }
+ { "Alnum" [ alpha? ] }
+ { "Punct" [ punct? ] }
+ { "Graph" [ java-printable? ] }
+ { "Print" [ java-printable? ] }
+ { "Blank" [ " \t" member? ] }
+ { "Cntrl" [ control-char? ] }
+ { "XDigit" [ hex-digit? ] }
+ { "Space" [ java-blank? ] }
+ } satisfy-tokens "p{" "}" surrounded-by ;
+
+MEMO: 'simple-escape' ( -- parser )
+ [
+ 'octal' ,
+ 'hex' ,
+ "c" token hide [ LETTER? ] satisfy 2seq ,
+ any-char ,
+ ] choice* [ char=-quot ] action ;
+
+MEMO: 'escape' ( -- parser )
+ "\\" token hide [
+ 'simple-escape-char' ,
+ 'predefined-char-class' ,
+ 'posix-character-class' ,
+ 'simple-escape' ,
+ ] choice* 2seq ;
+
+MEMO: 'any-char' ( -- parser )
+ "." token [ drop t ] literal-action ;
+
+MEMO: 'char' ( -- parser )
+ 'any-char' 'escape' 'ordinary-char' 3choice [ satisfy ] action ;
+
+DEFER: 'regexp'
+
+TUPLE: group-result str ;
+
+C: <group-result> group-result
+
+MEMO: 'non-capturing-group' ( -- parser )
+ "?:" token hide 'regexp' ;
+
+MEMO: 'positive-lookahead-group' ( -- parser )
+ "?=" token hide 'regexp' [ ensure ] action ;
+
+MEMO: 'negative-lookahead-group' ( -- parser )
+ "?!" token hide 'regexp' [ ensure-not ] action ;
+
+MEMO: 'simple-group' ( -- parser )
+ 'regexp' [ [ <group-result> ] action ] action ;
+
+MEMO: 'group' ( -- parser )
+ [
+ 'non-capturing-group' ,
+ 'positive-lookahead-group' ,
+ 'negative-lookahead-group' ,
+ 'simple-group' ,
+ ] choice* "(" ")" surrounded-by ;
+
+MEMO: 'range' ( -- parser )
+ any-char "-" token hide any-char 3seq
+ [ first2 char-between?-quot ] action ;
+
+MEMO: 'character-class-term' ( -- parser )
+ 'range'
+ 'escape'
+ [ "\\]" member? not ] satisfy [ char=-quot ] action
+ 3choice ;
+
+MEMO: 'positive-character-class' ( -- parser )
+ ! todo
+ "]" token [ CHAR: ] = ] literal-action 'character-class-term' repeat0 2seq
+ 'character-class-term' repeat1 2choice [ or-predicates ] action ;
+
+MEMO: 'negative-character-class' ( -- parser )
+ "^" token hide 'positive-character-class' 2seq
+ [ [ not ] append ] action ;
+
+MEMO: 'character-class' ( -- parser )
+ 'negative-character-class' 'positive-character-class' 2choice
+ "[" "]" surrounded-by [ satisfy ] action ;
+
+MEMO: 'escaped-seq' ( -- parser )
+ any-char repeat1
+ [ ignore-case? get token ] action "\\Q" "\\E" surrounded-by ;
+
+MEMO: 'break' ( quot -- parser )
+ satisfy ensure
+ epsilon just 2choice ;
+
+MEMO: 'break-escape' ( -- parser )
+ "$" token [ "\r\n" member? ] 'break' literal-action
+ "\\b" token [ blank? ] 'break' literal-action
+ "\\B" token [ blank? not ] 'break' literal-action
+ "\\z" token epsilon just literal-action 4choice ;
+
+MEMO: 'simple' ( -- parser )
+ [
+ 'escaped-seq' ,
+ 'break-escape' ,
+ 'group' ,
+ 'character-class' ,
+ 'char' ,
+ ] choice* ;
+
+MEMO: 'exactly-n' ( -- parser )
+ 'integer' [ exactly-n ] delay-action ;
+
+MEMO: 'at-least-n' ( -- parser )
+ 'integer' "," token hide 2seq [ at-least-n ] delay-action ;
+
+MEMO: 'at-most-n' ( -- parser )
+ "," token hide 'integer' 2seq [ at-most-n ] delay-action ;
+
+MEMO: 'from-m-to-n' ( -- parser )
+ 'integer' "," token hide 'integer' 3seq
+ [ first2 from-m-to-n ] delay-action ;
+
+MEMO: 'greedy-interval' ( -- parser )
+ 'exactly-n' 'at-least-n' 'at-most-n' 'from-m-to-n' 4choice ;
+
+MEMO: 'interval' ( -- parser )
+ 'greedy-interval'
+ 'greedy-interval' "?" token hide 2seq [ "reluctant {}" print ] action
+ 'greedy-interval' "+" token hide 2seq [ "possessive {}" print ] action
+ 3choice "{" "}" surrounded-by ;
+
+MEMO: 'repetition' ( -- parser )
+ [
+ ! Possessive
+ ! "*+" token [ <!*> ] literal-action ,
+ ! "++" token [ <!+> ] literal-action ,
+ ! "?+" token [ <!?> ] literal-action ,
+ ! Reluctant
+ ! "*?" token [ <(*)> ] literal-action ,
+ ! "+?" token [ <(+)> ] literal-action ,
+ ! "??" token [ <(?)> ] literal-action ,
+ ! Greedy
+ "*" token [ repeat0 ] literal-action ,
+ "+" token [ repeat1 ] literal-action ,
+ "?" token [ optional ] literal-action ,
+ ] choice* ;
+
+MEMO: 'dummy' ( -- parser )
+ epsilon [ ] literal-action ;
+
+! todo -- check the action
+! MEMO: 'term' ( -- parser )
+ ! 'simple'
+ ! 'repetition' 'interval' 'dummy' 3choice 2seq [ first2 call ] action
+ ! <!+> [ <and-parser> ] action ;
+
: strings ( alphabet length -- seqs )
>r dup length r> number-strings map-alphabet ;
-: nths ( nths seq -- subseq )
- ! nths is a sequence of ones and zeroes
+: switches ( seq1 seq -- subseq )
+ ! seq1 is a sequence of ones and zeroes
>r [ length ] keep [ nth 1 = ] curry subset r>
[ nth ] curry { } map-as ;
: power-set ( seq -- subsets )
- 2 over length exact-number-strings swap [ nths ] curry map ;
+ 2 over length exact-number-strings swap [ switches ] curry map ;
: push-either ( elt quot accum1 accum2 -- )
>r >r keep swap r> r> ? push ; inline
: attempt-each ( seq quot -- result )
(each) iterate-prep (attempt-each-integer) ; inline
+
+: ?nth* ( n seq -- elt/f ? )
+ 2dup bounds-check? [ nth-unsafe t ] [ 2drop f f ] if ; flushable
+
+: nths ( indices seq -- seq' )
+ [ swap nth ] with map ;
--- /dev/null
+Doug Coleman
--- /dev/null
+USING: help.markup help.syntax ;
+IN: singleton
+
+HELP: SINGLETON:
+{ $syntax "SINGLETON: class"
+} { $values
+ { "class" "a new tuple class to define" }
+} { $description
+ "Defines a new tuple class with membership predicate name? and a default empty constructor that is the class name itself."
+} { $examples
+ { $example "SINGLETON: foo\nfoo ." "T{ foo f }" }
+} { $see-also
+ POSTPONE: TUPLE:
+} ;
--- /dev/null
+! Copyright (C) 2007 Doug Coleman.
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel parser quotations prettyprint tuples words ;
+IN: singleton
+
+: SINGLETON:
+ CREATE-CLASS
+ dup { } define-tuple-class
+ dup unparse create-in reset-generic
+ dup construct-empty 1quotation define ; parsing
: stylesheet
H{
- { default-style
+ { default-span-style
H{
{ font "sans-serif" }
{ font-size 36 }
+ }
+ }
+ { default-block-style
+ H{
{ wrap-margin 1000 }
}
}
USING: smtp tools.test io.streams.string threads
-smtp.server kernel sequences namespaces logging ;
+smtp.server kernel sequences namespaces logging accessors
+assocs sorting ;
IN: smtp.tests
{ 0 0 } [ [ ] with-smtp-connection ] must-infer-as
[ { "hello" "." "world" } validate-message ] must-fail
[ "hello\r\nworld\r\n.\r\n" ] [
- { "hello" "world" } [ send-body ] with-string-writer
+ "hello\nworld" [ send-body ] with-string-writer
] unit-test
[ "500 syntax error" check-response ] must-fail
] must-fail
[
- V{
- { "To" "Slava <slava@factorcode.org>, Ed <dharmatech@factorcode.org>" }
+ {
{ "From" "Doug <erg@factorcode.org>" }
{ "Subject" "Factor rules" }
+ { "To" "Slava <slava@factorcode.org>, Ed <dharmatech@factorcode.org>" }
}
{ "slava@factorcode.org" "dharmatech@factorcode.org" }
"erg@factorcode.org"
] [
- "Factor rules"
- {
- "Slava <slava@factorcode.org>"
- "Ed <dharmatech@factorcode.org>"
- }
- "Doug <erg@factorcode.org>"
- simple-headers >r >r 2 head* r> r>
-] unit-test
-
-[
- {
- "To: Slava <slava@factorcode.org>, Ed <dharmatech@factorcode.org>"
- "From: Doug <erg@factorcode.org>"
- "Subject: Factor rules"
- f
- f
- ""
- "Hi guys"
- "Bye guys"
- }
- { "slava@factorcode.org" "dharmatech@factorcode.org" }
- "erg@factorcode.org"
-] [
- "Hi guys\nBye guys"
- "Factor rules"
- {
- "Slava <slava@factorcode.org>"
- "Ed <dharmatech@factorcode.org>"
- }
- "Doug <erg@factorcode.org>"
- prepare-simple-message
- >r >r f 3 pick set-nth f 4 pick set-nth r> r>
+ <email>
+ "Factor rules" >>subject
+ {
+ "Slava <slava@factorcode.org>"
+ "Ed <dharmatech@factorcode.org>"
+ } >>to
+ "Doug <erg@factorcode.org>" >>from
+ prepare
+ dup headers>> >alist sort-keys [
+ drop { "Date" "Message-Id" } member? not
+ ] assoc-subset
+ over to>>
+ rot from>>
] unit-test
[ ] [ [ 4321 smtp-server ] in-thread ] unit-test
[ ] [
[
+ "localhost" smtp-host set
4321 smtp-port set
- "Hi guys\nBye guys"
- "Factor rules"
- {
- "Slava <slava@factorcode.org>"
- "Ed <dharmatech@factorcode.org>"
- }
- "Doug <erg@factorcode.org>"
-
- send-simple-message
+ <email>
+ "Hi guys\nBye guys" >>body
+ "Factor rules" >>subject
+ {
+ "Slava <slava@factorcode.org>"
+ "Ed <dharmatech@factorcode.org>"
+ } >>to
+ "Doug <erg@factorcode.org>" >>from
+ send
] with-scope
-] unit-test
\ No newline at end of file
+] unit-test
! See http://factorcode.org/license.txt for BSD license.
USING: namespaces io io.timeouts kernel logging io.sockets
sequences combinators sequences.lib splitting assocs strings
-math.parser random system calendar io.encodings.ascii calendar.format ;
-
+math.parser random system calendar io.encodings.ascii
+calendar.format new-slots accessors ;
IN: smtp
SYMBOL: smtp-domain
"." over member? [ "Message cannot contain . on a line by itself" throw ] when ;
: send-body ( body -- )
+ string-lines
validate-message
[ write crlf ] each
"." write crlf ;
: get-ok ( -- ) flush receive-response check-response ;
-: send-raw-message ( body to from -- )
- [
- helo get-ok
- mail-from get-ok
- [ rcpt-to get-ok ] each
- data get-ok
- send-body get-ok
- quit get-ok
- ] with-smtp-connection ;
-
: validate-header ( string -- string' )
dup [ "\r\n" member? ] contains?
[ "Invalid header string: " swap append throw ] when ;
-: prepare-header ( key value -- )
+: write-header ( key value -- )
swap
- validate-header %
- ": " %
- validate-header % ;
+ validate-header write
+ ": " write
+ validate-header write
+ crlf ;
+
+: write-headers ( assoc -- )
+ [ write-header ] assoc-each ;
+
+TUPLE: email from to subject headers body ;
-: prepare-headers ( assoc -- )
- [ [ prepare-header ] "" make , ] assoc-each ;
+M: email clone
+ (clone) [ clone ] change-headers ;
+
+: (send) ( email -- )
+ [
+ helo get-ok
+ dup from>> mail-from get-ok
+ dup to>> [ rcpt-to get-ok ] each
+ data get-ok
+ dup headers>> write-headers
+ crlf
+ body>> send-body get-ok
+ quit get-ok
+ ] with-smtp-connection ;
: extract-email ( recepient -- email )
#! This could be much smarter.
">" %
] "" make ;
-: simple-headers ( subject to from -- headers to from )
- [
- >r dup ", " join "To" set [ extract-email ] map r>
- dup "From" set extract-email
- rot "Subject" set
- now timestamp>rfc822-string "Date" set
- message-id "Message-Id" set
- ] { } make-assoc -rot ;
-
-: prepare-message ( body headers -- body' )
- [
- prepare-headers
- "" ,
- dup string? [ string-lines ] when %
- ] { } make ;
+: set-header ( email value key -- email )
+ pick headers>> set-at ;
-: prepare-simple-message ( body subject to from -- body' to from )
- simple-headers >r >r prepare-message r> r> ;
+: prepare ( email -- email )
+ clone
+ dup from>> "From" set-header
+ [ extract-email ] change-from
+ dup to>> ", " join "To" set-header
+ [ [ extract-email ] map ] change-to
+ dup subject>> "Subject" set-header
+ now timestamp>rfc822-string "Date" set-header
+ message-id "Message-Id" set-header ;
-: send-message ( body headers to from -- )
- >r >r prepare-message r> r> send-raw-message ;
+: <email> ( -- email )
+ email construct-empty
+ H{ } clone >>headers ;
-: send-simple-message ( body subject to from -- )
- prepare-simple-message send-raw-message ;
+: send ( email -- )
+ prepare (send) ;
! Dirk's old AUTH CRAM-MD5 code. I don't know anything about
! CRAM MD5, and the old code didn't work properly either, so here
! (cram-md5-auth) "\r\n" append get-ok ;
! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-
-USE: new-slots
-
-TUPLE: email from to subject body ;
-
-: <email> ( -- email ) email construct-empty ;
-
-: send ( email -- )
- { email-body email-subject email-to email-from } get-slots
- send-simple-message ;
inspector layouts vocabs.loader prettyprint.config prettyprint
debugger io.streams.c io.streams.duplex io.files io.backend
quotations io.launcher words.private tools.deploy.config
-bootstrap.image io.encodings.utf8 ;
+bootstrap.image io.encodings.utf8 accessors ;
IN: tools.deploy.backend
: (copy-lines) ( stream -- )
[ (copy-lines) ] with-disposal ;
: run-with-output ( arguments -- )
- [
- +arguments+ set
- +stdout+ +stderr+ set
- ] H{ } make-assoc utf8 <process-stream>
- dup duplex-stream-out dispose
+ <process>
+ swap >>command
+ +stdout+ >>stderr
+ +closed+ >>stdin
+ utf8 <process-stream>
dup copy-lines
process-stream-process wait-for-process zero? [
"Deployment failed" throw
! See http://factorcode.org/license.txt for BSD license.
USING: io.files io words alien kernel math.parser alien.syntax
io.launcher system assocs arrays sequences namespaces qualified
-system math generator.fixup io.encodings.ascii ;
+system math generator.fixup io.encodings.ascii accessors ;
IN: tools.disassembler
: in-file "gdb-in.txt" temp-file ;
] with-file-writer ;
: run-gdb ( -- lines )
- [
- +closed+ +stdin+ set
- out-file +stdout+ set
- [ "gdb" , "-x" , in-file , "-batch" , ] { } make +arguments+ set
- ] { } make-assoc try-process
+ <process>
+ +closed+ >>stdin
+ out-file >>stdout
+ [ "gdb" , "-x" , in-file , "-batch" , ] { } make >>command
+ try-process
out-file ascii file-lines ;
: tabs>spaces ( str -- str' )
USING: unicode.syntax ;
IN: unicode.categories
-CATEGORY: blank Zs Zl Zp ;
+CATEGORY: blank Zs Zl Zp \r\n ;
CATEGORY: letter Ll ;
CATEGORY: LETTER Lu ;
CATEGORY: Letter Lu Ll Lt Lm Lo ;
"stat" <c-object> dup >r
stat check-status
r> ;
+
+: lstat* ( pathname -- stat )
+ "stat" <c-object> dup >r
+ lstat check-status
+ r> ;
: MAP_FAILED -1 <alien> ; inline
+: ESRCH 3 ; inline
: EEXIST 17 ; inline
! ! ! Unix functions
+++ /dev/null
-Chris Double
-Slava Pestov
+++ /dev/null
-! Copyright (C) 2004 Chris Double.
-! Copyright (C) 2006 Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: html http http.server.responders io kernel math
-namespaces prettyprint continuations random system sequences
-assocs ;
-IN: webapps.callback
-
-#! Name of the variable holding the continuation used to exit
-#! back to the httpd responder.
-SYMBOL: exit-continuation
-
-#! Tuple to hold global request data. This gets passed to
-#! the continuation when resumed so it can restore things
-#! like 'stdio' so it writes to the correct socket.
-TUPLE: request stream exitcc method url raw-query query header response ;
-
-: <request> ( -- request )
- stdio get
- exit-continuation get
- "method" get
- "request" get
- "raw-query" get
- "query" get
- "header" get
- "response" get
- request construct-boa ;
-
-: restore-request ( -- )
- request get
- dup request-stream stdio set
- dup request-method "method" set
- dup request-raw-query "raw-query" set
- dup request-query "query" set
- dup request-header "header" set
- dup request-response "response" set
- request-exitcc exit-continuation set ;
-
-: update-request ( request new-request -- )
- [ request-stream over set-request-stream ] keep
- [ request-method over set-request-method ] keep
- [ request-url over set-request-url ] keep
- [ request-raw-query over set-request-raw-query ] keep
- [ request-query over set-request-query ] keep
- [ request-header over set-request-header ] keep
- [ request-response over set-request-response ] keep
- request-exitcc swap set-request-exitcc ;
-
-: with-exit-continuation ( quot -- )
- #! Call the quotation with the variable exit-continuation bound
- #! such that when the exit continuation is called, computation
- #! will resume from the end of this 'with-exit-continuation' call.
- [
- exit-continuation set call exit-continuation get continue
- ] callcc0 drop ;
-
-: expiry-timeout ( -- ms ) 900 1000 * ;
-
-: get-random-id ( -- id )
- #! Generate a random id to use for continuation URL's
- 4 big-random unparse ;
-
-: callback-table ( -- <hashtable> )
- #! Return the global table of continuations
- \ callback-table get-global ;
-
-: reset-callback-table ( -- )
- #! Create the initial global table
- H{ } clone \ callback-table set-global ;
-
-reset-callback-table
-
-#! Tuple for holding data related to a callback.
-TUPLE: item quot expire? request id time-added ;
-
-: <item> ( quot expire? request id -- item )
- millis item construct-boa ;
-
-: expired? ( item -- ? )
- #! Return true if the callback item is expirable
- #! and has expired (ie. was added to the table more than
- #! timeout milliseconds ago).
- [ item-time-added expiry-timeout + millis < ] keep
- item-expire? and ;
-
-: expire-callbacks ( -- )
- #! Expire all continuations in the continuation table
- #! if they are 'timeout-seconds' old (ie. were added
- #! more than 'timeout-seconds' ago.
- callback-table clone [
- expired? [ callback-table delete-at ] [ drop ] if
- ] assoc-each ;
-
-: id>url ( id -- string )
- #! Convert the continuation id to an URL suitable for
- #! embedding in an HREF or other HTML.
- "/responder/callback/?id=" swap url-encode append ;
-
-: register-callback ( quot expire? -- url )
- #! Store a continuation in the table and associate it with
- #! a random id. That continuation will be expired after
- #! a certain period of time if 'expire?' is true.
- request get get-random-id [ <item> ] keep
- [ callback-table set-at ] keep
- id>url ;
-
-: register-html-callback ( quot expire? -- url )
- >r [ serving-html ] swap append r> register-callback ;
-
-: callback-responder ( -- )
- expire-callbacks
- "id" query-param callback-table at [
- [
- dup item-request [
- <request> update-request
- ] when*
- item-quot call
- exit-continuation get continue
- ] with-exit-continuation drop
- ] [
- "404 Callback not available" httpd-error
- ] if* ;
-
-global [
- "callback" [ callback-responder ] add-simple-responder
-] bind
+++ /dev/null
-Chris Double
+++ /dev/null
-! Copyright (C) 2004 Chris Double.
-! See http://factorcode.org/license.txt for BSD license.
-
-USING: http math namespaces io strings kernel html html.elements
-hashtables continuations quotations parser generic sequences
-webapps.callback http.server.responders ;
-IN: webapps.continuation
-
-#! Used inside the session state of responders to indicate whether the
-#! next request should use the post-refresh-get pattern. It is set to
-#! true after each request.
-SYMBOL: post-refresh-get?
-
-: >callable ( quot|interp|f -- interp )
- dup continuation? [
- [ continue ] curry
- ] when ;
-
-: forward-to-url ( url -- )
- #! When executed inside a 'show' call, this will force a
- #! HTTP 302 to occur to instruct the browser to forward to
- #! the request URL.
- [
- "HTTP/1.1 302 Document Moved\nLocation: " % %
- "\nContent-Length: 0\nContent-Type: text/plain\n\n" %
- ] "" make write exit-continuation get continue ;
-
-: forward-to-id ( id -- )
- #! When executed inside a 'show' call, this will force a
- #! HTTP 302 to occur to instruct the browser to forward to
- #! the request URL.
- >r "request" get r> id>url append forward-to-url ;
-
-SYMBOL: current-show
-
-: store-current-show ( -- )
- #! Store the current continuation in the variable 'current-show'
- #! so it can be returned to later by href callbacks. Note that it
- #! recalls itself when the continuation is called to ensure that
- #! it resets its value back to the most recent show call.
- [ ( 0 -- )
- [ ( 0 1 -- )
- current-show set ( 0 -- )
- continue
- ] callcc1
- nip
- restore-request
- call
- store-current-show
- ] callcc0 restore-request ;
-
-: redirect-to-here ( -- )
- #! Force a redirect to the client browser so that the browser
- #! goes to the current point in the code. This forces an URL
- #! change on the browser so that refreshing that URL will
- #! immediately run from this code point. This prevents the
- #! "this request will issue a POST" warning from the browser
- #! and prevents re-running the previous POST logic. This is
- #! known as the 'post-refresh-get' pattern.
- post-refresh-get? get [
- [
- >callable t register-callback forward-to-url
- ] callcc0 restore-request
- ] [
- t post-refresh-get? set
- ] if ;
-
-: (show) ( quot -- hashtable )
- #! See comments for show. The difference is the
- #! quotation MUST set the content-type using 'serving-html'
- #! or similar.
- store-current-show redirect-to-here
- [
- >callable t register-callback swap with-scope
- exit-continuation get continue
- ] callcc0 drop restore-request "response" get ;
-
-: show ( quot -- namespace )
- #! Call the quotation with the URL associated with the current
- #! continuation. All output from the quotation goes to the client
- #! browser. When the URL is later referenced then
- #! computation will resume from this 'show' call with a hashtable on
- #! the stack containing any query or post parameters.
- #! 'quot' has stack effect ( url -- )
- #! NOTE: On return from 'show' the stack is exactly the same as
- #! initial entry with 'quot' popped off and the hashtable pushed on. Even
- #! if the quotation consumes items on the stack.
- [ serving-html ] swap append (show) ;
-
-: (show-final) ( quot -- namespace )
- #! See comments for show-final. The difference is the
- #! quotation MUST set the content-type using 'serving-html'
- #! or similar.
- store-current-show redirect-to-here
- with-scope exit-continuation get continue ;
-
-: show-final ( quot -- namespace )
- #! Similar to 'show', except the quotation does not receive the URL
- #! to resume computation following 'show-final'. No continuation is
- #! stored for this resumption. As a result, 'show-final' is for use
- #! when a page is to be displayed with no further action to occur. Its
- #! use is an optimisation to save having to generate and save a continuation
- #! in that special case.
- #! 'quot' has stack effect ( -- ).
- [ serving-html ] swap compose (show-final) ;
-
-#! Name of variable for holding initial continuation id that starts
-#! the responder.
-SYMBOL: root-callback
-
-: cont-get/post-responder ( id-or-f -- )
- #! httpd responder that handles the root continuation request.
- #! The requests for actual continuation are processed by the
- #! 'callback-responder'.
- [
- [ f post-refresh-get? set <request> request set root-callback get call ] with-scope
- exit-continuation get continue
- ] with-exit-continuation drop ;
-
-: quot-url ( quot -- url )
- current-show get [ continue-with ] 2curry t register-callback ;
-
-: quot-href ( text quot -- )
- #! Write to standard output an HTML HREF where the href,
- #! when referenced, will call the quotation and then return
- #! back to the most recent 'show' call (via the callback-cc).
- #! The text of the link will be the 'text' argument on the
- #! stack.
- <a quot-url =href a> write </a> ;
-
-: install-cont-responder ( name quot -- )
- #! Install a cont-responder with the given name
- #! that will initially run the given quotation.
- #!
- #! Convert the quotation so it is run within a session namespace
- #! and that namespace is initialized first.
- [
- [ cont-get/post-responder ] "get" set
- [ cont-get/post-responder ] "post" set
- swap "responder" set
- root-callback set
- ] make-responder ;
-
-: show-message-page ( message -- )
- #! Display the message in an HTML page with an OK button.
- [
- "Press OK to Continue" [
- swap paragraph
- <a =href a> "OK" write </a>
- ] simple-page
- ] show 2drop ;
+++ /dev/null
-Chris Double
+++ /dev/null
-! Copyright (C) 2004 Chris Double.
-!
-! Redistribution and use in source and binary forms, with or without
-! modification, are permitted provided that the following conditions are met:
-!
-! 1. Redistributions of source code must retain the above copyright notice,
-! this list of conditions and the following disclaimer.
-!
-! 2. Redistributions in binary form must reproduce the above copyright notice,
-! this list of conditions and the following disclaimer in the documentation
-! and/or other materials provided with the distribution.
-!
-! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
-! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-!
-! Simple test applications
-USING: hashtables html kernel io html html.elements strings math
-assocs quotations webapps.continuation namespaces prettyprint
-sequences ;
-
-IN: webapps.continuation.examples
-
-: display-page ( title -- )
- #! Display a page with some text to test the cont-responder.
- #! The page has a link to the 'next' continuation.
- [
- <h1> over write </h1>
- swap [
- <a =href a> "Next" write </a>
- ] simple-html-document
- ] show 2drop ;
-
-: display-get-name-page ( -- name )
- #! Display a page prompting for input of a name and return that name.
- [
- "Enter your name" [
- <h1> swap write </h1>
- <form "post" =method =action form>
- "Name: " write
- <input "text" =type "name" =name "20" =size input/>
- <input "submit" =type "Ok" =value input/>
- </form>
- ] simple-html-document
- ] show "name" swap at ;
-
-: test-cont-responder ( -- )
- #! Test the cont-responder responder by displaying a few pages in a row.
- "Page one" display-page
- "Hello " display-get-name-page append display-page
- "Page three" display-page ;
-
-: test-cont-responder2 ( -- )
- #! Test the cont-responder responder by displaying a few pages in a loop.
- [ "one" "two" "three" "four" ] [ display-page ] each
- "Done!" display-page ;
-
-: test-cont-responder3 ( -- )
- #! Test the quot-href word by displaying a menu of the current
- #! test words. Note that we use show-final as we don't link to a 'next' page.
- [
- "Menu" [
- <h1> "Menu" write </h1>
- <ol>
- <li> "Test responder1" [ test-cont-responder ] quot-href </li>
- <li> "Test responder2" [ test-cont-responder2 ] quot-href </li>
- </ol>
- ] simple-html-document
- ] show-final ;
-
-: counter-example ( count -- )
- #! Display a counter which can be incremented or decremented
- #! using anchors.
- #!
- #! Don't need the original alist
- [
- #! And we don't need the 'url' argument
- drop
- "Counter: " over unparse append [
- dup <h2> unparse write </h2>
- "++" over 1quotation [ f ] swap append [ 1 + counter-example ] append quot-href
- "--" over 1quotation [ f ] swap append [ 1 - counter-example ] append quot-href
- drop
- ] simple-html-document
- ] show drop ;
-
-: counter-example2 ( -- )
- #! Display a counter which can be incremented or decremented
- #! using anchors.
- #!
- 0 "counter" set
- [
- #! We don't need the 'url' argument
- drop
- "Counter: " "counter" get unparse append [
- <h2> "counter" get unparse write </h2>
- "++" [ "counter" get 1 + "counter" set ] quot-href
- "--" [ "counter" get 1 - "counter" set ] quot-href
- ] simple-html-document
- ] show
- drop ;
-
-! Install the examples
-"counter1" [ drop 0 counter-example ] install-cont-responder
-"counter2" [ drop counter-example2 ] install-cont-responder
-"test1" [ test-cont-responder ] install-cont-responder
-"test2" [ drop test-cont-responder2 ] install-cont-responder
-"test3" [ drop test-cont-responder3 ] install-cont-responder
+++ /dev/null
-Chris Double
+++ /dev/null
-! Copyright (C) 2006 Chris Double. All Rights Reserved.
-! See http://factorcode.org/license.txt for BSD license.
-!
-USING: kernel furnace fjsc peg namespaces
- lazy-lists io io.files furnace.validator sequences
- http.client http.server http.server.responders
- webapps.file html ;
-IN: webapps.fjsc
-
-: compile ( code -- )
- #! Compile the factor code as a string, outputting the http
- #! response containing the javascript.
- serving-text
- 'expression' parse parse-result-ast fjsc-compile
- write flush ;
-
-! The 'compile' action results in an URL that looks like
-! 'responder/fjsc/compile'. It takes one query or post
-! parameter called 'code'. It calls the 'compile' word
-! passing the parameter to it on the stack.
-\ compile {
- { "code" v-required }
-} define-action
-
-: compile-url ( url -- )
- #! Compile the factor code at the given url, return the javascript.
- dup "http:" head? [ "Unable to access remote sites." throw ] when
- "http://" "host" header-param rot 3append http-get compile "();" write flush ;
-
-\ compile-url {
- { "url" v-required }
-} define-action
-
-: render-page* ( model body-template head-template -- )
- [
- [ render-component ] [ f rot render-component ] html-document
- ] serve-html ;
-
-: repl ( -- )
- #! The main 'repl' page.
- f "repl" "head" render-page* ;
-
-! An action called 'repl'
-\ repl { } define-action
-
-: fjsc-web-app ( -- )
- ! Create the web app, providing access
- ! under '/responder/fjsc' which calls the
- ! 'repl' action.
- "fjsc" "repl" "extra/webapps/fjsc" web-app
-
- ! An URL to the javascript resource files used by
- ! the 'fjsc' responder.
- "fjsc-resources" [
- [
- "extra/fjsc/resources/" resource-path doc-root set
- file-responder
- ] with-scope
- ] add-simple-responder
-
- ! An URL to the resource files used by
- ! 'termlib'.
- "fjsc-repl-resources" [
- [
- "extra/webapps/fjsc/resources/" resource-path doc-root set
- file-responder
- ] with-scope
- ] add-simple-responder ;
-
-MAIN: fjsc-web-app
+++ /dev/null
-<title>Factor to Javascript REPL</title>\r
-<link rel="stylesheet" type="text/css" href="/responder/fjsc-repl-resources/termlib/term_styles.css"/>\r
-<script type="text/javascript" src="/responder/fjsc-repl-resources/termlib/termlib.js"></script>\r
-<script type="text/javascript" src="/responder/fjsc-resources/jquery.js"></script>\r
-<script type="text/javascript" src="/responder/fjsc-resources/bootstrap.js"></script>\r
-<script type="text/javascript" src="/responder/fjsc-repl-resources/repl.js"></script>\r
-<script type="text/javascript" src="/responder/fjsc/compile-url?url=/responder/fjsc-resources/bootstrap.factor"></script>\r
+++ /dev/null
-<table border="0">
-<tr><td valign="top">
-<div id="repl" style="position:relative;"></div>
-<p>More information on the Factor to Javascript compiler can be found at these blog posts:
-<ul>
-<li><a href="http://www.bluishcoder.co.nz/2006/12/compiling-factor-to-javascript.html">Factor to Javascript Compiler</a></li>
-<li><a href="http://www.bluishcoder.co.nz/2006/12/factor-to-javascript-compiler-updates.html">Factor to Javascript Compiler Updates</a></li>
-<li><a href="http://www.bluishcoder.co.nz/2006/12/continuations-added-to-fjsc.html">Continuations added to fjsc</a></li>
-<li><a href="http://www.bluishcoder.co.nz/2006/12/cross-domain-json-with-fjsc.html">Cross Domain JSON with fjsc</a></li>
-<li><a href="http://www.bluishcoder.co.nz/2007/02/factor-to-javascript-compiler-makeover.html">Factor to Javascript Compiler Makeover</a></li>
-</ul>
-</p>
-<p>The terminal emulation code for the Factor REPL is provided by the awesome <a href="http://www.masswerk.at/termlib/index.html">termlib</a> library by Norbert Landsteiner. Documentation for termlib is <a href="/responder/fjsc-repl-resources/termlib/">available here</a>. Please note the license of 'termlib':</p>
-<blockquote>This JavaScript-library is free for private and academic use. Please include a readable copyright statement and a backlink to <http://www.masswerk.at> in the web page. The library should always be accompanied by the "readme.txt" and the sample HTML-documents.
-
-The term "private use" includes any personal or non-commercial use, which is not related to commercial activites, but excludes intranet, extranet and/or public net applications that are related to any kind of commercial or profit oriented activity.
-
-For commercial use see <a href="http://www.masswerk.at">http://www.masswerk.at</a> for contact information.</blockquote>
-</td>
-<td valign="top">
-<p><b>Stack</b></p>
-<div id="stack">
-</div>
-<p><b>Playground</b></p>
-<div id="playground">
-</div>
-<h3>Compiled Code</h3>
-<textarea id="compiled" cols="40" rows="10">
-</textarea>
-<p>Some useful words:
-<dl>
-<dt>vocabs ( -- seq )</dt>
-<dd>Return a sequence of available vocabularies</dd>
-<dt>words ( string -- seq )</dt>
-<dd>Return a sequence of words in the given vocabulary</dd>
-<dt>all-words ( -- seq )</dt>
-<dd>Return a sequence of all words</dd>
-</dl>
-</p>
-<p>The contents of <a href="/responder/fjsc-resources/bootstrap.factor">bootstrap.factor</a> have been loaded on startup.</p>
-</td>
-</tr>
-</table>
+++ /dev/null
-/* Copyright (C) 2007 Chris Double. All Rights Reserved.\r
- See http://factorcode.org/license.txt for BSD license. */\r
-\r
-var fjsc_repl = false;\r
-\r
-function fjsc_repl_handler() {\r
- var my_term = this;\r
- this.newLine();\r
- if(this.lineBuffer != '') {\r
- factor.server_eval(\r
- this.lineBuffer, \r
- function(text, result) { \r
- document.getElementById("compiled").value = result;\r
- display_datastack(); \r
- }, \r
- function() { my_term.prompt(); });\r
- }\r
- else\r
- my_term.prompt();\r
-}\r
-\r
-function fjsc_init_handler() {\r
- this.write(\r
- [\r
- TermGlobals.center('********************************************************'),\r
- TermGlobals.center('* *'),\r
- TermGlobals.center('* Factor to Javascript Compiler Example *'),\r
- TermGlobals.center('* *'),\r
- TermGlobals.center('********************************************************')\r
- ]);\r
- \r
- this.prompt();\r
-}\r
-\r
-function startup() {\r
- var conf = {\r
- x: 0,\r
- y: 0,\r
- cols: 64,\r
- rows: 18,\r
- termDiv: "repl",\r
- crsrBlinkMode: true,\r
- ps: "scratchpad ",\r
- initHandler: fjsc_init_handler,\r
- handler: fjsc_repl_handler\r
- };\r
- fjsc_repl = new Terminal(conf);\r
- fjsc_repl.open();\r
-}\r
-\r
-function display_datastack() {\r
- var html=[];\r
- html.push("<table border='1'>")\r
- for(var i = 0; i < factor.cont.data_stack.length; ++i) {\r
- html.push("<tr><td>")\r
- html.push(factor.cont.data_stack[i])\r
- html.push("</td></tr>")\r
- }\r
- html.push("</table>")\r
- document.getElementById('stack').innerHTML=html.join("");\r
-}\r
-\r
-jQuery(function() {\r
- startup();\r
- display_datastack();\r
-});\r
-\r
-factor.add_word("kernel", ".s", "primitive", function(next) { \r
- var stack = factor.cont.data_stack;\r
- var term = fjsc_repl;\r
- for(var i=0; i<stack.length; ++i) {\r
- term.type(""+stack[i]);\r
- term.newLine();\r
- }\r
- factor.call_next(next);\r
-});\r
-\r
-factor.add_word("io", "print", "primitive", function(next) { \r
- var stack = factor.cont.data_stack;\r
- var term = fjsc_repl;\r
- term.type(""+stack.pop());\r
- term.newLine();\r
- factor.call_next(next);\r
-});\r
-\r
-factor.add_word("io", "write", "primitive", function(next) { \r
- var stack = factor.cont.data_stack;\r
- var term = fjsc_repl;\r
- term.type(""+stack.pop());\r
- factor.call_next(next);\r
-});\r
-\r
-factor.add_word("io", ".", "primitive", function(next) { \r
- var stack = factor.cont.data_stack;\r
- var term = fjsc_repl;\r
- term.type(""+stack.pop());\r
- term.newLine();\r
- factor.call_next(next);\r
-});\r
+++ /dev/null
-<HTML>\r
-<HEAD>\r
- <TITLE>mass:werk termlib faq</TITLE>\r
-\r
-<STYLE TYPE="text/css">\r
-body,p,a,td {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 12px;\r
- color: #cccccc;\r
-}\r
-.lh13 {\r
- line-height: 13px;\r
-}\r
-.lh15 {\r
- line-height: 15px;\r
-}\r
-pre {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- color: #ccffaa;\r
- font-size: 12px;\r
- line-height: 15px;\r
-}\r
-.prop {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- color: #bbee99;\r
- font-size: 12px;\r
- line-height: 15px;\r
-}\r
-h1 {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 16px;\r
- color: #cccccc;\r
-}\r
-b.quest {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 14px;\r
- font-weight: bold;\r
- color: #bbee99;\r
-}\r
-a,a:link,a:visited {\r
- text-decoration: none;\r
- color: #77dd11;\r
-}\r
-a:hover {\r
- text-decoration: underline;\r
- color: #77dd11;\r
-}\r
-a:active {\r
- text-decoration: underline;\r
- color: #dddddd;\r
-}\r
-\r
-@media print {\r
- body { background-color: #ffffff; }\r
- body,p,a,td {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 12px;\r
- color: #000000;\r
- }\r
- .lh13 {\r
- line-height: 13px;\r
- }\r
- .lh15 {\r
- line-height: 15px;\r
- }\r
- pre,.prop {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 12px;\r
- color: #000000;\r
- line-height: 15px;\r
- }\r
- h1 {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 16px;\r
- color: #000000;\r
- }\r
- b.quest {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 14px;\r
- font-weight: bold;\r
- color: #000000;\r
- }\r
- a,a:link,a:visited {\r
- text-decoration: none;\r
- color: #000000;\r
- }\r
- a:hover {\r
- text-decoration: underline;\r
- color: #000000;\r
- }\r
- a:active {\r
- text-decoration: underline;\r
- color: #000000;\r
- }\r
-}\r
-</STYLE>\r
-</HEAD>\r
-\r
-\r
-<BODY BGCOLOR="#222222" LINK="#77dd11" TEXT="#cccccc" ALINK="#dddddd" VLINK="#77dd11"\r
-TOPMARGIN="0" BOTTOMMARGIN="0" LEFTMARGIN="0" RIGHTMARGIN="0" MARGINHEIGHT="0" MARGINWIDTH="0"><A NAME="top"></A>\r
-\r
-<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0" ALIGN="center">\r
-<TR>\r
- <TD NOWRAP><A HREF="index.html">termlib.js home</A></TD>\r
- <TD>|</TD>\r
- <TD NOWRAP><A HREF="multiterm_test.html">multiple terminal test</A></TD>\r
- <TD>|</TD>\r
- <TD NOWRAP><A HREF="parser_sample.html">sample parser</A></TD>\r
- <TD>|</TD>\r
- <TD NOWRAP>faq</TD>\r
- <TD>|</TD>\r
- <TD NOWRAP><A HREF="readme.txt" TITLE="readme.txt (text/plain)">documentation</A></TD>\r
-</TR>\r
-</TABLE>\r
-\r
-<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0" WIDTH="700" ALIGN="center">\r
- <TR><TD>\r
- <H1>frequently asked questions</H1>\r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- <BR>\r
- <UL>\r
- <LI CLASS="lh15"><A HREF="#chrome">Can I add chrome to the terminal? (e.g. a window header, a close box)</A></LI>\r
- <LI CLASS="lh15"><A HREF="#embed">How can I embed a terminal relative to my HTML layout?</A></LI>\r
- <LI CLASS="lh15"><A HREF="#syntax">I pasted your sample code and just got an error. - ???</A></LI>\r
- <LI CLASS="lh15"><A HREF="#keyboard">I can't get any input, but I don't get any erros too.</A></LI>\r
- <LI CLASS="lh15"><A HREF="#keylock">How can I temporary disable the keyboard handlers?</A></LI>\r
- <LI CLASS="lh15"><A HREF="#linesranges">How can I set the cusor to the start / the end of the command line?</A></LI>\r
- <LI CLASS="lh15"><A HREF="#historyunique">How can I limit the command history to unique entries only?</A></LI>\r
- <LI CLASS="lh15"><A HREF="#rebuild">How can I change my color theme on the fly?</A></LI>\r
- <LI CLASS="lh15"><A HREF="#connect">How can I connect to a server?</A></LI>\r
- </UL>\r
- </TD></TR>\r
- <TR><TD CLASS="lh13"><A NAME="chrome"></A>\r
- <BR>\r
-<B CLASS="quest">Can I add chrome to the terminal? (e.g. a window header, a close box)</B><BR><BR>\r
-\r
-Not by the means of the Terminal object's interface (since there are way too many things that you may possibly want to add).<BR>\r
-The Terminal object allows you to specify the background color, the frame color, the frame's width and the font class used. If you want to add more chrome, you must align this in a separate division element.<BR><BR>\r
-\r
-To calculate the dimensions of the terminal use this formula:<BR><BR>\r
-\r
-width: 2 * frameWidth + conf.cols * <width of > + 2 * 2px padding (left and right)<BR>\r
-height: 2 * frameWidth + conf.rows * conf.rowHeight + 2 * 2px padding (top and bottom).<BR><BR>\r
-\r
-Or you could get the empirical values for width and height by calling a terminal's `<SPAN CLASS="prop">getDimensions()</SPAN>' method, once the terminal is open. (see documentation in "readme.txt").<BR><BR>\r
-\r
-Finnally, you could obviously embed the terminal's division element in your custom chrome layout (see below). [This will not be compatible to Netscape 4.]<BR><BR>\r
-\r
-p.e.:<PRE>\r
- <div id="myTerminal1" style="position:absolute; top:100px; left:100px;">\r
- <table class="termChrome">\r
- <tbody>\r
- <tr>\r
- <td class="termTitle">terminal 1</td>\r
- </tr>\r
- <tr>\r
- <td class="termBody"><div id="termDiv1" style="position:relative"></div></td>\r
- </tr>\r
- </tbody>\r
- </table>\r
- </div>\r
-\r
- // get a terminal for this\r
-\r
- var term1 = new Terminal(\r
- {\r
- x: 0,\r
- y: 0,\r
- id: 1,\r
- termDiv: "termDiv1",\r
- handler: myTermHandler\r
- }\r
- );\r
- term1.open();\r
- \r
- // and this is how to move the chrome and the embedded terminal\r
-\r
- TermGlobals.setElementXY( "myTerminal1", 200, 80 );\r
-</PRE>\r
-To keep track of the instance for any widgets use the terminal's `id' property. (You must set this in the configuration object to a unique value for this purpose.)<BR><BR>\r
-\r
-For a demonstration see the <A HREF="chrome_sample.html">Chrome Sample Page</A>.\r
- </TD></TR>\r
- <TR><TD CLASS="lh13"><A NAME="embed"></A>\r
- <BR>\r
-<B CLASS="quest">How can I embed a terminal relative to my HTML layout?</B><BR><BR>\r
-\r
-Define your devision element with attribute "position" set to "relative" and place this inside your layout. Call "new Terminal()" with config-values { x: 0, y: 0 } to leave it at its relative origin.\r
- </TD></TR>\r
- <TR><TD CLASS="lh13"><A NAME="syntax"></A>\r
- <BR>\r
-<B CLASS="quest">I pasted your sample code and just got an error. - ???</B><BR><BR>\r
-\r
-The short examples are kept arbitrarily simple to show the syntax.<BR>\r
-Make sure that your divison element(s) is/are rendered by the browser before `Terminal.open()' is called.<BR><BR>\r
-\r
-Does not work:\r
-<PRE> <head>\r
- <script>\r
- var term = new Terminal();\r
- term.open();\r
- </script>\r
- </head>\r
-</PRE>\r
-Does work:\r
-<PRE> <head>\r
- <script>\r
- var term;\r
- \r
- function termOpen() {\r
- // to be called from outside after compile time\r
- term = new Terminal();\r
- term.open();\r
- }\r
- </script>\r
- </head>\r
-</PRE>\r
-c.f. "readme.txt"<BR>\r
-(Opening a terminal by clicking a link implies also that the page has currently focus.)<BR><BR>\r
-With v.1.01 and higher this doesn't cause an error any more.<BR>`Terminal.prototype.open()' now returns a value for success.\r
- </TD></TR>\r
- <TR><TD CLASS="lh13"><A NAME="keyboard"></A>\r
- <BR>\r
-<B CLASS="quest">I can't get any input, but I don't get any erros too.</B><BR><BR>\r
-\r
-The Terminal object's functionality relies on the browsers ability to generate and handle keyboard events.<BR>\r
-Sadly some browsers lack a full implementation of the event model. (e.g. Konquerer [khtml] and early versions of Apple Safari, which is a descendant of khtml.)\r
- </TD></TR>\r
- <TR><TD CLASS="lh13"><A NAME="keylock"></A>\r
- <BR>\r
-<B CLASS="quest">How can I temporary disable the keyboard handlers?</B><BR>\r
-<SPAN CLASS="prop">(The terminal is blocking my HTML form fields, etc.)</SPAN><BR><BR>\r
-\r
-With version 1.03 there's a global property `<SPAN CLASS="prop">TermGlobals.keylock</SPAN>'. Set this to `true' to disable the keyboard handlers without altering any other state. Reset it to `false' to continue with your terminal session(s).\r
- </TD></TR>\r
- <TR><TD CLASS="lh13"><A NAME="linesranges"></A>\r
- <BR>\r
-<B CLASS="quest">How can I set the cusor to the start / the end of the command line?</B><BR><BR>\r
-\r
-In case you need to implement a shortcut (like ^A of some UN*X-shells) to jump to the beginning or the end of the current input line, there are two private instance methods you could utilize:<BR><BR>\r
-`<SPAN CLASS="prop">_getLineEnd(<row>, <col>)</SPAN>' returns an array [<row>, <col>] with the position of the last character in the logical input line with ASCII value >= 32 (0x20).<BR><BR>\r
-`<SPAN CLASS="prop">_getLineStart(<row>, <col>)</SPAN>' returns an array [<row>, <col>] with the position of the first character in the logical input line with ASCII value >= 32 (0x20).<BR><BR>\r
-Both take a row and a column of a cursor position as arguments.<BR><BR>\r
-\r
-p.e.:\r
-<PRE>\r
- // jump to the start of the input line\r
-\r
- myCtrlHandler() {\r
- // catch ^A and jump to start of the line\r
- if (this.inputChar == 1) {\r
- var firstChar = this._getLineStart(this.r, this.c);\r
- this.cursorSet(firstChar[0], firstChar[1]);\r
- }\r
- }</PRE>\r
-(Keep in mind that this is not exactly a good example, since some browser actually don't issue a keyboard event for \r
-"^A". And other browsers, which do catch such codes, are not very reliable in that.)\r
- </TD></TR>\r
- <TR><TD CLASS="lh13"><A NAME="historyunique"></A>\r
- <BR>\r
-<B CLASS="quest">How can I limit the command history to unique entries only?</B><BR>\r
- <SPAN CLASS="prop">(My application effords commands to be commonly repeated.)</SPAN><BR><BR>\r
-\r
-With version 1.05 there is a new configuration and control flag `<SPAN CLASS="prop">historyUnique</SPAN>'. All you need is setting this to `true' in your terminal's configuration object.\r
- </TD></TR>\r
- <TR><TD CLASS="lh13"><A NAME="rebuild"></A>\r
- <BR>\r
-<B CLASS="quest">How can I change my color theme on the fly?</B><BR><BR>\r
-\r
-With version 1.07 there is a new method `<SPAN CLASS="prop">Terminal.rebuild()</SPAN>'.<BR>\r
-This method updates the GUI to current config settings while preserving all other state.<BR><BR>\r
-p.e.:\r
-<PRE>\r
- // change color settings on the fly\r
- // here: set bgColor to white and font style to class "termWhite"\r
- // method rebuild() updates the GUI without side effects\r
- // assume var term holds a referene to a Terminal object already active\r
-\r
- term.conf.bgColor = '#ffffff';\r
- term.conf.fontClass = 'termWhite';\r
- term.rebuild();</PRE>\r
- </TD></TR>\r
- <TR><TD CLASS="lh13"><A NAME="connect"></A>\r
- <BR>\r
-<B CLASS="quest">How can I connect to a server?</B><BR><BR>\r
-\r
-The Terminal object only provides an interface to handle console input and output.<BR>\r
-External connections have to be handled outside the Terminal object. You could use the XMLHttpRequest-Object (and use a communication model like AJAX or JSON) or connect via a frame or iframe element to a foreign host.<BR><BR>\r
-Handling connections is considered to be out of the realm of the "termlib.js" library.<BR>\r
-The code you need is in fact quite simple:\r
-<PRE>\r
- function connectToHost(url) {\r
- if (window.XMLHttpRequest) {\r
- request = new XMLHttpRequest();\r
- }\r
- else if (window.ActiveXObject) {\r
- request = new ActiveXObject('Microsoft.XMLHTTP');\r
- }\r
- if (request) {\r
- request.onreadystatechange = requestChangeHandler;\r
- request.open('GET', url);\r
- request.send('');\r
- }\r
- else {\r
- // XMLHttpRequest not implemented\r
- }\r
- }\r
- \r
- function requestChangeHandler() {\r
- if (request.readyState == 4) {\r
- // readyState 4: complete; now test for server's response status\r
- if (request.status == 200) {\r
- // response in request.responseText or request.responseXML if XML-code\r
- // if it's JS-code we could get this by eval(request.responseText)\r
- // by this we could import whole functions to be used via the terminal\r
- }\r
- else {\r
- // connection error\r
- // status code and message in request.status and request.statusText\r
- }\r
- }\r
- }\r
-</PRE>\r
-You should use this only together with a timer (window.setTimeout()) to handle connection timeouts.<BR>\r
-Additionally you would need some syntax to authenticate and tell the server what you want.<BR>\r
-For this purpose you could use the following methods of the XMLHttpRequest object:<BR><BR>\r
-\r
- <TABLE BORDER="0" CELLSPACING="0" CELLPADDING="3">\r
- <TR VALIGN="top"><TD NOWRAP CLASS="prop">setRequestHeader("<I>headerLabel</I>", "<I>value</I>")</TD><TD>set a HTTP header to be sent to the server</TD></TR>\r
- <TR VALIGN="top"><TD NOWRAP CLASS="prop">getResponseHeader("<I>headerLabel</I>")</TD><TD>get a HTTP header sent from the server</TD></TR>\r
- <TR VALIGN="top"><TD NOWRAP CLASS="prop">open(<I>method</I>, "<I>url</I>" [, <I>asyncFlag</I> [,<BR> "<I>userid</I>" [, "<I>password</I>"]]])</TD><TD>assign the destination properties to the request.<BR>be aware that userid and password are not encrypted!</TD></TR>\r
- <TR VALIGN="top"><TD NOWRAP CLASS="prop">send(<I>content</I>)</TD><TD>transmit a message body (post-string or DOM object)</TD></TR>\r
- <TR VALIGN="top"><TD NOWRAP CLASS="prop">abort()</TD><TD>use this to stop a pending connection</TD></TR>\r
- </TABLE>\r
-\r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- <BR>\r
- Norbert Landsteiner - August 2005<BR>\r
- <A HREF="http://www.masswerk.at/" TARGET="_blank">http://www.masswerk.at</A>\r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- <BR>\r
- <A HREF="#top">> top of page</A>\r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- \r
- </TD></TR>\r
-</TABLE>\r
-\r
-<DIV ID="termDiv" STYLE="position:absolute; top:20px; left:100px;"></DIV>\r
-\r
-</BODY>\r
-</HTML>
\ No newline at end of file
+++ /dev/null
-<HTML>\r
-<HEAD>\r
- <TITLE>mass:werk termlib</TITLE>\r
-\r
-<STYLE TYPE="text/css">\r
-body,p,a,td {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 12px;\r
- color: #cccccc;\r
-}\r
-.lh13 {\r
- line-height: 13px;\r
-}\r
-.lh15 {\r
- line-height: 15px;\r
-}\r
-pre {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 12px;\r
- color: #ccffaa;\r
- line-height: 15px;\r
-}\r
-.prop {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- color: #bbee99;\r
- font-size: 12px;\r
- line-height: 15px;\r
-}\r
-h1 {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 16px;\r
- color: #cccccc;\r
-}\r
-a,a:link,a:visited {\r
- text-decoration: none;\r
- color: #77dd11;\r
-}\r
-a:hover {\r
- text-decoration: underline;\r
- color: #77dd11;\r
-}\r
-a:active {\r
- text-decoration: underline;\r
- color: #dddddd;\r
-}\r
-\r
-@media print {\r
- body { background-color: #ffffff; }\r
- body,p,a,td {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 12px;\r
- color: #000000;\r
- }\r
- .lh13 {\r
- line-height: 13px;\r
- }\r
- .lh15 {\r
- line-height: 15px;\r
- }\r
- pre,.prop {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 12px;\r
- color: #000000;\r
- line-height: 15px;\r
- }\r
- h1 {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 16px;\r
- color: #000000;\r
- }\r
- a,a:link,a:visited {\r
- text-decoration: none;\r
- color: #000000;\r
- }\r
- a:hover {\r
- text-decoration: underline;\r
- color: #000000;\r
- }\r
- a:active {\r
- text-decoration: underline;\r
- color: #000000;\r
- }\r
-}\r
-</STYLE>\r
-</HEAD>\r
-\r
-\r
-<BODY BGCOLOR="#222222" LINK="#77dd11" TEXT="#cccccc" ALINK="#dddddd" VLINK="#77dd11"\r
-TOPMARGIN="0" BOTTOMMARGIN="0" LEFTMARGIN="0" RIGHTMARGIN="0" MARGINHEIGHT="0" MARGINWIDTH="0"><A NAME="top"></A>\r
-\r
-<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0" ALIGN="center">\r
-<TR>\r
- <TD NOWRAP>termlib.js home</TD>\r
- <TD>|</TD>\r
- <TD NOWRAP><A HREF="multiterm_test.html">multiple terminal test</A></TD>\r
- <TD>|</TD>\r
- <TD NOWRAP><A HREF="parser_sample.html">sample parser</A></TD>\r
- <TD>|</TD>\r
- <TD NOWRAP><A HREF="faq.html">faq</A></TD>\r
- <TD>|</TD>\r
- <TD NOWRAP><A HREF="readme.txt" TITLE="readme.txt (text/plain)">documentation</A></TD>\r
-</TR>\r
-</TABLE>\r
-\r
-<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0" WIDTH="700" ALIGN="center">\r
- <TR><TD>\r
- <H1>mass:werk termlib.js</H1>\r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- The JavaScript library "termlib.js" provides a `Terminal' object, which\r
- facillitates a simple and object oriented approach to generate and control a\r
- terminal-like interface for web services.<BR><BR>\r
- \r
- "termlib.js" features direct keyboard input and powerful output methods\r
- for multiple and simultanious instances of the `Terminal' object.<BR><BR>\r
- \r
- The library was written with the aim of simple usage and a maximum of compatibility\r
- with minimal foot print in the global namespace.<BR><BR><BR>\r
- \r
- \r
- A short example:<BR>\r
- <PRE>\r
- var term = new Terminal( {handler: termHandler} );\r
- term.open();\r
-\r
- function termHandler() {\r
- this.newLine();\r
- var line = this.lineBuffer;\r
- if (line != "") {\r
- this.write("You typed: "+line);\r
- }\r
- this.prompt();\r
- }\r
- </PRE>\r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- <B>License</B><BR><BR>\r
-\r
- This JavaScript-library is <U>free for private and academic use</U>.\r
- Please include a readable copyright statement and a backlink to <http://www.masswerk.at> in the\r
- web page. The library should always be accompanied by the "readme.txt" and the sample HTML-documents.<BR><BR>\r
-\r
- The term "private use" includes any personal or non-commercial use, which is not related\r
- to commercial activites, but excludes intranet, extranet and/or public net applications\r
- that are related to any kind of commercial or profit oriented activity.<BR><BR>\r
-\r
- For commercial use see <<A HREF="http://www.masswerk.at/" TARGET="_blank">http://www.masswerk.at</A>> for contact information.\r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- <B>Distribution</B><BR><BR>\r
-\r
- This JavaScript-library may be distributed freely as long it is distributed together with the "readme.txt" and the sample HTML-documents and this document.<BR><BR>\r
-\r
- Any changes to the library should be commented and be documented in the readme-file.<BR>\r
- Any changes must be reflected in the `Terminal.version' string as "Version.Subversion (compatibility)".\r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- <B>Disclaimer</B><BR><BR>\r
-\r
- This software is distributed AS IS and in the hope that it will be useful, but WITHOUT ANY\r
- WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
- PURPOSE. The entire risk as to the quality and performance of the product is borne by the\r
- user. No use of the product is authorized hereunder except under this disclaimer.\r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- <B>History</B><BR><BR>\r
-\r
- This library evolved from the terminal script "TermApp" ((c) N. Landsteiner 2003) and is in its\r
- current form a down scaled spinn-off of the "JS/UIX" project. (JS/UIX is not a free software by now.)\r
- c.f.: <<A HREF="http://www.masswerk.at/jsuix/" TARGET="_blank">http://www.masswerk.at/jsuix</A>><BR><BR>\r
-\r
- For version history: see the <A HREF="readme.txt">readme.txt</A>.\r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- <BR>\r
- <B>Download</B><BR><BR>\r
- Be sure to have read the license information and the disclamer and that you are willing to respect copyrights.<BR><BR>\r
-\r
- <SPAN CLASS="prop">Download:</SPAN> <A HREF="termlib.zip">termlib.zip</A> (~ 40 KB, incl. docs)<BR><BR>\r
- Current version is "1.07 (original)".<BR>\r
- The files are now provided with line breaks in format <CRLF>.<BR>\r
- \r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- <B>Author</B><BR><BR>\r
- © Norbert Landsteiner 2003-2005<BR>\r
- mass:werk – media environments<BR>\r
- <A HREF="http://www.masswerk.at/" TARGET="_blank">http://www.masswerk.at</A>\r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- <BR>\r
- Author's note:<BR>\r
- Please do not contact me on questions of simple usage. There is an extensive documentation (readme.txt) including plenty of sample code that should provide all information you need.\r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- <BR>\r
- <A HREF="#top">> top of page</A>\r
- </TD></TR>\r
- <TR><TD CLASS="lh13">\r
- \r
- </TD></TR>\r
-</TABLE>\r
-\r
-<DIV ID="termDiv" STYLE="position:absolute; top:20px; left:100px;"></DIV>\r
-\r
-</BODY>\r
-</HTML>
\ No newline at end of file
+++ /dev/null
-<HTML>\r
-<HEAD>\r
- <TITLE>termlib Multiple Terminal Test</TITLE>\r
- <SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript" SRC="termlib.js"></SCRIPT>\r
-\r
-<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">\r
-<!--\r
-\r
-/*\r
- multiple terminal test for termlib.js\r
-\r
- (c) Norbert Landsteiner 2003-2005\r
- mass:werk - media environments\r
- <http://www.masswerk.at>\r
-\r
-*/\r
-\r
-var term=new Array();\r
-\r
-var helpPage=[\r
- '%CS%+r Terminal Help %-r%n',\r
- ' This is just a tiny test for multiple terminals.',\r
- ' use one of the following commands:',\r
- ' clear .... clear the terminal',\r
- ' exit ..... close the terminal (or <ESC>)',\r
- ' id ....... show terminal\'s id',\r
- ' switch ... switch to other terminal',\r
- ' help ..... show this help page',\r
- ' other input will be echoed to the terminal.',\r
- ' '\r
-];\r
-\r
-function termOpen(n) {\r
- if (!term[n]) {\r
- var y=(n==1)? 70: 280;\r
- term[n]=new Terminal(\r
- {\r
- x: 220,\r
- y: y,\r
- rows: 12,\r
- greeting: '%+r +++ Terminal #'+n+' ready. +++ %-r%nType "help" for help.%n',\r
- id: n,\r
- termDiv: 'termDiv'+n,\r
- crsrBlinkMode: true,\r
- handler: termHandler,\r
- exitHandler: termExitHandler\r
- }\r
- );\r
- if (term[n]) term[n].open();\r
- }\r
- else if (term[n].closed) {\r
- term[n].open();\r
- }\r
- else {\r
- term[n].focus();\r
- }\r
-}\r
-\r
-function termHandler() {\r
- // called on <CR> or <ENTER>\r
- this.newLine();\r
- var cmd=this.lineBuffer;\r
- if (cmd!='') {\r
- if (cmd=='switch') {\r
- var other=(this.id==1)? 2:1;\r
- termOpen(other);\r
- }\r
- else if (cmd=='clear') {\r
- this.clear();\r
- }\r
- else if (cmd=='exit') {\r
- this.close();\r
- }\r
- else if (cmd=='help') {\r
- this.write(helpPage);\r
- }\r
- else if (cmd=='id') {\r
- this.write('terminal id: '+this.id);\r
- }\r
- else {\r
- this.type('You typed: '+cmd);\r
- this.newLine();\r
- }\r
- }\r
- this.prompt();\r
-}\r
-\r
-function termExitHandler() {\r
- // optional handler called on exit\r
- // activate other terminal if open\r
- var other=(this.id==1)? 2:1;\r
- if ((term[other]) && (term[other].closed==false)) term[other].focus();\r
-}\r
-\r
-//-->\r
-</SCRIPT>\r
-\r
-<STYLE TYPE="text/css">\r
-body,p,a,td {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 12px;\r
- color: #cccccc;\r
-}\r
-.lh15 {\r
- line-height: 15px;\r
-}\r
-.term {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 12px;\r
- color: #33d011;\r
- background: none;\r
-}\r
-.termReverse {\r
- color: #111111;\r
- background: #33d011;\r
-}\r
-a,a:link,a:visited {\r
- text-decoration: none;\r
- color: #77dd11;\r
-}\r
-a:hover {\r
- text-decoration: underline;\r
- color: #77dd11;\r
-}\r
-a:active {\r
- text-decoration: underline;\r
- color: #dddddd;\r
-}\r
-\r
-a.termopen,a.termopen:link,a.termopen:visited {\r
- text-decoration: none;\r
- color: #77dd11;\r
- background: none;\r
-}\r
-a.termopen:hover {\r
- text-decoration: none;\r
- color: #222222;\r
- background: #77dd11;\r
-}\r
-a.termopen:active {\r
- text-decoration: none;\r
- color: #222222;\r
- background: #dddddd;\r
-}\r
-\r
-</STYLE>\r
-</HEAD>\r
-\r
-\r
-<BODY BGCOLOR="#222222" LINK="#77dd11" TEXT="#cccccc" ALINK="#dddddd" VLINK="#77dd11"\r
-TOPMARGIN="0" BOTTOMMARGIN="0" LEFTMARGIN="0" RIGHTMARGIN="0" MARGINHEIGHT="0" MARGINWIDTH="0">\r
-\r
-<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0" ALIGN="center">\r
-<TR>\r
- <TD NOWRAP><A HREF="index.html">termlib.js home</A></TD>\r
- <TD>|</TD>\r
- <TD NOWRAP>multiple terminal test</TD>\r
- <TD>|</TD>\r
- <TD NOWRAP><A HREF="parser_sample.html">sample parser</A></TD>\r
- <TD>|</TD>\r
- <TD NOWRAP><A HREF="faq.html">faq</A></TD>\r
- <TD>|</TD>\r
- <TD NOWRAP><A HREF="readme.txt" TITLE="readme.txt (text/plain)">documentation</A></TD>\r
-</TR>\r
-</TABLE>\r
-\r
-<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0">\r
- <TR><TD NOWRAP>\r
- Multiple Terminal Test<BR> \r
- </TD></TR>\r
- <TR><TD NOWRAP>\r
- <A HREF="javascript:termOpen(1)" onfocus="if(this.blur)this.blur();" onmouseover="window.status='terminal 1'; return true" onmouseout="window.status=''; return true" CLASS="termopen">> open terminal 1 </A>\r
- </TD></TR>\r
- <TR><TD NOWRAP>\r
- <A HREF="javascript:termOpen(2)" onfocus="if(this.blur)this.blur();" onmouseover="window.status='terminal 2'; return true" onmouseout="window.status=''; return true" CLASS="termopen">> open terminal 2 </A>\r
- </TD></TR>\r
- <TR><TD NOWRAP CLASS="lh15">\r
- <BR>\r
- (c) mass:werk,<BR>N. Landsteiner 2003-2005<BR>\r
- <A HREF="http://www.masswerk.at/" TARGET="_blank">http://www.masswerk.at</A>\r
- </TD></TR>\r
-</TABLE>\r
-\r
-<DIV ID="termDiv1" STYLE="position:absolute; top:20px; left:100px;"></DIV>\r
-<DIV ID="termDiv2" STYLE="position:absolute; top:20px; left:100px;"></DIV>\r
-\r
-</BODY>\r
-</HTML>
\ No newline at end of file
+++ /dev/null
-<HTML>\r
-<HEAD>\r
- <TITLE>termlib Sample Parser</TITLE>\r
- <SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript" SRC="termlib.js"></SCRIPT>\r
- <SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript" SRC="termlib_parser.js"></SCRIPT>\r
-\r
-<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">\r
-<!--\r
-\r
-/*\r
- test sample for termlib.js and termlib_parser.js\r
-\r
- (c) Norbert Landsteiner 2005\r
- mass:werk - media environments\r
- <http://www.masswerk.at>\r
-\r
-*/\r
-\r
-var term;\r
-\r
-var helpPage=[\r
- '%CS%+r Terminal Help %-r%n',\r
- ' This is just a sample to demonstrate command line parsing.',\r
- ' ',\r
- ' Use one of the following commands:',\r
- ' clear [-a] .......... clear the terminal',\r
- ' option "a" also removes the status line',\r
- ' number -n<value> .... return value of option "n" (test for options)',\r
- ' repeat -n<value> .... repeats the first argument n times (another test)',\r
- ' login <username> .... sample login (test for raw mode)',\r
- ' exit ................ close the terminal (same as <ESC>)',\r
- ' help ................ show this help page',\r
- ' ',\r
- ' other input will be echoed to the terminal as a list of parsed arguments',\r
- ' in the format <argument index> <quoting level> "<parsed value>".',\r
- ' '\r
-];\r
-\r
-function termOpen() {\r
- if (!term) {\r
- term=new Terminal(\r
- {\r
- x: 220,\r
- y: 70,\r
- termDiv: 'termDiv',\r
- ps: '[guest]$',\r
- initHandler: termInitHandler,\r
- handler: commandHandler\r
- }\r
- );\r
- if (term) term.open();\r
- }\r
- else if (term.closed) {\r
- term.open();\r
- }\r
- else {\r
- term.focus();\r
- }\r
-}\r
-\r
-function termInitHandler() {\r
- // output a start up screen\r
- this.write(\r
- [\r
- TermGlobals.center('####################################################', 80),\r
- TermGlobals.center('# #', 80),\r
- TermGlobals.center('# termlib.js - Sample Parser #', 80),\r
- TermGlobals.center('# Input is echoed as a list of parsed arguments. #', 80),\r
- TermGlobals.center('# #', 80),\r
- TermGlobals.center('# Type "help" for commands. #', 80),\r
- TermGlobals.center('# #', 80),\r
- TermGlobals.center('# (c) N. Landsteiner 2005; www.masswerk.at #', 80),\r
- TermGlobals.center('# #', 80),\r
- TermGlobals.center('####################################################', 80),\r
- '%n'\r
- ]\r
- );\r
- // set a double status line\r
- this.statusLine('', 8,2); // just a line of strike\r
- this.statusLine(' +++ This is just a test sample for command parsing. Type "help" for help. +++');\r
- this.maxLines -= 2;\r
- // and leave with prompt\r
- this.prompt();\r
-}\r
-\r
-function commandHandler() {\r
- this.newLine();\r
- // check for raw mode first (should not be parsed)\r
- if (this.rawMode) {\r
- if (this.env.getPassword) {\r
- // sample password handler (lineBuffer == stored username ?)\r
- if (this.lineBuffer == this.env.username) {\r
- this.user = this.env.username;\r
- this.ps = '['+this.user+']>';\r
- }\r
- else {\r
- this.type('Sorry.');\r
- }\r
- this.env.username = '';\r
- this.env.getPassword = false;\r
- }\r
- // leave in normal mode\r
- this.rawMode = false;\r
- this.prompt();\r
- return;\r
- }\r
- // normal command parsing\r
- // just call the termlib_parser with a reference of the calling Terminal instance\r
- // parsed arguments will be imported in this.argv,\r
- // quoting levels per argument in this.argQL (quoting character or empty)\r
- // cursor for arguments is this.argc (used by parserGetopt)\r
- // => see 'termlib_parse.js' for configuration and details\r
- parseLine(this);\r
- if (this.argv.length == 0) {\r
- // no commmand line input\r
- }\r
- else if (this.argQL[0]) {\r
- // first argument quoted -> error\r
- this.write("Syntax error: first argument quoted.");\r
- }\r
- else {\r
- var cmd = this.argv[this.argc++];\r
- /*\r
- process commands now\r
- 1st argument: this.argv[this.argc]\r
- */\r
- if (cmd == 'help') {\r
- this.write(helpPage);\r
- }\r
- else if (cmd == 'clear') {\r
- // get options\r
- var opts = parserGetopt(this, 'aA');\r
- if (opts.a) {\r
- // discard status line on opt "a" or "A"\r
- this.maxLines = this.conf.rows;\r
- }\r
- this.clear();\r
- }\r
- else if (cmd == 'number') {\r
- // test for value options\r
- var opts = parserGetopt(this, 'n');\r
- if (opts.illegals.length) this.type('illegal option. usage: number -n<value>')\r
- else if ((opts.n) && (opts.n.value != -1)) this.type('option value: '+opts.n.value)\r
- else this.type('usage: number -n<value>');\r
- }\r
- else if (cmd == 'repeat') {\r
- // another test for value options\r
- var opts = parserGetopt(this, 'n');\r
- if (opts.illegals.length) this.type('illegal option. usage: repeat -n<value> <string>')\r
- else if ((opts.n) && (opts.n.value != -1)) {\r
- // first normal argument is again this.argv[this.argc]\r
- var s = this.argv[this.argc];\r
- if (typeof s != 'undefined') {\r
- // repeat this string n times\r
- var a = [];\r
- for (var i=0; i<opts.n.value; i++) a[a.length] = s;\r
- this.type(a.join(' '));\r
- }\r
- }\r
- else this.type('usage: repeat -n<value> <string>');\r
- }\r
- else if (cmd == 'login') {\r
- // sample login (test for raw mode)\r
- if ((this.argc == this.argv.length) || (this.argv[this.argc] == '')) {\r
- this.type('usage: login <username>');\r
- }\r
- else {\r
- this.env.getPassword = true;\r
- this.env.username = this.argv[this.argc];\r
- this.write('%+iSample login: repeat username as password.%-i%n');\r
- this.type('password: ');\r
- // exit in raw mode (blind input)\r
- this.rawMode = true;\r
- this.lock = false;\r
- return;\r
- }\r
- }\r
- else if (cmd == 'exit') {\r
- this.close();\r
- return;\r
- }\r
- else {\r
- // for test purpose just output argv as list\r
- // assemble a string of style-escaped lines and output it in more-mode\r
- s=' INDEX QL ARGUMENT%n';\r
- for (var i=0; i<this.argv.length; i++) {\r
- s += TermGlobals.stringReplace('%', '%%',\r
- TermGlobals.fillLeft(i, 6) +\r
- TermGlobals.fillLeft((this.argQL[i])? this.argQL[i]:'-', 4) +\r
- ' "' + this.argv[i] + '"'\r
- ) + '%n';\r
- }\r
- this.write(s, 1);\r
- return;\r
- }\r
- }\r
- this.prompt();\r
-}\r
-\r
-\r
-//-->\r
-</SCRIPT>\r
-\r
-<STYLE TYPE="text/css">\r
-body,p,a,td {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 12px;\r
- color: #cccccc;\r
-}\r
-.lh15 {\r
- line-height: 15px;\r
-}\r
-.term {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 12px;\r
- color: #33d011;\r
- background: none;\r
-}\r
-.termReverse {\r
- color: #111111;\r
- background: #33d011;\r
-}\r
-a,a:link,a:visited {\r
- text-decoration: none;\r
- color: #77dd11;\r
-}\r
-a:hover {\r
- text-decoration: underline;\r
- color: #77dd11;\r
-}\r
-a:active {\r
- text-decoration: underline;\r
- color: #dddddd;\r
-}\r
-\r
-a.termopen,a.termopen:link,a.termopen:visited {\r
- text-decoration: none;\r
- color: #77dd11;\r
- background: none;\r
-}\r
-a.termopen:hover {\r
- text-decoration: none;\r
- color: #222222;\r
- background: #77dd11;\r
-}\r
-a.termopen:active {\r
- text-decoration: none;\r
- color: #222222;\r
- background: #dddddd;\r
-}\r
-\r
-</STYLE>\r
-</HEAD>\r
-\r
-\r
-<BODY BGCOLOR="#222222" LINK="#77dd11" TEXT="#cccccc" ALINK="#dddddd" VLINK="#77dd11"\r
-TOPMARGIN="0" BOTTOMMARGIN="0" LEFTMARGIN="0" RIGHTMARGIN="0" MARGINHEIGHT="0" MARGINWIDTH="0">\r
-\r
-<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0" ALIGN="center">\r
-<TR>\r
- <TD NOWRAP><A HREF="index.html">termlib.js home</A></TD>\r
- <TD>|</TD>\r
- <TD NOWRAP><A HREF="multiterm_test.html">multiple terminal test</A></TD>\r
- <TD>|</TD>\r
- <TD NOWRAP>sample parser</TD>\r
- <TD>|</TD>\r
- <TD NOWRAP><A HREF="faq.html">faq</A></TD>\r
- <TD>|</TD>\r
- <TD NOWRAP><A HREF="readme.txt" TITLE="readme.txt (text/plain)">documentation</A></TD>\r
-</TR>\r
-</TABLE>\r
-\r
-<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0">\r
- <TR><TD NOWRAP>\r
- Sample Parser Test<BR> \r
- </TD></TR>\r
- <TR><TD NOWRAP>\r
- <A HREF="javascript:termOpen()" onfocus="if(this.blur)this.blur();" onmouseover="window.status='terminal 1'; return true" onmouseout="window.status=''; return true" CLASS="termopen">> open terminal </A>\r
- </TD></TR>\r
- <TR><TD NOWRAP>\r
- \r
- </TD></TR>\r
- <TR><TD NOWRAP CLASS="lh15">\r
- <BR>\r
- (c) mass:werk,<BR>N. Landsteiner 2003-2005<BR>\r
- <A HREF="http://www.masswerk.at/" TARGET="_blank">http://www.masswerk.at</A>\r
- </TD></TR>\r
-</TABLE>\r
-\r
-<DIV ID="termDiv" STYLE="position:absolute;"></DIV>\r
-\r
-</BODY>\r
-</HTML>
\ No newline at end of file
+++ /dev/null
-**** mass:werk termlib.js - JS-WebTerminal Object v1.07 ****\r
-\r
- (c) Norbert Landsteiner 2003-2005\r
- mass:werk - media environments\r
- <http://www.masswerk.at>\r
-\r
-\r
-\r
-\r
-Contents:\r
-\r
- 1 About\r
- 2 Creating a new Terminal Instance\r
- 2.1 Configuration Values\r
- 3 Using the Terminal\r
- 3.1 The Default Handler\r
- 3.2 Input Modes\r
- 3.2.1 Normal Line Input (Command Line Mode)\r
- 3.2.1.2 Special Keys (ctrlHandler)\r
- 3.2.2 Raw Mode\r
- 3.2.3 Character Mode\r
- 3.3 Other Handlers\r
- 3.3.1 initHandler\r
- 3.3.2 exitHandler\r
- 3.4 Flags for Behaviour Control\r
- 4 Output Methods\r
- 4.1 Terminal.type()\r
- 4.2 Terminal.write()\r
- 4.3 Terminal.typeAt()\r
- 4.4 Terminal.setChar()\r
- 4.5 Terminal.newLine()\r
- 4.6 Terminal.clear()\r
- 4.7 Terminal.statusLine()\r
- 4.8 Terminal.printRowFromString()\r
- 4.9 Terminal.redraw()\r
- 5 Cursor Methods and Editing\r
- 5.1 Terminal.cursorOn()\r
- 5.2 Terminal.cursorOff()\r
- 5.3 Terminal.cursorSet()\r
- 5.4 Terminal.cursorLeft()\r
- 5.5 Terminal.cursorRight()\r
- 5.6 Terminal.backspace()\r
- 5.7 Terminal.fwdDelete()\r
- 5.8 Terminal.isPrintable()\r
- 6 Other Methods of the Terminal Object\r
- 6.1 Terminal.prompt()\r
- 6.2 Terminal.reset()\r
- 6.3 Terminal.open()\r
- 6.4 Terminal.close()\r
- 6.5 Terminal.focus()\r
- 6.6 Terminal.moveTo()\r
- 6.7 Terminal.resizeTo()\r
- 6.8 Terminal.getDimensions()\r
- 6.9 Terminal.rebuild()\r
- 7 Global Static Methods (TermGlobals)\r
- 7.1 TermGlobals.setFocus()\r
- 7.2 TermGlobals.keylock (Global Locking Flag)\r
- 7.3 TermGlobalsText Methods\r
- 7.3.1 TermGlobals.normalize()\r
- 7.3.2 TermGlobals.fillLeft()\r
- 7.3.3 TermGlobals.center()\r
- 7.3.4 TermGlobals.stringReplace()\r
- 8 Localization\r
- 9 Cross Browser Functions\r
- 10 Architecture, Internals\r
- 10.1 Global Entities\r
- 10.2 I/O Architecture\r
- 10.3 Compatibility\r
- 11 History\r
- 12 Example for a Command Line Parser\r
- 13 License\r
- 14 Disclaimer\r
- 15 References\r
-\r
-\r
-\r
-\r
-1 About\r
-\r
-The Terminal library "termlib.js" provides an object oriented constructor and control\r
-methods for a terminal-like DHTML interface.\r
-\r
-"termlib.js" features direct keyboard input and powerful output methods for multiple\r
-instances of the `Terminal' object (including focus control).\r
-\r
-The library was written with the aim of simple usage and a maximum of compatibility with\r
-minimal foot print in the global namespace.\r
-\r
-\r
-A simple example:\r
-\r
- // creating a terminal and using it\r
-\r
- var term = new Terminal( {handler: termHandler} );\r
- term.open();\r
-\r
- function termHandler() {\r
- var line = this.lineBuffer;\r
- this.newLine();\r
- if (line == "help") {\r
- this.write(helpPage)\r
- }\r
- else if (line == "exit") {\r
- this.close();\r
- return;\r
- }\r
- else if (line != "") {\r
- this.write("You typed: "+line);\r
- }\r
- this.prompt();\r
- }\r
-\r
- var helpPage = [\r
- "This is the monstrous help page for my groovy terminal.",\r
- "Commands available:",\r
- " help ... print this monstrous help page",\r
- " exit ... leave this groovy terminal",\r
- " ",\r
- "Have fun!"\r
- ];\r
-\r
-\r
-You should provide CSS font definitions for the classes ".term" (normal video) and\r
-".termReverse" (reverse video) in a monospaced font.\r
-A sample stylesheet "term_styles.css" comes with this library.\r
-\r
-See the sample application "multiterm_test.html" for a demo of multiple terminals.\r
-\r
-v.1.01: If you configure to use another font class (see 2.1 Configuration Values),\r
- you must provide a subclass ".termReverse" for reversed video.\r
-\r
- p.e.: .myFontClass .termReverse {\r
- /* your definitions for reverse video here */\r
- }\r
- \r
- With the addition of `conf.fontClass' you can now create multiple\r
- instances with independend appearences.\r
-\r
-\r
-\r
-\r
-2 Creating a new Terminal Instance\r
-\r
-Use the `new' constructor to create a new instance of the Terminal object. You will want\r
-to supply a configuration object as an argument to the constructor. If the `new'\r
-constructor is called without an object as its first argument, default values are used.\r
-\r
-p.e.:\r
-\r
- // creating a new instance of Terminal\r
-\r
- var conf= {\r
- x: 100,\r
- y: 100,\r
- cols: 80,\r
- rows: 24\r
- }\r
-\r
- var term = new Term(conf);\r
- term.open();\r
-\r
-`Terminal.open()' initializes the terminal and makes it visible to the user.\r
-This is handled in by separate method to allow the re-initilization of instances\r
-previously closed.\r
-\r
-NOTE:\r
-The division element (or NS-layer) that holds the terminal must be present when calling\r
-`Terminal.open()'. So you must not call this method from the header of a HTML-document at\r
-compile time.\r
-\r
-\r
-\r
-2.1 Configuration Values\r
-\r
-Set any of these values in your configuration object to override:\r
-\r
- \r
- LABEL DEFAULT VALUE COMMENT\r
- \r
- x 100 terminal's position x in px\r
- y 100 terminal's position y in px\r
- divDiv 'termDiv' id of terminals CSS division\r
- bgColor '#181818' background color (HTML hex value)\r
- frameColor '#555555' frame color (HTML hex value)\r
- frameWidth 1 frame border width in px\r
- fontClass 'term' class name of CSS font definition to use\r
- cols 80 number of cols per row\r
- rows 24 number of rows\r
- rowHeight 15 a row's line-height in px\r
- blinkDelay 500 delay for cursor blinking in milliseconds\r
- crsrBlinkMode false true for blinking cursor\r
- crsrBlockMode true true for block-cursor else underscore\r
- DELisBS false handle <DEL> as <BACKSPACE>\r
- printTab true handle <TAB> as printable (prints as space)\r
- printEuro true handle unicode 0x20AC (Euro sign) as printable\r
- catchCtrlH true handle ^H as <BACKSPACE>\r
- closeOnESC true close terminal on <ESC>\r
- historyUnique false prevent consecutive and identical entries in history\r
- id 0 terminal id\r
- ps '>' prompt string\r
- greeting '%+r Terminal ready. %-r' string for greeting if no initHandler is used\r
- handler termDefaultHandler reference to handler for command interpretation\r
- ctrlHandler null reference to handler called on uncatched special keys\r
- initHandler null reference to handler called at end of init()\r
- exitHandler null reference to handler called on close()\r
-\r
-\r
-At least you will want to specify `handler' to implement your own command parser.\r
-\r
-Note: While `id' is not used by the Termninal object, it provides an easy way to identify\r
-multiple terminals by the use of "this.id". (e.g.: "if (this.id == 1) startupterm = true;")\r
-\r
-p.e.:\r
-\r
- // creating two individual Terminal instances\r
-\r
- var term1 = new Terminal(\r
- {\r
- id: 1,\r
- x: 200,\r
- y: 10,\r
- cols: 80,\r
- rows: 12,\r
- greeting: "*** This is Terminal 1 ***",\r
- handler: myTerminalHandler\r
- }\r
- );\r
- term1.open();\r
-\r
- var term2 = new Terminal(\r
- {\r
- id: 2,\r
- x, 200,\r
- y: 220,\r
- cols: 80\r
- rows: 12,\r
- greeting: "*** This is Terminal 2 ***",\r
- handler: myTerminalHandler\r
- }\r
- );\r
- term2.open();\r
-\r
-\r
-\r
-\r
-3 Using the Terminal\r
-\r
-There are 4 different handlers that are called by a Terminal instance to process input and\r
-some flags to control the input mode and behaviour.\r
-\r
-\r
-\r
-3.1 The Default Handler (a simlple example for input handling)\r
-\r
-If no handlers are defined in the configuration object, a default handler is called to\r
-handle a line of user input. The default command line handler `termDefaultHandler' just\r
-closes the command line with a new line and echos the input back to the user:\r
-\r
- function termDefaultHandler() {\r
- this.newLine();\r
- if (this.lineBuffer != '') {\r
- this.type('You typed: '+this.lineBuffer);\r
- this.newLine();\r
- }\r
- this.prompt();\r
- }\r
-\r
-First you may note that the instance is refered to as `this'. So you need not worry about\r
-which Terminal instance is calling your handler. As the handler is entered, the terminal\r
-is locked for user input and the cursor is off. The current input is available as a string\r
-value in `this.lineBuffer'.\r
-\r
-The method `type(<text>)' just does what it says and types a string at the current cursor\r
-position to the terminal screen.\r
-\r
-`newLine()' moves the cursor to a new line.\r
-\r
-The method `prompt()' adds a new line if the cursor isn't at the start of a line, outputs\r
-the prompt string (as specified in the configuration), activates the cursor, and unlocks\r
-the terminal for further input. While you're doing normal command line processing, always\r
-call `prompt()' when leaving your handler.\r
-\r
-In fact this is all you need to create your own terminal application. Please see at least\r
-the method `write()' for a more powerful output method.\r
-\r
-Below we will refer to all methods of the Terminal object as `Terminal.<method>()'.\r
-You can call them as `this.<method>()' in a handler or as methods of your named instance\r
-in other context (e.g.: "myTerminal.close()").\r
-\r
-[In technical terms these methods are methods of the Terminal's prototype object, while\r
-the properties are properties of a Termninal instance. Since this doesn't make any\r
-difference to your script, we'll refer to both as `Terminal.<method-or-property>'.]\r
-\r
-\r
-\r
-3.2 Input Modes\r
-\r
-3.2.1 Normal Line Input (Command Line Mode)\r
-\r
-By default the terminal is in normal input mode. Any printable characters in the range of\r
-ASCII 0x20 - 0xff are echoed to the terminal and may be edited with the use of the cursor\r
-keys and the <BACKSPACE> key.\r
-The cursor keys UP and DOWN let the user browse in the command line history (the list of\r
-all commands issued previously in this Terminal instance).\r
-\r
-If the user presses <CR> or <ENTER>, the line is read from the terminal buffer, converted\r
-to a string, and placed in `Terminal.lineBuffer' (-> `this.lineBuffer') for further use.\r
-The terminal is then locked for further input and the specified handler\r
-(`Terminal.handler') is called.\r
-\r
-\r
-3.2.1.2 Special Keys (ctrlHandler)\r
-\r
-If a special character (ASCII<0x20) or an according combination of <CTRL> and a key is\r
-pressed, which is not caught for editing or "enter", and a handler for `ctrlHandler' is\r
-specified, this handler is called.\r
-The ASCII value of the special character is available in `Terminal.inputChar'. Please note\r
-that the terminal is neither locked, nor is the cursor off - all further actions have to\r
-be controlled by `ctrlHandler'. (The tracking of <CTRL>-<key> combinations as "^C" usually\r
-works but cannot be taken for granted.)\r
-\r
-A named reference of the special control values in POSIX form (as well as the values of\r
-the cursor keys [LEFT, RIGHT, UP, DOWN]) is available in the `termKey' object.\r
-\r
-p.e.:\r
-\r
- // a simple ctrlHandler\r
-\r
- function myCtrlHandler() {\r
- if (this.inputChar == termKey.ETX) {\r
- // exit on ^C (^C == ASCII 0x03 == <ETX>)\r
- this.close();\r
- }\r
- }\r
-\r
-If no `ctrlHandler' is specified, control keys are ignored (default).\r
-\r
-\r
-3.2.2 Raw Mode\r
-\r
-If the flag `Terminal.rawMode' is set to a value evaluating to `true', no special keys are\r
-tracked but <CR> and <ENTER> (and <ESC>, if the flag `Terminal.closeOnESC' is set).\r
-The input is NOT echoed to the terminal. All printable key values [0x20-0xff] are\r
-transformed to characters and added to `Terminal.lineBuffer' sequentially. The command\r
-line input is NOT added to the history.\r
-\r
-This mode is especially suitable for password input.\r
-\r
-p.e.:\r
-\r
- // using raw mode for password input\r
-\r
- function myTermHandler() {\r
- this.newLine();\r
- // we stored a flag in Terminal.env to track the status\r
- if (this.env.getpassword) {\r
- // leave raw mode\r
- this.rawMode = false;\r
- if (passwords[this.env.user] == this.lineBuffer) {\r
- // matched\r
- this.type('Welcome '+this.env.user);\r
- this.env.loggedin = true;\r
- }\r
- else {\r
- this.type('Sorry.');\r
- }\r
- this.env.getpassword = false;\r
- }\r
- else {\r
- // simple parsing\r
- var args = this.lineBuffer.split(' ');\r
- var cmd = args[0];\r
- if (cmd == 'login') {\r
- var user = args[1];\r
- if (!user) {\r
- this.type('usage: login <username>');\r
- }\r
- else {\r
- this.env.user = user;\r
- this.env.getpassword = true;\r
- this.type('password? ');\r
- // enter raw mode\r
- this.rawMode = true;\r
- // leave without prompt so we must unlock first\r
- this.lock = false;\r
- return;\r
- }\r
- }\r
- /*\r
- other actions ...\r
- */\r
- }\r
- this.prompt();\r
- }\r
-\r
-In this example a handler is set up to process the command "login <username>" and ask for\r
-a password for the given user name in raw mode. Note the use of the object `Terminal.env'\r
-which is just an empty object set up at the creation of the Terminal instance. Its only\r
-purpose is to provide an individual namespace for private data to be stored by a Terminal\r
-instance.\r
-\r
-NOTE: The flag `Terminal.lock' is used to control the keyboard locking. If we would not\r
-set this to `false' before leaving in raw mode, we would be caught in dead-lock, since no\r
-input could be entered and our handler wouldn't be called again. - A dreadful end of our\r
-terminal session.\r
-\r
-NOTE: Raw mode utilizes the property `Terminal.lastLine' to collect the input string.\r
-This is normally emty, when a handler is called. This is not the case if your script left\r
-the input process on a call of ctrlHandler. You should clear `Terminal.lastLine' in such\r
-a case, if you're going to enter raw mode immediatly after this.\r
-\r
-\r
-3.2.3 Character Mode\r
-\r
-If the flag `Terminal.charMode' is set to a value evaluating to `true', the terminal is in\r
-character mode. In this mode the numeric ASCII value of the next key typed is stored in\r
-`Terminal.inputChar'. The input is NOT echoed to the terminal. NO locking or cursor\r
-control is performed and left to the handler.\r
-You can use this mode to implement your editor or a console game.\r
-`Terminal.charMode' takes precedence over `Terminal.rawMode'.\r
-\r
-p.e.: \r
-\r
- // using char mode\r
-\r
- function myTermHandler() {\r
- // this is the normal handler\r
- this.newLine();\r
- // simple parsing\r
- var args = this.lineBuffer.split(' ');\r
- var cmd = args[0];\r
- if (cmd == 'edit') {\r
- // init the editor\r
- myEditor(this);\r
- // redirect the handler to editor\r
- this.handler = myEditor;\r
- // leave in char mode\r
- this.charMode = true;\r
- // show cursor\r
- this.cursorOn();\r
- // don't forget unlocking\r
- this.lock = false;\r
- return;\r
- }\r
- /*\r
- other actions ...\r
- */\r
- this.prompt();\r
- }\r
-\r
- function myEditor(initterm) {\r
- // our dummy editor (featuring modal behaviour)\r
- if (initterm) {\r
- // perform initialization tasks\r
- initterm.clear();\r
- initterm.write('this is a simple test editor; leave with <ESC> then "q"%n%n');\r
- initterm.env.mode = '';\r
- // store a reference of the calling handler\r
- initterm.env.handler = initterm.handler;\r
- return;\r
- }\r
- // called as handler -> lock first\r
- this.lock=true;\r
- // hide cursor\r
- this.cursorOff();\r
- var key = this.inputChar;\r
- if (this.env.mode == 'ctrl') {\r
- // control mode\r
- if (key == 113) {\r
- // "q" => quit\r
- // leave charMode and reset the handler to normal\r
- this.charMode = false;\r
- this.handler = this.env.handler;\r
- // clear the screen\r
- this.clear();\r
- // prompt and return\r
- this.prompt();\r
- return;\r
- }\r
- else {\r
- // leave control mode\r
- this.env.mode = '';\r
- }\r
- }\r
- else {\r
- // edit mode\r
- if (key == termKey.ESC) {\r
- // enter control mode\r
- // we'd better indicate this in a status line ...\r
- this.env.mode = 'ctrl';\r
- }\r
- else if (key == termKey.LEFT) {\r
- // cursor left\r
- }\r
- else if (key == termKey.RIGHT) {\r
- // cursor right\r
- }\r
- if (key == termKey.UP) {\r
- // cursor up\r
- }\r
- else if (key == termKey.DOWN) {\r
- // cursor down\r
- }\r
- else if (key == termKey.CR) {\r
- // cr or enter\r
- }\r
- else if (key == termKey.BS) {\r
- // backspace\r
- }\r
- else if (key == termKey.DEL) {\r
- // fwd delete\r
- // conf.DELisBS is not evaluated in charMode!\r
- }\r
- else if (this.isPrintable(key)) {\r
- // printable char - just type it\r
- var ch = String.fromCharCode(key);\r
- this.type(ch);\r
- }\r
- }\r
- // leave unlocked with cursor\r
- this.lock = false;\r
- this.cursorOn();\r
- }\r
-\r
-\r
-Note the redirecting of the input handler to replace the command line handler by the\r
-editor. The method `Terminal.clear()' clears the terminal.\r
-`Terminal.cursorOn()' and `Terminal.cursorOff()' are used to show and hide the cursor.\r
-\r
-\r
-\r
-3.3 Other Handlers\r
-\r
-There are two more handlers that can be specified in the configuration object:\r
-\r
-\r
-3.3.1 initHandler\r
-\r
-`initHandler' is called at the end of the initialization triggered by `Terminal.open()'.\r
-The default action - if no `initHandler' is specified - is:\r
-\r
- // default initilization\r
-\r
- this.write(this.conf.greeting);\r
- this.newLine();\r
- this.prompt();\r
-\r
-Use `initHandler' to perform your own start up tasks (e.g. show a start up screen). Keep\r
-in mind that you should unlock the terminal and possibly show a cursor to give the\r
-impression of a usable terminal.\r
-\r
-\r
-3.3.2 exitHandler\r
-\r
-`exitHandler' is called by `Terminal.close()' just before hiding the terminal. You can use\r
-this handler to implement any tasks to be performed on exit. Note that this handler is\r
-called even if the terminal is closed on <ESC> outside of your inputHandlers control.\r
-\r
-See the file "multiterm_test.html" for an example.\r
-\r
-\r
-\r
-3.4 Overview: Flags for Behaviour Control\r
-\r
-These falgs are accessible as `Terminal.<flag>' at runtime. If not stated else, the\r
-initial value may be specified in the configuration object.\r
-The configuration object and its properties are accessible at runtime via `Terminal.conf'.\r
-\r
-\r
- NAME DEFAULT VALUE MEANING\r
-\r
- blink_delay 500 delay for cursor blinking in milliseconds.\r
-\r
- crsrBlinkMode false true for blinking cursor.\r
- if false, cursor is static.\r
- \r
- crsrBlockMode true true for block-cursor else underscore.\r
-\r
- DELisBS false handle <DEL> as <BACKSPACE>.\r
-\r
- printTab true handle <TAB> as printable (prints as space)\r
- if false <TAB> is handled as a control character\r
-\r
- printEuro true handle the euro sign as valid input char.\r
- if false char 0x20AC is printed, but not accepted\r
- in the command line\r
-\r
- catchCtrlH true handle ^H as <BACKSPACE>.\r
- if false, ^H must be tracked by a custom\r
- ctrlHandler.\r
-\r
- closeOnESC true close terminal on <ESC>.\r
- if true, <ESC> is not available for ctrHandler.\r
-\r
-\r
- historyUnique false unique history entries.\r
- if true, entries that are identical to the last\r
- entry in the user history will not be added.\r
-\r
- charMode false terminal in character mode (tracks next key-code).\r
- (runtime only)\r
- \r
- rawMode false terminal in raw mode (no echo, no editing).\r
- (runtime only)\r
-\r
-\r
-Not exactly a flag but useful:\r
-\r
- ps '>' prompt string.\r
-\r
-\r
-\r
-\r
-4 Output Methods\r
-\r
-Please note that any output to the terminal implies an advance of the cursor. This means,\r
-that if your output reaches the last column of your terminal, the cursor is advanced and\r
-a new line is opened automatically. This procedure may include scrolling to make room for\r
-the new line. While this is not of much interest for most purposes, please note that, if\r
-you output a string of length 80 to a 80-columns-terminal, and a new line, and another\r
-string, this will result in an empty line between the two strings.\r
-\r
-\r
-4.1 Terminal.type( <text> [,<stylevector>] )\r
-\r
-Types the string <text> at the current cursor position to the terminal. Long lines are\r
-broken where the last column of the terminal is reached and continued in the next line.\r
-`Terminal.write()' does not support any kind of arbitrary line breaks. (This is just a\r
-basic output routine. See `Terminal.write()' for a more powerful output method.)\r
-\r
-A bitvector may be supplied as an optional second argument to represent a style or a\r
-combination of styles. The meanings of the bits set are interpreted as follows:\r
-\r
-<stylevector>:\r
-\r
- 1 ... reverse (2 power 0)\r
- 2 ... underline (2 power 1)\r
- 4 ... italics (2 power 2)\r
- 8 ... strike (2 power 3)\r
-\r
-So "Terminal.type( 'text', 5 )" types "text" in italics and reverse video.\r
-\r
-Note:\r
-There is no bold, for most monospaced fonts (including Courier) tend to render wider in\r
-bold. Since this would bring the terminal's layout out of balance, we just can't use bold\r
-as a style. - Sorry.\r
-\r
-The HTML-representation of this styles are defined in "TermGlobals.termStyleOpen" and\r
-"TermGlobals.termStyleClose".\r
-\r
-\r
-4.2 Terminal.write( <text> [,<usemore>] )\r
-\r
-Writes a text with markup to the terminal. If an optional second argument evaluates to\r
-true, a UN*X-style utility like `more' is used to page the text. The text may be supplied\r
-as a single string (with newline character "\n") or as an array of lines. Any other input\r
-is transformed to a string value before output.\r
-\r
-4.2.1 Mark-up:\r
-\r
-`Terminal.write()' employs a simple mark-up with the following syntax:\r
-\r
-<markup>: %([+|-]<style>|n|CS|%)\r
- \r
- where "+" and '-' are used to switch on and off a style, where\r
- \r
- <style>:\r
- \r
- "i" ... italics\r
- "r" ... reverse\r
- "s" ... strike\r
- "u" ... underline\r
- \r
- "p" ... reset to plain ("%+p" == "%-p")\r
- \r
- styles may be combined and may overlap. (e.g. "This is %+rREVERSE%-r, %+uUNDER%+iSCORE%-u%-i.")\r
- \r
- "%n" represents a new line (in fact "\n" is translated to "%n" before processing)\r
- \r
- "%CS" clears the terminal screen\r
- \r
- "%%" represents the percent character ('%')\r
-\r
-\r
-4.2.2 Buffering:\r
-\r
-`Terminal.write()' writes via buffered output to the terminal. This means that the\r
-provided text is rendered to a buffer first and then only the visible parts are transfered\r
-to the terminal display buffers. This avoids scrolling delays for long output.\r
-\r
-4.2.3 UseMore Mode:\r
-\r
-The buffering of `Terminal.write()' allows for pagewise output, which may be specified by\r
-a second boolean argument. If <usemore> evaluates to `true' and the output exceeds the\r
-range of empty rows on the terminal screen, `Terminal.write()' performs like the UN*X\r
-utility `more'. The next page may be accessed by hitting <SPACE> while <q> terminates\r
-paging and returns with the prompt (-> `Terminal.prompt()').\r
-\r
-To use this facillity make sure to return immediatly after calling `Terminal.write()' in\r
-order to allow the more-routine to track the user input.\r
-The terminal is set to "charMode == false" afterwards.\r
-\r
-p.e.:\r
-\r
- // using Terminal.write as a pager\r
-\r
- function myTermHandler() {\r
- this.newLine();\r
- var args = this.lineBuffer.split(' ');\r
- var cmd = args[0];\r
- if (cmd == 'more') {\r
- var page = args[1];\r
- if (myPages[page]) {\r
- // Terminal.write as a pager\r
- this.write(myPages[page], true);\r
- return;\r
- }\r
- else {\r
- // Terminal.write for simple output\r
- this.write('no such page.');\r
- }\r
- }\r
- /*\r
- other actions ...\r
- */\r
- this.prompt();\r
- }\r
-\r
-\r
-4.3 Terminal.typeAt( <r>, <c>, <text> [,<stylevector>] )\r
-\r
-Output the string <text> at row <r>, col <c>.\r
-For <stylevector> see `Terminal.type()'.\r
-`Terminal.typeAt()' does not move the cursor.\r
-\r
-\r
-4.4 Terminal.setChar( <charcode>, <r>, <c> [,<stylevector>] )\r
-\r
-Output a single character represented by the ASCII value of <charcode> at row <r>, col <c>.\r
-For <stylevector> see `Terminal.type()'.\r
-\r
-\r
-4.5 Terminal.newLine()\r
-\r
-Moves the cursor to the first column of the next line and performs scrolling, if needed.\r
-\r
-\r
-4.6 Terminal.clear()\r
-\r
-Clears the terminal screen. (Returns with cursor off.)\r
-\r
-\r
-4.7 Terminal.statusLine( <text> [,<stylevector> [,<lineoffset>]] )\r
-\r
-All output acts on a logical screen with the origin at row 0 / col 0. While the origin is\r
-fixed, the logical width and height of the terminal are defined by `Terminal.maxCols' and\r
-`Terminal.maxLines'. These are set to the configuration dimensions at initilization and by\r
-`Terminal.reset()', but may be altered at any moment. Please note that there are no bounds\r
-checked, so make sure that `Terminal.maxCols' and `Terminal.maxLines' are less or equal\r
-to the configuration dimensions.\r
-\r
-You may want to decrement `Terminal.maxLines' to keep space for a reserved status line.\r
-`Terminal.statusLine( <text>, <style> )' offers a simple way to type a text to the last\r
-line of the screen as defined by the configuration dimensions.\r
-\r
- // using statusLine()\r
-\r
- function myHandler() {\r
- // ...\r
- // reserve last line\r
- this.maxLines = term.conf.rows-1;\r
- // print to status line in reverse video\r
- this.statusLine("Status: <none>", 1);\r
- // ...\r
- }\r
-\r
-For multiple status lines the optional argument <lineoffset> specifies the addressed row,\r
-where 1 is the line closest to the bottom, 2 the second line from the bottom and so on.\r
-(default: 1)\r
-\r
-\r
-4.8 Terminal.printRowFromString( <r> , <text> [,<stylevector>] )\r
-\r
-Outputs the string <text> to row <r> in the style of an optional <stylevector>.\r
-If the string's length exceeds the length of the row (up to `Terminal.conf.cols'), extra\r
-characteres are ignored, else any extra space is filled with character code 0 (prints as\r
-<SPACE>).\r
-The valid range for <row> is: 0 >= <row> < `Terminal.maxLines'.\r
-`Terminal.printRowFromString()' does not set the cursor.\r
-\r
-You could, for example, use this method to output a line of a text editor's buffer.\r
-\r
-p.e.:\r
-\r
- // page refresh function of a text editor\r
-\r
- function myEditorRefresh(termref, topline) {\r
- // termref: reference to Terminal instance\r
- // topline: index of first line to print\r
- // lines of text are stored in termref.env.lines\r
- for (var r=0; r<termref.maxLines; r++) {\r
- var i = topline + r;\r
- if (i < termref.env.lines.length) {\r
- // output stored line\r
- termref.printRowFromString(r, termref.env.lines[i]);\r
- }\r
- else {\r
- // output <tilde> for empty line\r
- termref.printRowFromString(r, '~');\r
- }\r
- }\r
- // set cursor to origin\r
- termref.r = termref.c = 0; // same as termref.cursorSet(0, 0);\r
- }\r
-\r
-\r
-4.9 Terminal.redraw( <row> )\r
-\r
-Basic function to redraw a terminal row <row> according to screen buffer values.\r
-For hackers only. (e.g.: for a console game, hack screen buffers first and redraw all\r
-changed rows at once.)\r
-\r
-\r
-\r
-\r
-5 Cursor Methods and Editing\r
-\r
-\r
-5.1 Terminal.cursorOn()\r
-\r
-Show the cursor.\r
-\r
-\r
-5.2 Terminal.cursorOff()\r
-\r
-Hide the cursor.\r
-\r
-\r
-5.3 Terminal.cursorSet( <r>, <c> )\r
-\r
-Set the cursor position to row <r> column <c>.\r
-`Terminal.cursorSet()' preserves the cursor's active state (on/off).\r
-\r
-\r
-5.4 Terminal.cursorLeft()\r
-\r
-Move the cursor left. (Movement is restricted to the logical input line.)\r
-`Terminal.cursorLeft()' preserves the cursor's active state (on/off).\r
-\r
-\r
-5.5 Terminal.cursorRight()\r
-\r
-Move the cursor right. (Movement is restricted to the logical input line.)\r
-`Terminal.cursorRight()' preserves the cursor's active state (on/off).\r
-\r
-\r
-5.6 Terminal.backspace()\r
-\r
-Delete the character left from the cursor, if the cursor is not in first position of the\r
-logical input line.\r
-`Terminal.backspace()' preserves the cursor's active state (on/off).\r
-\r
-\r
-5.7 Terminal.fwdDelete()\r
-\r
-Delete the character under the cursor.\r
-`Terminal.fwdDelete()' preserves the cursor's active state (on/off).\r
-\r
-\r
-5.8 Terminal.isPrintable( <key code> [,<unicode page 1 only>] )\r
-\r
-Returns `true' if the character represented by <key code> is printable with the current\r
-settings. An optional second argument <unicode page 1 only> limits the range of valid\r
-values to 255 with the exception of the Euro sign, if the flag `Terminal.printEuro' is set.\r
-(This second flag is used for input methods but not for output methods. So you may only\r
-enter portable characters, but you may print others to the terminals screen.)\r
-\r
-\r
-\r
-\r
-6 Other Methods of the Terminal Object\r
-\r
-6.1 Terminal.prompt()\r
-\r
-Performes the following actions:\r
-\r
- * advance the cursor to a new line, if the cursor is not at 1st column\r
- * type the prompt string (as specified in the configuaration object)\r
- * show the cursor\r
- * unlock the terminal\r
-\r
-(The value of the prompt string can be accessed and changed in `Terminal.ps'.)\r
-\r
-\r
-6.2 Terminal.reset()\r
-\r
-Resets the terminal to sane values and clears the terminal screen.\r
-\r
-\r
-6.3 Terminal.open()\r
-\r
-Opens the terminal. If this is a fresh instance, the HTML code for the terminal is\r
-generated. On re-entry the terminal's visibility is set to `true'. Initialization tasks\r
-are performed and the optional initHandler called. If no initHandler is specified in the\r
-configuration object, the greeting (configuration or default value) is shown and the user\r
-is prompted for input.\r
-\r
-v.1.01: `Terminal.open()' now checks for the existence of the DHTML element as defined in\r
- `Terminal.conf.termDiv' and returns success.\r
-\r
-\r
-6.4 Terminal.close()\r
-\r
-Closes the terminal and hides its visibility. An optional exitHandler (specified in the\r
-configuration object) is called, and finally the flag `Terminal.closed' is set to true. So\r
-you can check for existing terminal instances as you would check for a `window' object\r
-created by `window.open()'.\r
-\r
-p.e.:\r
-\r
- // check for a terminals state\r
- // let array "term" hold references to terminals\r
-\r
- if (term[n]) {\r
- if (term[n].closed) {\r
- // terminal exists and is closed\r
- // re-enter via "term[n].open()"\r
- }\r
- else {\r
- // terminal exists and is currently open\r
- }\r
- }\r
- else {\r
- // no such terminal\r
- // create it via "term[n] = new Terminal()"\r
- }\r
-\r
-\r
-6.5 Terminal.focus()\r
-\r
-Set the keyboard focus to this instance of Terminal. (As `window.focus()'.)\r
-\r
-\r
-6.6 Terminal.moveTo( <x>, <y> )\r
-\r
-Move the terminal to position <x>/<y> in px.\r
-(As `window.moveTo()', but inside the HTML page.)\r
-\r
-\r
-6.7 Terminal.resizeTo( <x>, <y> )\r
-\r
-Resize the terminal to dimensions <x> cols and <y> rows.\r
-<x> must be at least 4, <y> at least 2.\r
-`Terminal.resizeTo()' resets `Terminal.conf.rows', `Terminal.conf.cols',\r
-`Terminal.maxLines', and `Terminal.maxCols' to <y> and <x>, but leaves the instance' state\r
-else unchanged. Clears the terminal's screen and returns success.\r
-\r
-(A bit like `window.resizeTo()', but with rows and cols instead of px.)\r
-\r
-\r
-6.8 Terminal.getDimensions()\r
-\r
-Returns an object with properties "width" and "height" with numeric values for the\r
-terminal's outer dimensions in px. Values are zero (0) if the element is not present or\r
-if the method fails otherwise.\r
-\r
-\r
-6.9 Terminal.rebuild()\r
-\r
-Rebuilds the Terminal object's GUI preserving its state and content.\r
-Use this to change the color theme on the fly.\r
-\r
-p.e.:\r
-\r
- // change color settings on the fly\r
- // here: set bgColor to white and font style to "termWhite"\r
- // method rebuild() updates the GUI without side effects\r
-\r
- term.conf.bgColor = '#ffffff';\r
- term.conf.fontClass = 'termWhite';\r
- term.rebuild();\r
-\r
-\r
-\r
-\r
-7 Global Static Methods (TermGlobals)\r
-\r
-\r
-7.1 TermGlobals.setFocus( <termref> )\r
-\r
-Sets the keyboard focus to the instance referenced by <termref>.\r
-The focus is controlled by `TermGlobals.activeTerm' which may be accessed directly.\r
-See also: `Terminal.focus()'\r
-\r
-\r
-7.2 TermGlobals.keylock (Global Locking Flag)\r
-\r
-The global flag `TermGlobals.keylock' allows temporary keyboard locking without any\r
-other change of state. Use this to free the keyboard for any other resources.\r
-(added in v.1.03)\r
-\r
-\r
-7.3 TermGlobals Text Methods\r
-\r
-There is a small set of methods for common terminal related string tasks:\r
-\r
-\r
-7.3.1 TermGlobals.normalize( <n>, <fieldlength> )\r
-\r
-Converts a number to a string, which is filled at its left with zeros ("0") to the total\r
-length of <filedlength>. (e.g.: "TermGlobals.normalize(1, 2)" => "01")\r
-\r
-\r
-7.3.2 TermGlobals.fillLeft( <value>, <fieldlength> )\r
-\r
-Converts a value to a string and fills it to the left with blanks to <fieldlength>.\r
-\r
-\r
-7.3.3 TermGlobals.center( <text>, <length> )\r
-\r
-Adds blanks at the left of the string <text> until the text would be centered at a line\r
-of length <length>. (No blanks are added to the the right.)\r
-\r
-\r
-7.3.4 TermGlobals.stringReplace( <string1>, <string2>, <text> )\r
-\r
-Replaces all occurences of the string <string1> in <text> with <string2>.\r
-This is just a tiny work around for browsers with no support of RegExp.\r
-\r
-\r
-\r
-\r
-8 Localization\r
-\r
-The strings and key-codes used by the more utility of `Terminal.write()' are the only\r
-properties of "termlib.js" that may need localization. These properties are defined in\r
-`TermGlobals'. You may override them as needed:\r
-\r
-PROPERTY STANDARD VALUE COMMENT\r
-\r
-TermGlobals.lcMorePrompt1 ' -- MORE -- ' 1st string\r
-TermGlobals.lcMorePromtp1Style 1 reverse\r
-TermGlobals.lcMorePrompt2 ' (Type: space to continue, \'q\' to quit)' appended string\r
-TermGlobals.lcMorePrompt2Style 0 plain\r
-TermGlobals.lcMoreKeyAbort 113 (key-code: q)\r
-TermGlobals.lcMoreKeyContinue 32 (key-code <SPACE>)\r
-\r
-\r
-As "TermGlobals.lcMorePrompt2" is appended to "TermGlobals.lcMorePrompt1" make sure that\r
-the length of the combined strings does not exceed `Terminal.conf.cols'.\r
-\r
-\r
-\r
-\r
-9 Cross Browser Functions\r
-\r
-For DHTML rendering some methods - as needed by the Terminal library - are provided.\r
-These may also be accessed for other purposes.\r
-\r
-\r
-9.1 TermGlobals.writeElement( <element id>, <text> [,<NS4 parent document>] )\r
-\r
-Writes <text> to the DHTML element with id/name <element id>. \r
-<NS4 parent document> is used for NS4 only and specifies an optional reference to a parent\r
-document (default `window.document').\r
-\r
-9.2 TermGlobals.setElementXY( <element id>, <x>, <y> )\r
-\r
-Sets the DHTML element with id/name <element id> to position <x>/<y>.\r
-For NS4 works only with children of the top document (window.document).\r
-\r
-\r
-9.3 TermGlobals.setVisible( <element id>, <value> )\r
-\r
-If <value> evaluates to `true' show DHTML element with id/name <element id> else hide it.\r
-For NS4 works only with children of the top document (window.document).\r
-\r
-\r
-9.4 Custom Fixes for Missing String Methods\r
-\r
-Although `String.fromCharCode' and `String.prototype.charCodeAt' are defined by ECMA-262-2\r
-specifications, a few number of browsers lack them in their JavaScript implementation. At\r
-compile time custom methods are installed to fix this. Please note that they work only\r
-with ASCII characters and values in the range of [0x20-0xff].\r
-\r
-\r
-9.5 TermGlobals.setDisplay( <element id>, <value> )\r
-\r
-Sets the style.display property of the element with id/name <element id> to the given\r
-<value>. (added with v. 1.06)\r
-\r
-\r
-\r
-\r
-10 Architecture, Internals\r
-\r
-10.1 Global Entities\r
-\r
-The library is designed to leave only a small foot print in the namespace while providing\r
-suitable usability:\r
-\r
- Globals defined in this library:\r
-\r
- Terminal (Terminal object, `new' constructor and prototype methods)\r
- TerminalDefaults (default configuration, static object)\r
- termDefaultHandler (default command line handler, static function)\r
- TermGlobals (common vars and code for all instances, static object and methods)\r
- termKey (named mappings for special keys, static object)\r
- termDomKeyRef (special key mapping for DOM key constants, static object)\r
-\r
-\r
- Globals defined for fixing String methods, if missing\r
- (String.fromCharCode, String.prototype.charCodeAt):\r
-\r
- termString_keyref, termString_keycoderef, termString_makeKeyref\r
-\r
- \r
- Required CSS classes for font definitions: ".term", ".termReverse".\r
-\r
-\r
-\r
-10.2 I/O Architecture\r
-\r
-The Terminal object renders keyboard input from keyCodes to a line buffer and/or to a\r
-special keyCode buffer. In normal input mode printable input is echoed to the screen\r
-buffers. Special characters like <LEFT>, <RIGHT>, <BACKSPACE> are processed for command\r
-line editing by the internal key-handler `TermGlobals.keyHandler' and act directly on the\r
-screen buffers. On <CR> or <ENTER> the start and end positions of the current line are\r
-evaluated (terminated by ASCII 0x01 at the beginning which separates the prompt from the\r
-user input, and any value less than ASCII 0x20 (<SPACE>) at the right end). Then the\r
-character representation for the buffer values in this range are evaluated and\r
-concatenated to a string stored in `Terminal.lineBuffer'. As this involves some\r
-ASCII-to-String-transformations, the range of valid printable input characters is limited\r
-to the first page of unicode characters (0x0020-0x00ff).\r
-\r
-There are two screen buffers for output, one for character codes (ASCII values) and one\r
-for style codes. Style codes represent combination of styles as a bitvector (see\r
-`Terminal.type' for bit values.) The method `Terminal.redraw(<row>)' finally renders the\r
-buffers values to a string of HTML code, which is written to the HTML entity holding the\r
-according terminal row. The character buffer is a 2 dimensional array\r
-`Terminal.charBuf[<row>][<col>]' with ranges for <row> from 0 to less than\r
-`Terminal.conf.rows' and for <col> from 0 to less than `Terminal.conf.cols'. The style\r
-buffer is a 2 dimensional array `Terminal.styleBuf[<row>][<col>]' with according ranges.\r
-\r
-So every single character is represented by a ASCII code in `Terminal.charBuf' and a\r
-style-vector in `Terminal.styleBuf'. The range of printable character codes is unlimitted\r
-but should be kept to the first page of unicode characters (0x0020-0x00ff) for\r
-compatibility purpose. (c.f. 8.4)\r
-\r
-Keyboard input is first handled on the `KEYDOWN' event by the handler `TermGlobals.keyFix'\r
-to remap the keyCodes of cursor keys to consistent values. (To make them distinctable from\r
-any other possibly printable values, the values of POSIX <IS4> to <IS1> where chosen.)\r
-The mapping of the cursor keys is stored in the properties LEFT, RIGHT, UP, and DOWN of\r
-the global static object `termKey'.\r
-\r
-The main keyboard handler `TermGlobals.keyHandler' (invoked on `KEYPRESS' or by\r
-`TermGlobals.keyFix') does some final mapping first. Then the input is evaluated as\r
-controlled by the flags `Terminal.rawMode' and `Terminal.charMode' with precedence of the\r
-latter. In dependancy of the mode defined and the handlers currently defined, the input\r
-either is ignored, or is internally processed for command line editing, or one of the\r
-handlers is called.\r
-\r
-In the case of the simultanous presecence of two instances of Terminal, the keyboard focus\r
-is controlled via a reference stored in `TermGlobals.activeTerm'. This reference is also\r
-used to evaluate the `this'-context of the key handlers which are methods of the static\r
-Object `TermGlobals'.\r
-\r
-A terminal's screen consists of a HTML-table element residing in the HTML/CSS division\r
-spcified in `Terminal.conf.termDiv'. Any output is handled on a per row bases. The\r
-individual rows are either nested sub-divisions of the main divisions (used for NS4 or\r
-browsers not compatible to the "Gecko" engine) or the indiviual table data elements (<TD>)\r
-of the terminal's inner table (used for browsers employing the "Gecko" engine).\r
-(This implementation was chosen for rendering speed and in order to minimize any screen\r
-flicker.) Any output or change of state in a raw results in the inner HTML contents of a\r
-row's HTML element to be rewritten. Please note that as a result of this a blinking cursor\r
-may cause a flicker in the line containing the cursor's position while displayed by a\r
-browser, which employs the "Gecko" engine.\r
-\r
-\r
-\r
-10.3 Compatibility\r
-\r
-Standard web browsers with a JavaScript implementation compliant to ECMA-262 2nd edition\r
-[ECMA262-2] and support for the anonymous array and object constructs and the anonymous\r
-function construct in the form of "myfunc = function(x) {}" (c.f. ECMA-262 3rd edion\r
-[ECMA262-3] for details). This comprises almost all current browsers but Konquerer (khtml)\r
-and versions of Apple Safari for Mac OS 10.0-10.28 (Safari < 1.1) which lack support for\r
-keyboard events.\r
-\r
-To provide a maximum of compatibilty the extend of language keywords used was kept to a\r
-minimum and does not exceed the lexical conventions of ECMA-262-2. Especially there is no\r
-use of the `switch' statement or the `RegExp' method of the global object. Also the use of\r
-advanced Array methods like `push', `shift', `splice' was avoided.\r
-\r
-\r
-\r
-\r
-11 History\r
-\r
-This library evolved from the terminal script "TermApp" ((c) N. Landsteiner 2003) and is\r
-in its current form a down scaled spinn-off of the "JS/UIX" project [JS/UIX] (evolution\r
-"JS/UIX v0.5"). c.f.: <http://www.masswerk.at/jsuix>\r
-\r
-v 1.01: added Terminal.prototype.resizeTo(x,y)\r
- added Terminal.conf.fontClass (=> configureable class name)\r
- Terminal.prototype.open() now checks for element conf.termDiv in advance\r
- and returns success.\r
-\r
-v 1.02: added support for <TAB> and Euro sign\r
- Terminal.conf.printTab\r
- Terminal.conf.printEuro\r
- and method Terminal.prototype.isPrintable(keycode)\r
- added support for getopt to sample parser ("parser_sample.html")\r
-\r
-\r
-v 1.03: added global keyboard locking (TermGlobals.keylock)\r
- modified Terminal.prototype.redraw for speed (use of locals)\r
-\r
-\r
-v 1.04: modified the key handler to fix a bug with MSIE5/Mac\r
- fixed a bug in TermGlobals.setVisible with older MSIE-alike browsers without\r
- DOM support.\r
- moved the script of the sample parser to an individual document\r
- => "termlib_parser.js" (HTML document is "parser_sample.html" as before)\r
-\r
-v 1.05: added config flag historyUnique.\r
-\r
-v 1.06: fixed CTRl+ALT (Windows alt gr) isn't CTRL any more\r
- -> better support for international keyboards with MSIE/Win.\r
- fixed double backspace bug for Safari;\r
- added TermGlobals.setDisplay for setting style.display props\r
- termlib.js now outputs lower case html (xhtml compatibility)\r
- (date: 12'2006)\r
-\r
-v 1.07: added method Terminal.rebuild() to rebuild the GUI with new color settings.\r
- (date: 01'2007)\r
-\r
-\r
-\r
-\r
-12 Example for a Command Line Parser\r
-\r
- // parser example, splits command line to args with quoting and escape\r
- // for use as `Terminal.handler'\r
- \r
- function commandHandler() {\r
- this.newLine();\r
- var argv = ['']; // arguments vector\r
- var argQL = ['']; // quoting level\r
- var argc = 0; // arguments cursor\r
- var escape = false ; // escape flag\r
- for (var i=0; i<this.lineBuffer.length; i++) {\r
- var ch= this.lineBuffer.charAt(i);\r
- if (escape) {\r
- argv[argc] += ch;\r
- escape = false;\r
- }\r
- else if ((ch == '"') || (ch == "'") || (ch == "`")) {\r
- if (argQL[argc]) {\r
- if (argQL[argc] == ch) {\r
- argc ++;\r
- argv[argc] = argQL[argc] = '';\r
- }\r
- else {\r
- argv[argc] += ch;\r
- }\r
- }\r
- else {\r
- if (argv[argc] != '') {\r
- argc ++;\r
- argv[argc] = '';\r
- argQL[argc] = ch;\r
- }\r
- else {\r
- argQL[argc] = ch;\r
- }\r
- }\r
- }\r
- else if ((ch == ' ') || (ch == '\t')) {\r
- if (argQL[argc]) {\r
- argv[argc] += ch;\r
- }\r
- else if (argv[argc] != '') {\r
- argc++;\r
- argv[argc] = argQL[argc] = '';\r
- }\r
- }\r
- else if (ch == '\\') {\r
- escape = true;\r
- }\r
- else {\r
- argv[argc] += ch;\r
- }\r
- }\r
- if ((argv[argc] == '') && (!argQL[argc])) {\r
- argv.length--;\r
- argQL.length--;\r
- }\r
- if (argv.length == 0) {\r
- // no commmand line input\r
- }\r
- else if (argQL[0]) {\r
- // first argument quoted -> error\r
- this.write("Error: first argument quoted by "+argQL[0]);\r
- }\r
- else {\r
- argc = 0;\r
- var cmd = argv[argc++];\r
- /*\r
- parse commands\r
- 1st argument is argv[argc]\r
- arguments' quoting levels in argQL[argc] are of (<empty> | ' | " | `)\r
- */\r
- if (cmd == 'help') {\r
- this.write(helpPage);\r
- }\r
- else if (cmd == 'clear') {\r
- this.clear();\r
- }\r
- else if (cmd == 'exit') {\r
- this.close();\r
- return;\r
- }\r
- else {\r
- // for test purpose just output argv as list\r
- // assemple a string of style-escaped lines and output it in more-mode\r
- s=' ARG QL VALUE%n';\r
- for (var i=0; i<argv.length; i++) {\r
- s += TermGlobals.stringReplace('%', '%%',\r
- TermGlobals.fillLeft(i, 6) +\r
- TermGlobals.fillLeft((argQL[i])? argQL[i]:'-', 4) +\r
- ' "' + argv[i] + '"'\r
- ) + '%n';\r
- }\r
- this.write(s, 1);\r
- return;\r
- }\r
- }\r
- this.prompt();\r
- }\r
-\r
-\r
-The file "parser_sample.html" features a stand-alone parser ("termlib_parser.js") very\r
-much like this. You are free to use it according to the termlib-license (see sect. 13).\r
-It provides configurable values for quotes and esape characters and imports the parsed\r
-argument list into a Terminal instance's namespace. ("parser_sample.html" and\r
-"termlib_parser.js" should accompany this file.)\r
-\r
-\r
-\r
-\r
-13 License\r
-\r
-This JavaScript-library is free for private and academic use.\r
-Please include a readable copyright statement and a backlink to <http://www.masswerk.at>\r
-in the web page. The library should always be accompanied by the 'readme.txt' and the\r
-sample HTML-documents.\r
-\r
-The term "private use" includes any personal or non-commercial use, which is not related\r
-to commercial activites, but excludes intranet, extranet and/or public net applications\r
-that are related to any kind of commercial or profit oriented activity.\r
-\r
-For commercial use see <http://www.masswerk.at> for contact information.\r
-\r
-Any changes to the library should be commented and be documented in the readme-file.\r
-Any changes must be reflected in the `Terminal.version' string as\r
-"Version.Subversion (compatibility)".\r
-\r
-\r
-\r
-\r
-14 Disclaimer\r
-\r
-This software is distributed AS IS and in the hope that it will be useful, but WITHOUT ANY\r
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
-PURPOSE. The entire risk as to the quality and performance of the product is borne by the\r
-user. No use of the product is authorized hereunder except under this disclaimer.\r
-\r
-\r
-\r
-\r
-15 References\r
-\r
-[ECMA262-2] "ECMAScript Language Specification" Standard ECMA-262 2nd Edition\r
- August 1998 (ISO/IEC 16262 - April 1998)\r
-\r
-[ECMA262-3] "ECMAScript Language Specification" Standard ECMA-262 3rd Edition Final\r
- 24 March 2000\r
-\r
-[JS/UIX] JS/UIX - JavaScript Uniplexed Interface eXtension\r
- <http://www.masswerk.at/jsuix>\r
-\r
-\r
-\r
-\r
-\r
-Norbert Landsteiner / Vienna, August 2005\r
-mass:werk - media environments\r
-<http://www.masswerk.at>\r
-See web site for contact information.\r
+++ /dev/null
-.term {\r
- font-family: courier,fixed,swiss,sans-serif;\r
- font-size: 12px;\r
- color: #33d011;\r
- background: none;\r
-}\r
-\r
-.termReverse {\r
- color: #111111;\r
- background: #33d011;\r
-}\r
+++ /dev/null
-/*\r
- termlib.js - JS-WebTerminal Object v1.07\r
-\r
- (c) Norbert Landsteiner 2003-2005\r
- mass:werk - media environments\r
- <http://www.masswerk.at>\r
-\r
- Creates [multiple] Terminal instances.\r
-\r
- Synopsis:\r
-\r
- myTerminal = new Terminal(<config object>);\r
- myTerminal.open();\r
-\r
- <config object> overrides any values of object `TerminalDefaults'.\r
- individual values of `id' must be supplied for multiple terminals.\r
- `handler' specifies a function to be called for input handling.\r
- (see `Terminal.prototype.termDefaultHandler()' and documentation.)\r
-\r
- globals defined in this library:\r
- Terminal (Terminal object)\r
- TerminalDefaults (default configuration)\r
- termDefaultHandler (default command line handler)\r
- TermGlobals (common vars and code for all instances)\r
- termKey (named mappings for special keys)\r
- termDomKeyRef (special key mapping for DOM constants)\r
-\r
- globals defined for fixing String methods, if missing\r
- (String.fromCharCode, String.prototype.charCodeAt):\r
- termString_keyref, termString_keycoderef, termString_makeKeyref\r
-\r
- required CSS classes for font definitions: ".term", ".termReverse".\r
-\r
- Compatibilty:\r
- Standard web browsers with a JavaScript implementation compliant to\r
- ECMA-262 2nd edition and support for the anonymous array and object\r
- constructs and the anonymous function construct in the form of\r
- "myfunc=function(x) {}" (c.f. ECMA-262 3rd edion for details).\r
- This comprises almost all current browsers but Konquerer (khtml) and\r
- versions of Apple Safari for Mac OS 10.0-10.28 (Safari 1.0) which\r
- lack support for keyboard events.\r
-\r
- License:\r
- This JavaScript-library is free for private and academic use.\r
- Please include a readable copyright statement and a backlink to\r
- <http://www.masswerk.at> in the web page.\r
- The library should always be accompanied by the 'readme.txt' and the\r
- sample HTML-documents.\r
- \r
- The term "private use" includes any personal or non-commercial use, which\r
- is not related to commercial activites, but excludes intranet, extranet\r
- and/or public net applications that are related to any kind of commercial\r
- or profit oriented activity.\r
-\r
- For commercial use see <http://www.masswerk.at> for contact information.\r
- \r
- Any changes should be commented and must be reflected in `Terminal.version'\r
- in the format: "Version.Subversion (compatibility)".\r
-\r
- Disclaimer:\r
- This software is distributed AS IS and in the hope that it will be useful,\r
- but WITHOUT ANY WARRANTY; without even the implied warranty of\r
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The entire risk as to\r
- the quality and performance of the product is borne by the user. No use of\r
- the product is authorized hereunder except under this disclaimer.\r
-\r
- ### The sections above must not be removed. ###\r
- \r
- version 1.01: added Terminal.prototype.resizeTo(x,y)\r
- added Terminal.conf.fontClass (=> configureable class name)\r
- Terminal.prototype.open() now checks for element conf.termDiv\r
- in advance and returns success.\r
-\r
- version 1.02: added support for <TAB> and Euro sign\r
- (Terminal.conf.printTab, Terminal.conf.printEuro)\r
- and a method to evaluate printable chars:\r
- Terminal.prototype.isPrintable(keycode)\r
-\r
- version 1.03: added global keyboard locking (TermGlobals.keylock)\r
- modified Terminal.prototype.redraw for speed (use of locals)\r
-\r
- version 1.04: modified the key handler to fix a bug with MSIE5/Mac\r
- fixed a bug in TermGlobals.setVisible with older MSIE-alike\r
- browsers without DOM support.\r
-\r
- version 1.05: added config flag historyUnique.\r
- \r
- version 1.06: fixed CTRl+ALT (Windows alt gr) isn't CTRL any more\r
- fixed double backspace bug for Safari;\r
- added TermGlobals.setDisplay for setting style.display props\r
- termlib.js now outputs lower case html (xhtml compatibility)\r
-\r
- version 1.07: added method rebuild() to rebuild with new color settings.\r
-\r
-*/\r
-\r
-var TerminalDefaults = {\r
- // dimensions\r
- cols:80,\r
- rows:24,\r
- // appearance\r
- x:100,\r
- y:100,\r
- termDiv:'termDiv',\r
- bgColor:'#181818',\r
- frameColor:'#555555',\r
- frameWidth:1,\r
- rowHeight:15,\r
- blinkDelay:500,\r
- // css class\r
- fontClass:'term',\r
- // initial cursor mode\r
- crsrBlinkMode:false,\r
- crsrBlockMode:true,\r
- // key mapping\r
- DELisBS:false,\r
- printTab:true,\r
- printEuro:true,\r
- catchCtrlH:true,\r
- closeOnESC:true,\r
- // prevent consecutive history doublets\r
- historyUnique:false,\r
- // optional id\r
- id:0,\r
- // strings\r
- ps:'>',\r
- greeting:'%+r Terminal ready. %-r',\r
- // handlers\r
- handler:termDefaultHandler,\r
- ctrlHandler:null,\r
- initHandler:null,\r
- exitHandler:null\r
-}\r
-\r
-var Terminal = function(conf) {\r
- if (typeof conf != 'object') conf=new Object();\r
- else {\r
- for (var i in TerminalDefaults) {\r
- if (typeof conf[i] == 'undefined') conf[i]=TerminalDefaults[i];\r
- }\r
- }\r
- this.conf=conf;\r
- this.version='1.07 (original)';\r
- this.isSafari= (navigator.userAgent.indexOf('Safari')>=0)? true:false;\r
- this.setInitValues();\r
-}\r
-\r
-Terminal.prototype.setInitValues=function() {\r
- this.id=this.conf.id;\r
- this.maxLines=this.conf.rows;\r
- this.maxCols=this.conf.cols;\r
- this.termDiv=this.conf.termDiv;\r
- this.crsrBlinkMode=this.conf.crsrBlinkMode;\r
- this.crsrBlockMode=this.conf.crsrBlockMode;\r
- this.blinkDelay=this.conf.blinkDelay;\r
- this.DELisBS=this.conf.DELisBS;\r
- this.printTab=this.conf.printTab;\r
- this.printEuro=this.conf.printEuro;\r
- this.catchCtrlH=this.conf.catchCtrlH;\r
- this.closeOnESC=this.conf.closeOnESC;\r
- this.historyUnique=this.conf.historyUnique;\r
- this.ps=this.conf.ps;\r
- this.closed=false;\r
- this.r;\r
- this.c;\r
- this.charBuf=new Array();\r
- this.styleBuf=new Array();\r
- this.scrollBuf=null;\r
- this.blinkBuffer=0;\r
- this.blinkTimer;\r
- this.cursoractive=false;\r
- this.lock=true;\r
- this.insert=false;\r
- this.charMode=false;\r
- this.rawMode=false;\r
- this.lineBuffer='';\r
- this.inputChar=0;\r
- this.lastLine='';\r
- this.guiCounter=0;\r
- this.history=new Array();\r
- this.histPtr=0;\r
- this.env=new Object();\r
- this.ns4ParentDoc=null;\r
- this.handler=this.conf.handler;\r
- this.ctrlHandler=this.conf.ctrlHandler;\r
- this.initHandler=this.conf.initHandler;\r
- this.exitHandler=this.conf.exitHandler;\r
-}\r
-\r
-function termDefaultHandler() {\r
- this.newLine();\r
- if (this.lineBuffer != '') {\r
- this.type('You typed: '+this.lineBuffer);\r
- this.newLine();\r
- }\r
- this.prompt();\r
-}\r
-\r
-Terminal.prototype.open=function() {\r
- if (this.termDivReady()) {\r
- if (!this.closed) this._makeTerm();\r
- this.init();\r
- return true;\r
- }\r
- else return false;\r
-}\r
-\r
-Terminal.prototype.close=function() {\r
- this.lock=true;\r
- this.cursorOff();\r
- if (this.exitHandler) this.exitHandler();\r
- TermGlobals.setVisible(this.termDiv,0);\r
- this.closed=true;\r
-}\r
-\r
-Terminal.prototype.init=function() {\r
- // wait for gui\r
- if (this.guiReady()) {\r
- this.guiCounter=0;\r
- // clean up at re-entry\r
- if (this.closed) {\r
- this.setInitValues();\r
- }\r
- this.clear();\r
- TermGlobals.setVisible(this.termDiv,1);\r
- TermGlobals.enableKeyboard(this);\r
- if (this.initHandler) {\r
- this.initHandler();\r
- }\r
- else {\r
- this.write(this.conf.greeting);\r
- this.newLine();\r
- this.prompt();\r
- }\r
- }\r
- else {\r
- this.guiCounter++;\r
- if (this.guiCounter>18000) {\r
- if (confirm('Terminal:\nYour browser hasn\'t responded for more than 2 minutes.\nRetry?')) this.guiCounter=0\r
- else return;\r
- };\r
- TermGlobals.termToInitialze=this;\r
- window.setTimeout('TermGlobals.termToInitialze.init()',200);\r
- }\r
-}\r
-\r
-Terminal.prototype.getRowArray=function(l,v) {\r
- var a=new Array();\r
- for (var i=0; i<l; i++) a[i]=v;\r
- return a;\r
-}\r
-\r
-Terminal.prototype.type=function(text,style) {\r
- for (var i=0; i<text.length; i++) {\r
- var ch=text.charCodeAt(i);\r
- if (!this.isPrintable(ch)) ch=94;\r
- this.charBuf[this.r][this.c]=ch;\r
- this.styleBuf[this.r][this.c]=(style)? style:0;\r
- var last_r=this.r;\r
- this._incCol();\r
- if (this.r!=last_r) this.redraw(last_r);\r
- }\r
- this.redraw(this.r)\r
-}\r
-\r
-Terminal.prototype.write=function(text,usemore) {\r
- // write to scroll buffer with markup\r
- // new line = '%n' prepare any strings or arrys first\r
- if (typeof text != 'object') {\r
- if (typeof text!='string') text=''+text;\r
- if (text.indexOf('\n')>=0) {\r
- var ta=text.split('\n');\r
- text=ta.join('%n');\r
- }\r
- }\r
- else {\r
- if (text.join) text=text.join('%n')\r
- else text=''+text;\r
- }\r
- this._sbInit(usemore);\r
- var chunks=text.split('%');\r
- var esc=(text.charAt(0)!='%');\r
- var style=0;\r
- for (var i=0; i<chunks.length; i++) {\r
- if (esc) {\r
- if (chunks[i].length>0) this._sbType(chunks[i],style)\r
- else if (i>0) this._sbType('%', style);\r
- esc=false;\r
- }\r
- else {\r
- var func=chunks[i].charAt(0);\r
- if ((chunks[i].length==0) && (i>0)) {\r
- this._sbType("%",style);\r
- esc=true;\r
- }\r
- else if (func=='n') {\r
- this._sbNewLine();\r
- if (chunks[i].length>1) this._sbType(chunks[i].substring(1),style);\r
- }\r
- else if (func=='+') {\r
- var opt=chunks[i].charAt(1);\r
- opt=opt.toLowerCase();\r
- if (opt=='p') style=0\r
- else if (opt=='r') style|=1\r
- else if (opt=='u') style|=2\r
- else if (opt=='i') style|=4\r
- else if (opt=='s') style|=8;\r
- if (chunks[i].length>2) this._sbType(chunks[i].substring(2),style);\r
- }\r
- else if (func=='-') {\r
- var opt=chunks[i].charAt(1);\r
- opt=opt.toLowerCase();\r
- if (opt=='p') style|=0\r
- else if (opt=='r') style&=~1\r
- else if (opt=='u') style&=~2\r
- else if (opt=='i') style&=~4\r
- else if (opt=='s') style&=~8;\r
- if (chunks[i].length>2) this._sbType(chunks[i].substring(2),style);\r
- }\r
- else if ((chunks[i].length>1) && (chunks[i].charAt(0)=='C') && (chunks[i].charAt(1)=='S')) {\r
- this.clear();\r
- this._sbInit();\r
- if (chunks[i].length>2) this._sbType(chunks[i].substring(2),style);\r
- }\r
- else {\r
- if (chunks[i].length>0) this._sbType(chunks[i],style);\r
- }\r
- }\r
- }\r
- this._sbOut();\r
-}\r
-\r
-Terminal.prototype._sbType=function(text,style) {\r
- // type to scroll buffer\r
- var sb=this.scrollBuf;\r
- for (var i=0; i<text.length; i++) {\r
- var ch=text.charCodeAt(i);\r
- if (!this.isPrintable(ch)) ch=94;\r
- sb.lines[sb.r][sb.c]=ch;\r
- sb.styles[sb.r][sb.c]=(style)? style:0;\r
- sb.c++;\r
- if (sb.c>=this.maxCols) this._sbNewLine();\r
- }\r
-}\r
-\r
-Terminal.prototype._sbNewLine=function() {\r
- var sb=this.scrollBuf;\r
- sb.r++;\r
- sb.c=0;\r
- sb.lines[sb.r]=this.getRowArray(this.conf.cols,0);\r
- sb.styles[sb.r]=this.getRowArray(this.conf.cols,0);\r
-}\r
-\r
-\r
-Terminal.prototype._sbInit=function(usemore) {\r
- var sb=this.scrollBuf=new Object();\r
- var sbl=sb.lines=new Array();\r
- var sbs=sb.styles=new Array();\r
- sb.more=usemore;\r
- sb.line=0;\r
- sb.status=0;\r
- sb.r=0;\r
- sb.c=this.c;\r
- sbl[0]=this.getRowArray(this.conf.cols,0);\r
- sbs[0]=this.getRowArray(this.conf.cols,0);\r
- for (var i=0; i<this.c; i++) {\r
- sbl[0][i]=this.charBuf[this.r][i];\r
- sbs[0][i]=this.styleBuf[this.r][i];\r
- }\r
-}\r
-\r
-Terminal.prototype._sbOut=function() {\r
- var sb=this.scrollBuf;\r
- var sbl=sb.lines;\r
- var sbs=sb.styles;\r
- var tcb=this.charBuf;\r
- var tsb=this.styleBuf;\r
- var ml=this.maxLines;\r
- var buflen=sbl.length;\r
- if (sb.more) {\r
- if (sb.status) {\r
- if (this.inputChar==TermGlobals.lcMoreKeyAbort) {\r
- this.r=ml-1;\r
- this.c=0;\r
- tcb[this.r]=this.getRowArray(this.maxLines,0);\r
- tsb[this.r]=this.getRowArray(this.maxLines,0);\r
- this.redraw(this.r);\r
- this.handler=sb.handler;\r
- this.charMode=false;\r
- this.inputChar=0;\r
- this.scrollBuf=null;\r
- this.prompt();\r
- return;\r
- }\r
- else if (this.inputChar==TermGlobals.lcMoreKeyContinue) {\r
- this.clear();\r
- }\r
- else {\r
- return;\r
- }\r
- }\r
- else {\r
- if (this.r>=ml-1) this.clear();\r
- }\r
- }\r
- if (this.r+buflen-sb.line<=ml) {\r
- for (var i=sb.line; i<buflen; i++) {\r
- var r=this.r+i-sb.line;\r
- tcb[r]=sbl[i];\r
- tsb[r]=sbs[i];\r
- this.redraw(r);\r
- }\r
- this.r+=sb.r-sb.line;\r
- this.c=sb.c;\r
- if (sb.more) {\r
- if (sb.status) this.handler=sb.handler;\r
- this.charMode=false;\r
- this.inputChar=0;\r
- this.scrollBuf=null;\r
- this.prompt();\r
- return;\r
- }\r
- }\r
- else if (sb.more) {\r
- ml--;\r
- if (sb.status==0) {\r
- sb.handler=this.handler;\r
- this.handler=this._sbOut;\r
- this.charMode=true;\r
- sb.status=1;\r
- }\r
- if (this.r) {\r
- var ofs=ml-this.r;\r
- for (var i=sb.line; i<ofs; i++) {\r
- var r=this.r+i-sb.line;\r
- tcb[r]=sbl[i];\r
- tsb[r]=sbs[i];\r
- this.redraw(r);\r
- }\r
- }\r
- else {\r
- var ofs=sb.line+ml;\r
- for (var i=sb.line; i<ofs; i++) {\r
- var r=this.r+i-sb.line;\r
- tcb[r]=sbl[i];\r
- tsb[r]=sbs[i];\r
- this.redraw(r);\r
- }\r
- }\r
- sb.line=ofs;\r
- this.r=ml;\r
- this.c=0;\r
- this.type(TermGlobals.lcMorePrompt1, TermGlobals.lcMorePromtp1Style);\r
- this.type(TermGlobals.lcMorePrompt2, TermGlobals.lcMorePrompt2Style);\r
- this.lock=false;\r
- return;\r
- }\r
- else if (buflen>=ml) {\r
- var ofs=buflen-ml;\r
- for (var i=0; i<ml; i++) {\r
- var r=ofs+i;\r
- tcb[i]=sbl[r];\r
- tsb[i]=sbs[r];\r
- this.redraw(i);\r
- }\r
- this.r=ml-1;\r
- this.c=sb.c;\r
- }\r
- else {\r
- var dr=ml-buflen;\r
- var ofs=this.r-dr;\r
- for (var i=0; i<dr; i++) {\r
- var r=ofs+i;\r
- for (var c=0; c<this.maxCols; c++) {\r
- tcb[i][c]=tcb[r][c];\r
- tsb[i][c]=tsb[r][c];\r
- }\r
- this.redraw(i);\r
- }\r
- for (var i=0; i<buflen; i++) {\r
- var r=dr+i;\r
- tcb[r]=sbl[i];\r
- tsb[r]=sbs[i];\r
- this.redraw(r);\r
- }\r
- this.r=ml-1;\r
- this.c=sb.c;\r
- }\r
- this.scrollBuf=null;\r
-}\r
-\r
-// basic console output\r
-\r
-Terminal.prototype.typeAt=function(r,c,text,style) {\r
- var tr1=this.r;\r
- var tc1=this.c;\r
- this.cursorSet(r,c);\r
- for (var i=0; i<text.length; i++) {\r
- var ch=text.charCodeAt(i);\r
- if (!this.isPrintable(ch)) ch=94;\r
- this.charBuf[this.r][this.c]=ch;\r
- this.styleBuf[this.r][this.c]=(style)? style:0;\r
- var last_r=this.r;\r
- this._incCol();\r
- if (this.r!=last_r) this.redraw(last_r);\r
- }\r
- this.redraw(this.r);\r
- this.r=tr1;\r
- this.c=tc1;\r
-}\r
-\r
-Terminal.prototype.statusLine = function(text,style,offset) {\r
- var ch,r;\r
- style=((style) && (!isNaN(style)))? parseInt(style)&15:0;\r
- if ((offset) && (offset>0)) r=this.conf.rows-offset\r
- else r=this.conf.rows-1;\r
- for (var i=0; i<this.conf.cols; i++) {\r
- if (i<text.length) {\r
- ch=text.charCodeAt(i);\r
- if (!this.isPrintable(ch)) ch = 94;\r
- }\r
- else ch=0;\r
- this.charBuf[r][i]=ch;\r
- this.styleBuf[r][i]=style;\r
- }\r
- this.redraw(r);\r
-}\r
-\r
-Terminal.prototype.printRowFromString = function(r,text,style) {\r
- var ch;\r
- style=((style) && (!isNaN(style)))? parseInt(style)&15:0;\r
- if ((r>=0) && (r<this.maxLines)) {\r
- if (typeof text != 'string') text=''+text;\r
- for (var i=0; i<this.conf.cols; i++) {\r
- if (i<text.length) {\r
- ch=text.charCodeAt(i);\r
- if (!this.isPrintable(ch)) ch = 94;\r
- }\r
- else ch=0;\r
- this.charBuf[r][i]=ch;\r
- this.styleBuf[r][i]=style;\r
- }\r
- this.redraw(r);\r
- }\r
-}\r
-\r
-Terminal.prototype.setChar=function(ch,r,c,style) {\r
- this.charBuf[r][c]=ch;\r
- this.styleBuf[this.r][this.c]=(style)? style:0;\r
- this.redraw(r);\r
-}\r
-\r
-Terminal.prototype._charOut=function(ch, style) {\r
- this.charBuf[this.r][this.c]=ch;\r
- this.styleBuf[this.r][this.c]=(style)? style:0;\r
- this.redraw(this.r);\r
- this._incCol();\r
-}\r
-\r
-Terminal.prototype._incCol=function() {\r
- this.c++;\r
- if (this.c>=this.maxCols) {\r
- this.c=0;\r
- this._incRow();\r
- }\r
-}\r
-\r
-Terminal.prototype._incRow=function() {\r
- this.r++;\r
- if (this.r>=this.maxLines) {\r
- this._scrollLines(0,this.maxLines);\r
- this.r=this.maxLines-1;\r
- }\r
-}\r
-\r
-Terminal.prototype._scrollLines=function(start, end) {\r
- window.status='Scrolling lines ...';\r
- start++;\r
- for (var ri=start; ri<end; ri++) {\r
- var rt=ri-1;\r
- this.charBuf[rt]=this.charBuf[ri];\r
- this.styleBuf[rt]=this.styleBuf[ri];\r
- }\r
- // clear last line\r
- var rt=end-1;\r
- this.charBuf[rt]=this.getRowArray(this.conf.cols,0);\r
- this.styleBuf[rt]=this.getRowArray(this.conf.cols,0);\r
- this.redraw(rt);\r
- for (var r=end-1; r>=start; r--) this.redraw(r-1);\r
- window.status='';\r
-}\r
-\r
-Terminal.prototype.newLine=function() {\r
- this.c=0;\r
- this._incRow();\r
-}\r
-\r
-Terminal.prototype.clear=function() {\r
- window.status='Clearing display ...';\r
- this.cursorOff();\r
- this.insert=false;\r
- for (var ri=0; ri<this.maxLines; ri++) {\r
- this.charBuf[ri]=this.getRowArray(this.conf.cols,0);\r
- this.styleBuf[ri]=this.getRowArray(this.conf.cols,0);\r
- this.redraw(ri);\r
- }\r
- this.r=0;\r
- this.c=0;\r
- window.status='';\r
-}\r
-\r
-Terminal.prototype.reset=function() {\r
- if (this.lock) return;\r
- this.lock=true;\r
- this.rawMode=false;\r
- this.charMode=false;\r
- this.maxLines=this.conf.rows;\r
- this.maxCols=this.conf.cols;\r
- this.lastLine='';\r
- this.lineBuffer='';\r
- this.inputChar=0;\r
- this.clear();\r
-}\r
-\r
-Terminal.prototype.cursorSet=function(r,c) {\r
- var crsron=this.cursoractive;\r
- if (crsron) this.cursorOff();\r
- this.r=r%this.maxLines;\r
- this.c=c%this.maxCols;\r
- this._cursorReset(crsron);\r
-}\r
-\r
-Terminal.prototype._cursorReset=function(crsron) {\r
- if (crsron) this.cursorOn()\r
- else {\r
- this.blinkBuffer=this.styleBuf[this.r][this.c];\r
- }\r
-}\r
-\r
-Terminal.prototype.cursorOn=function() {\r
- if (this.blinkTimer) clearTimeout(this.blinkTimer);\r
- this.blinkBuffer=this.styleBuf[this.r][this.c];\r
- this._cursorBlink();\r
- this.cursoractive=true;\r
-}\r
-\r
-Terminal.prototype.cursorOff=function() {\r
- if (this.blinkTimer) clearTimeout(this.blinkTimer);\r
- if (this.cursoractive) {\r
- this.styleBuf[this.r][this.c]=this.blinkBuffer;\r
- this.redraw(this.r);\r
- this.cursoractive=false;\r
- }\r
-}\r
-\r
-Terminal.prototype._cursorBlink=function() {\r
- if (this.blinkTimer) clearTimeout(this.blinkTimer);\r
- if (this == TermGlobals.activeTerm) {\r
- if (this.crsrBlockMode) {\r
- this.styleBuf[this.r][this.c]=(this.styleBuf[this.r][this.c]&1)?\r
- this.styleBuf[this.r][this.c]&254:this.styleBuf[this.r][this.c]|1;\r
- }\r
- else {\r
- this.styleBuf[this.r][this.c]=(this.styleBuf[this.r][this.c]&2)?\r
- this.styleBuf[this.r][this.c]&253:this.styleBuf[this.r][this.c]|2;\r
- }\r
- this.redraw(this.r);\r
- }\r
- if (this.crsrBlinkMode) this.blinkTimer=setTimeout('TermGlobals.activeTerm._cursorBlink()', this.blinkDelay);\r
-}\r
-\r
-Terminal.prototype.cursorLeft=function() {\r
- var crsron=this.cursoractive;\r
- if (crsron) this.cursorOff();\r
- var r=this.r;\r
- var c=this.c;\r
- if (c>0) c--\r
- else if (r>0) {\r
- c=this.maxCols-1;\r
- r--;\r
- }\r
- if (this.isPrintable(this.charBuf[r][c])) {\r
- this.r=r;\r
- this.c=c;\r
- }\r
- this.insert=true;\r
- this._cursorReset(crsron);\r
-}\r
-\r
-Terminal.prototype.cursorRight=function() {\r
- var crsron=this.cursoractive;\r
- if (crsron) this.cursorOff();\r
- var r=this.r;\r
- var c=this.c;\r
- if (c<this.maxCols-1) c++\r
- else if (r<this.maxLines-1) {\r
- c=0;\r
- r++;\r
- }\r
- if (!this.isPrintable(this.charBuf[r][c])) {\r
- this.insert=false;\r
- }\r
- if (this.isPrintable(this.charBuf[this.r][this.c])) {\r
- this.r=r;\r
- this.c=c;\r
- }\r
- this._cursorReset(crsron);\r
-}\r
-\r
-Terminal.prototype.backspace=function() {\r
- var crsron=this.cursoractive;\r
- if (crsron) this.cursorOff();\r
- var r=this.r;\r
- var c=this.c;\r
- if (c>0) c--\r
- else if (r>0) {\r
- c=this.maxCols-1;\r
- r--;\r
- };\r
- if (this.isPrintable(this.charBuf[r][c])) {\r
- this._scrollLeft(r, c);\r
- this.r=r;\r
- this.c=c;\r
- }; \r
- this._cursorReset(crsron);\r
-}\r
-\r
-Terminal.prototype.fwdDelete=function() {\r
- var crsron=this.cursoractive;\r
- if (crsron) this.cursorOff();\r
- if (this.isPrintable(this.charBuf[this.r][this.c])) {\r
- this._scrollLeft(this.r,this.c);\r
- if (!this.isPrintable(this.charBuf[this.r][this.c])) this.insert=false;\r
- }\r
- this._cursorReset(crsron);\r
-}\r
-\r
-Terminal.prototype.prompt=function() {\r
- this.lock=true;\r
- if (this.c>0) this.newLine();\r
- this.type(this.ps);\r
- this._charOut(1);\r
- this.lock=false;\r
- this.cursorOn();\r
-}\r
-\r
-Terminal.prototype._scrollLeft=function(r,c) {\r
- var rows=new Array();\r
- rows[0]=r;\r
- while (this.isPrintable(this.charBuf[r][c])) {\r
- var ri=r;\r
- var ci=c+1;\r
- if (ci==this.maxCols) {\r
- if (ri<this.maxLines-1) {\r
- ci=0;\r
- ri++;\r
- rows[rows.length]=ri;\r
- }\r
- else {\r
- break;\r
- }\r
- }\r
- this.charBuf[r][c]=this.charBuf[ri][ci];\r
- this.styleBuf[r][c]=this.styleBuf[ri][ci];\r
- c=ci;\r
- r=ri;\r
- }\r
- if (this.charBuf[r][c]!=0) this.charBuf[r][c]=0;\r
- for (var i=0; i<rows.length; i++) this.redraw(rows[i]);\r
-}\r
-\r
-Terminal.prototype._scrollRight=function(r,c) {\r
- var rows=new Array();\r
- var end=this._getLineEnd(r,c);\r
- var ri=end[0];\r
- var ci=end[1];\r
- if ((ci==this.maxCols-1) && (ri==this.maxLines-1)) {\r
- if (r==0) return;\r
- this._scrollLines(0,this.maxLines);\r
- this.r--;\r
- r--;\r
- ri--;\r
- }\r
- rows[r]=1;\r
- while (this.isPrintable(this.charBuf[ri][ci])) {\r
- var rt=ri;\r
- var ct=ci+1;\r
- if (ct==this.maxCols) {\r
- ct=0;\r
- rt++;\r
- rows[rt]=1;\r
- }\r
- this.charBuf[rt][ct]=this.charBuf[ri][ci];\r
- this.styleBuf[rt][ct]=this.styleBuf[ri][ci];\r
- if ((ri==r) && (ci==c)) break;\r
- ci--;\r
- if (ci<0) {\r
- ci=this.maxCols-1;\r
- ri--;\r
- rows[ri]=1;\r
- }\r
- }\r
- for (var i=r; i<this.maxLines; i++) {\r
- if (rows[i]) this.redraw(i);\r
- }\r
-}\r
-\r
-Terminal.prototype._getLineEnd=function(r,c) {\r
- if (!this.isPrintable(this.charBuf[r][c])) {\r
- c--;\r
- if (c<0) {\r
- if (r>0) {\r
- r--;\r
- c=this.maxCols-1;\r
- }\r
- else {\r
- c=0;\r
- }\r
- }\r
- }\r
- if (this.isPrintable(this.charBuf[r][c])) {\r
- while (true) {\r
- var ri=r;\r
- var ci=c+1;\r
- if (ci==this.maxCols) {\r
- if (ri<this.maxLines-1) {\r
- ri++;\r
- ci=0;\r
- }\r
- else {\r
- break;\r
- }\r
- }\r
- if (!this.isPrintable(this.charBuf[ri][ci])) break;\r
- c=ci;\r
- r=ri;\r
- }\r
- }\r
- return [r,c];\r
-}\r
-\r
-Terminal.prototype._getLineStart=function(r,c) {\r
- // not used by now, just in case anyone needs this ...\r
- var ci, ri;\r
- if (!this.isPrintable(this.charBuf[r][c])) {\r
- ci=c-1;\r
- ri=r;\r
- if (ci<0) {\r
- if (ri==0) return [0,0];\r
- ci=this.maxCols-1;\r
- ri--;\r
- }\r
- if (!this.isPrintable(this.charBuf[ri][ci])) return [r,c]\r
- else {\r
- r=ri;\r
- c=ci;\r
- }\r
- }\r
- while (true) {\r
- var ri=r;\r
- var ci=c-1;\r
- if (ci<0) {\r
- if (ri==0) break;\r
- ci=this.maxCols-1;\r
- ri--;\r
- }\r
- if (!this.isPrintable(this.charBuf[ri][ci])) break;;\r
- r=ri;\r
- c=ci;\r
- }\r
- return [r,c];\r
-}\r
-\r
-Terminal.prototype._getLine=function() {\r
- var end=this._getLineEnd(this.r,this.c);\r
- var r=end[0];\r
- var c=end[1];\r
- var line=new Array();\r
- while (this.isPrintable(this.charBuf[r][c])) {\r
- line[line.length]=String.fromCharCode(this.charBuf[r][c]);\r
- if (c>0) c--\r
- else if (r>0) {\r
- c=this.maxCols-1;\r
- r--;\r
- }\r
- else break;\r
- }\r
- line.reverse();\r
- return line.join('');\r
-}\r
-\r
-Terminal.prototype._clearLine=function() {\r
- var end=this._getLineEnd(this.r,this.c);\r
- var r=end[0];\r
- var c=end[1];\r
- var line='';\r
- while (this.isPrintable(this.charBuf[r][c])) {\r
- this.charBuf[r][c]=0;\r
- if (c>0) {\r
- c--;\r
- }\r
- else if (r>0) {\r
- this.redraw(r);\r
- c=this.maxCols-1;\r
- r--;\r
- }\r
- else break;\r
- }\r
- if (r!=end[0]) this.redraw(r);\r
- c++;\r
- this.cursorSet(r,c);\r
- this.insert=false;\r
-}\r
-\r
-Terminal.prototype.isPrintable=function(ch, unicodePage1only) {\r
- if ((unicodePage1only) && (ch>255)) {\r
- return ((ch==termKey.EURO) && (this.printEuro))? true:false;\r
- }\r
- return (\r
- ((ch>=32) && (ch!=termKey.DEL)) ||\r
- ((this.printTab) && (ch==termKey.TAB))\r
- );\r
-}\r
-\r
-// keyboard focus\r
-\r
-Terminal.prototype.focus=function() {\r
- TermGlobals.activeTerm=this;\r
-}\r
-\r
-// global store and functions\r
-\r
-var TermGlobals={\r
- termToInitialze:null,\r
- activeTerm:null,\r
- kbdEnabled:false,\r
- keylock:false,\r
- lcMorePrompt1: ' -- MORE -- ',\r
- lcMorePromtp1Style: 1,\r
- lcMorePrompt2: ' (Type: space to continue, \'q\' to quit)',\r
- lcMorePrompt2Style: 0,\r
- lcMoreKeyAbort: 113,\r
- lcMoreKeyContinue: 32\r
-};\r
-\r
-// keybard focus\r
-\r
-TermGlobals.setFocus=function(termref) {\r
- TermGlobals.activeTerm=termref;\r
-}\r
-\r
-// text related\r
-\r
-TermGlobals.normalize=function(n,m) {\r
- var s=''+n;\r
- while (s.length<m) s='0'+s;\r
- return s;\r
-}\r
-\r
-TermGlobals.fillLeft=function(t,n) {\r
- if (typeof t != 'string') t=''+t;\r
- while (t.length<n) t=' '+t;\r
- return t;\r
-}\r
-\r
-TermGlobals.center=function(t,l) {\r
- var s='';\r
- for (var i=t.length; i<l; i+=2) s+=' ';\r
- return s+t;\r
-}\r
-\r
-TermGlobals.stringReplace=function(s1,s2,t) {\r
- var l1=s1.length;\r
- var l2=s2.length;\r
- var ofs=t.indexOf(s1);\r
- while (ofs>=0) {\r
- t=t.substring(0,ofs)+s2+t.substring(ofs+l1);\r
- ofs=t.indexOf(s1,ofs+l2);\r
- }\r
- return t;\r
-}\r
-\r
-// keyboard\r
-\r
-var termKey= {\r
- // special key codes\r
- 'NUL': 0x00,\r
- 'SOH': 0x01,\r
- 'STX': 0x02,\r
- 'ETX': 0x03,\r
- 'EOT': 0x04,\r
- 'ENQ': 0x05,\r
- 'ACK': 0x06,\r
- 'BEL': 0x07,\r
- 'BS': 0x08,\r
- 'HT': 0x09,\r
- 'TAB': 0x09,\r
- 'LF': 0x0A,\r
- 'VT': 0x0B,\r
- 'FF': 0x0C,\r
- 'CR': 0x0D,\r
- 'SO': 0x0E,\r
- 'SI': 0x0F,\r
- 'DLE': 0x10,\r
- 'DC1': 0x11,\r
- 'DC2': 0x12,\r
- 'DC3': 0x13,\r
- 'DC4': 0x14,\r
- 'NAK': 0x15,\r
- 'SYN': 0x16,\r
- 'ETB': 0x17,\r
- 'CAN': 0x18,\r
- 'EM': 0x19,\r
- 'SUB': 0x1A,\r
- 'ESC': 0x1B,\r
- 'IS4': 0x1C,\r
- 'IS3': 0x1D,\r
- 'IS2': 0x1E,\r
- 'IS1': 0x1F,\r
- 'DEL': 0x7F,\r
- // other specials\r
- 'EURO': 0x20AC,\r
- // cursor mapping\r
- 'LEFT': 0x1C,\r
- 'RIGHT': 0x1D,\r
- 'UP': 0x1E,\r
- 'DOWN': 0x1F\r
-};\r
-\r
-var termDomKeyRef = {\r
- DOM_VK_LEFT: termKey.LEFT,\r
- DOM_VK_RIGHT: termKey.RIGHT,\r
- DOM_VK_UP: termKey.UP,\r
- DOM_VK_DOWN: termKey.DOWN,\r
- DOM_VK_BACK_SPACE: termKey.BS,\r
- DOM_VK_RETURN: termKey.CR,\r
- DOM_VK_ENTER: termKey.CR,\r
- DOM_VK_ESCAPE: termKey.ESC,\r
- DOM_VK_DELETE: termKey.DEL,\r
- DOM_VK_TAB: termKey.TAB\r
-};\r
-\r
-TermGlobals.enableKeyboard=function(term) {\r
- if (!this.kbdEnabled) {\r
- if (document.addEventListener) document.addEventListener("keypress", this.keyHandler, true)\r
- else {\r
- if ((self.Event) && (self.Event.KEYPRESS)) document.captureEvents(Event.KEYPRESS);\r
- document.onkeypress = this.keyHandler;\r
- }\r
- window.document.onkeydown=this.keyFix;\r
- this.kbdEnabled=true;\r
- }\r
- this.activeTerm=term;\r
-}\r
-\r
-TermGlobals.keyFix=function(e) {\r
- var term=TermGlobals.activeTerm;\r
- if ((TermGlobals.keylock) || (term.lock)) return true;\r
- if (window.event) {\r
- var ch=window.event.keyCode;\r
- if (!e) e=window.event;\r
- if (e.DOM_VK_UP) {\r
- for (var i in termDomKeyRef) {\r
- if ((e[i]) && (ch == e[i])) {\r
- this.keyHandler({which:termDomKeyRef[i],_remapped:true});\r
- if (e.preventDefault) e.preventDefault();\r
- if (e.stopPropagation) e.stopPropagation();\r
- e.cancleBubble=true;\r
- return false;\r
- }\r
- }\r
- e.cancleBubble=false;\r
- return true;\r
- }\r
- else {\r
- // no DOM support\r
- if ((ch==8) && (!term.isSafari)) TermGlobals.keyHandler({which:termKey.BS,_remapped:true})\r
- else if (ch==9) TermGlobals.keyHandler({which:termKey.TAB,_remapped:true})\r
- else if (ch==37) TermGlobals.keyHandler({which:termKey.LEFT,_remapped:true})\r
- else if (ch==39) TermGlobals.keyHandler({which:termKey.RIGHT,_remapped:true})\r
- else if (ch==38) TermGlobals.keyHandler({which:termKey.UP,_remapped:true})\r
- else if (ch==40) TermGlobals.keyHandler({which:termKey.DOWN,_remapped:true})\r
- else if (ch==127) TermGlobals.keyHandler({which:termKey.DEL,_remapped:true})\r
- else if ((ch>=57373) && (ch<=57376)) {\r
- if (ch==57373) TermGlobals.keyHandler({which:termKey.UP,_remapped:true})\r
- else if (ch==57374) TermGlobals.keyHandler({which:termKey.DOWN,_remapped:true})\r
- else if (ch==57375) TermGlobals.keyHandler({which:termKey.LEFT,_remapped:true})\r
- else if (ch==57376) TermGlobals.keyHandler({which:termKey.RIGHT,_remapped:true});\r
- }\r
- else {\r
- e.cancleBubble=false;\r
- return true;\r
- }\r
- if (e.preventDefault) e.preventDefault();\r
- if (e.stopPropagation) e.stopPropagation();\r
- e.cancleBubble=true;\r
- return false;\r
- }\r
- }\r
-}\r
-\r
-TermGlobals.keyHandler=function(e) {\r
- var term=TermGlobals.activeTerm;\r
- if ((TermGlobals.keylock) || (term.lock)) return true;\r
- if ((window.event) && (window.event.preventDefault)) window.event.preventDefault()\r
- else if ((e) && (e.preventDefault)) e.preventDefault();\r
- if ((window.event) && (window.event.stopPropagation)) window.event.stopPropagation()\r
- else if ((e) && (e.stopPropagation)) e.stopPropagation();\r
- var ch;\r
- var ctrl=false;\r
- var shft=false;\r
- var remapped=false;\r
- if (e) {\r
- ch=e.which;\r
- ctrl=(((e.ctrlKey) && (e.altKey)) || (e.modifiers==2));\r
- shft=((e.shiftKey) || (e.modifiers==4));\r
- if (e._remapped) {\r
- remapped=true;\r
- if (window.event) {\r
- //ctrl=((ctrl) || (window.event.ctrlKey));\r
- ctrl=((ctrl) || ((window.event.ctrlKey) && (!window.event.altKey)));\r
- shft=((shft) || (window.event.shiftKey));\r
- }\r
- }\r
- }\r
- else if (window.event) {\r
- ch=window.event.keyCode;\r
- //ctrl=(window.event.ctrlKey);\r
- ctrl=((window.event.ctrlKey) && (!window.event.altKey)); // allow alt gr == ctrl alts\r
- shft=(window.event.shiftKey);\r
- }\r
- else {\r
- return true;\r
- }\r
- if ((ch=='') && (remapped==false)) {\r
- // map specials\r
- if (e==null) e=window.event;\r
- if ((e.charCode==0) && (e.keyCode)) {\r
- if (e.DOM_VK_UP) {\r
- for (var i in termDomKeyRef) {\r
- if ((e[i]) && (e.keyCode == e[i])) {\r
- ch=termDomKeyRef[i];\r
- break;\r
- }\r
- }\r
- }\r
- else {\r
- // NS4\r
- if (e.keyCode==28) ch=termKey.LEFT\r
- else if (e.keyCode==29) ch=termKey.RIGHT\r
- else if (e.keyCode==30) ch=termKey.UP\r
- else if (e.keyCode==31) ch=termKey.DOWN\r
- // Mozilla alike but no DOM support\r
- else if (e.keyCode==37) ch=termKey.LEFT\r
- else if (e.keyCode==39) ch=termKey.RIGHT\r
- else if (e.keyCode==38) ch=termKey.UP\r
- else if (e.keyCode==40) ch=termKey.DOWN\r
- // just to have the TAB mapping here too\r
- else if (e.keyCode==9) ch=termKey.TAB;\r
- }\r
- }\r
- }\r
- // key actions\r
- if (term.charMode) {\r
- term.insert=false;\r
- term.inputChar=ch;\r
- term.lineBuffer='';\r
- term.handler();\r
- if ((ch<=32) && (window.event)) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- if (!ctrl) {\r
- // special keys\r
- if (ch==termKey.CR) {\r
- term.lock=true;\r
- term.cursorOff();\r
- term.insert=false;\r
- if (term.rawMode) {\r
- term.lineBuffer=term.lastLine;\r
- }\r
- else {\r
- term.lineBuffer=term._getLine();\r
- if (\r
- (term.lineBuffer!='') && ((!term.historyUnique) ||\r
- (term.history.length==0) ||\r
- (term.lineBuffer!=term.history[term.history.length-1]))\r
- ) {\r
- term.history[term.history.length]=term.lineBuffer;\r
- }\r
- term.histPtr=term.history.length;\r
- }\r
- term.lastLine='';\r
- term.inputChar=0;\r
- term.handler();\r
- if (window.event) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- else if (ch==termKey.ESC) {\r
- if (term.conf.closeOnESC) term.close();\r
- if (window.event) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- if ((ch<32) && (term.rawMode)) {\r
- if (window.event) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- else {\r
- if (ch==termKey.LEFT) {\r
- term.cursorLeft();\r
- if (window.event) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- else if (ch==termKey.RIGHT) {\r
- term.cursorRight();\r
- if (window.event) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- else if (ch==termKey.UP) {\r
- term.cursorOff();\r
- if (term.histPtr==term.history.length) term.lastLine=term._getLine();\r
- term._clearLine();\r
- if ((term.history.length) && (term.histPtr>=0)) {\r
- if (term.histPtr>0) term.histPtr--;\r
- term.type(term.history[term.histPtr]);\r
- }\r
- else if (term.lastLine) term.type(term.lastLine);\r
- term.cursorOn();\r
- if (window.event) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- else if (ch==termKey.DOWN) {\r
- term.cursorOff();\r
- if (term.histPtr==term.history.length) term.lastLine=term._getLine();\r
- term._clearLine();\r
- if ((term.history.length) && (term.histPtr<=term.history.length)) {\r
- if (term.histPtr<term.history.length) term.histPtr++;\r
- if (term.histPtr<term.history.length) term.type(term.history[term.histPtr])\r
- else if (term.lastLine) term.type(term.lastLine);\r
- }\r
- else if (term.lastLine) term.type(term.lastLine);\r
- term.cursorOn();\r
- if (window.event) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- else if (ch==termKey.BS) {\r
- term.backspace();\r
- if (window.event) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- else if (ch==termKey.DEL) {\r
- if (term.DELisBS) term.backspace()\r
- else term.fwdDelete();\r
- if (window.event) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- }\r
- }\r
- if (term.rawMode) {\r
- if (term.isPrintable(ch)) {\r
- term.lastLine+=String.fromCharCode(ch);\r
- }\r
- if ((ch==32) && (window.event)) window.event.cancleBubble=true\r
- else if ((window.opera) && (window.event)) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- else {\r
- if ((term.conf.catchCtrlH) && ((ch==termKey.BS) || ((ctrl) && (ch==72)))) {\r
- // catch ^H\r
- term.backspace();\r
- if (window.event) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- else if ((term.ctrlHandler) && ((ch<32) || ((ctrl) && (term.isPrintable(ch,true))))) {\r
- if (((ch>=65) && (ch<=96)) || (ch==63)) {\r
- // remap canonical\r
- if (ch==63) ch=31\r
- else ch-=64;\r
- }\r
- term.inputChar=ch;\r
- term.ctrlHandler();\r
- if (window.event) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- else if ((ctrl) || (!term.isPrintable(ch,true))) {\r
- if (window.event) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- else if (term.isPrintable(ch,true)) {\r
- if (term.blinkTimer) clearTimeout(term.blinkTimer);\r
- if (term.insert) {\r
- term.cursorOff();\r
- term._scrollRight(term.r,term.c);\r
- }\r
- term._charOut(ch);\r
- term.cursorOn();\r
- if ((ch==32) && (window.event)) window.event.cancleBubble=true\r
- else if ((window.opera) && (window.event)) window.event.cancleBubble=true;\r
- return false;\r
- }\r
- }\r
- return true;\r
-}\r
-\r
-// term gui\r
-\r
-TermGlobals.hasSubDivs=false;\r
-TermGlobals.hasLayers=false;\r
-TermGlobals.termStringStart='';\r
-TermGlobals.termStringEnd='';\r
-\r
-TermGlobals.termSpecials=new Array();\r
-TermGlobals.termSpecials[0]=' ';\r
-TermGlobals.termSpecials[1]=' ';\r
-TermGlobals.termSpecials[9]=' ';\r
-TermGlobals.termSpecials[32]=' ';\r
-TermGlobals.termSpecials[34]='"';\r
-TermGlobals.termSpecials[38]='&';\r
-TermGlobals.termSpecials[60]='<';\r
-TermGlobals.termSpecials[62]='>';\r
-TermGlobals.termSpecials[127]='◊';\r
-TermGlobals.termSpecials[0x20AC]='€';\r
-\r
-TermGlobals.termStyles=new Array(1,2,4,8);\r
-TermGlobals.termStyleOpen=new Array();\r
-TermGlobals.termStyleClose=new Array();\r
-TermGlobals.termStyleOpen[1]='<span class="termReverse">';\r
-TermGlobals.termStyleClose[1]='<\/span>';\r
-TermGlobals.termStyleOpen[2]='<u>';\r
-TermGlobals.termStyleClose[2]='<\/u>';\r
-TermGlobals.termStyleOpen[4]='<i>';\r
-TermGlobals.termStyleClose[4]='<\/i>';\r
-TermGlobals.termStyleOpen[8]='<strike>';\r
-TermGlobals.termStyleClose[8]='<\/strike>';\r
-\r
-Terminal.prototype._makeTerm=function(rebuild) {\r
- window.status='Building terminal ...';\r
- TermGlobals.hasLayers=(document.layers)? true:false;\r
- TermGlobals.hasSubDivs=(navigator.userAgent.indexOf('Gecko')<0);\r
- var divPrefix=this.termDiv+'_r';\r
- var s='';\r
- s+='<table border="0" cellspacing="0" cellpadding="'+this.conf.frameWidth+'">\n';\r
- s+='<tr><td bgcolor="'+this.conf.frameColor+'"><table border="0" cellspacing="0" cellpadding="2"><tr><td bgcolor="'+this.conf.bgColor+'"><table border="0" cellspacing="0" cellpadding="0">\n';\r
- var rstr='';\r
- for (var c=0; c<this.conf.cols; c++) rstr+=' ';\r
- for (var r=0; r<this.conf.rows; r++) {\r
- var termid=((TermGlobals.hasLayers) || (TermGlobals.hasSubDivs))? '' : ' id="'+divPrefix+r+'"';\r
- s+='<tr><td nowrap height="'+this.conf.rowHeight+'"'+termid+' class="'+this.conf.fontClass+'">'+rstr+'<\/td><\/tr>\n';\r
- }\r
- s+='<\/table><\/td><\/tr>\n';\r
- s+='<\/table><\/td><\/tr>\n';\r
- s+='<\/table>\n';\r
- var termOffset=2+this.conf.frameWidth;\r
- if (TermGlobals.hasLayers) {\r
- for (var r=0; r<this.conf.rows; r++) {\r
- s+='<layer name="'+divPrefix+r+'" top="'+(termOffset+r*this.conf.rowHeight)+'" left="'+termOffset+'" class="'+this.conf.fontClass+'"><\/layer>\n';\r
- }\r
- this.ns4ParentDoc=document.layers[this.termDiv].document;\r
- TermGlobals.termStringStart='<table border="0" cellspacing="0" cellpadding="0"><tr><td nowrap height="'+this.conf.rowHeight+'" class="'+this.conf.fontClass+'">';\r
- TermGlobals.termStringEnd='<\/td><\/tr><\/table>';\r
- }\r
- else if (TermGlobals.hasSubDivs) {\r
- for (var r=0; r<this.conf.rows; r++) {\r
- s+='<div id="'+divPrefix+r+'" style="position:absolute; top:'+(termOffset+r*this.conf.rowHeight)+'px; left: '+termOffset+'px;" class="'+this.conf.fontClass+'"><\/div>\n';\r
- }\r
- TermGlobals.termStringStart='<table border="0" cellspacing="0" cellpadding="0"><tr><td nowrap height="'+this.conf.rowHeight+'" class="'+this.conf.fontClass+'">';\r
- TermGlobals.termStringEnd='<\/td><\/tr><\/table>';\r
- }\r
- TermGlobals.writeElement(this.termDiv,s);\r
- if (!rebuild) {\r
- TermGlobals.setElementXY(this.termDiv,this.conf.x,this.conf.y);\r
- TermGlobals.setVisible(this.termDiv,1);\r
- }\r
- window.status='';\r
-}\r
-\r
-Terminal.prototype.rebuild=function() {\r
- // check for bounds and array lengths\r
- var rl=this.conf.rows;\r
- var cl=this.conf.cols;\r
- for (var r=0; r<rl; r++) {\r
- var cbr=this.charBuf[r];\r
- if (!cbr) {\r
- this.charBuf[r]=this.getRowArray(cl,0);\r
- this.styleBuf[r]=this.getRowArray(cl,0);\r
- }\r
- else if (cbr.length<cl) {\r
- for (var c=cbr.length; c<cl; c++) {\r
- this.charBuf[r][c]=0;\r
- this.styleBuf[r][c]=0;\r
- }\r
- }\r
- }\r
- var resetcrsr=false;\r
- if (this.r>=rl) {\r
- r=rl-1;\r
- resetcrsr=true;\r
- }\r
- if (this.c>=cl) {\r
- c=cl-1;\r
- resetcrsr=true;\r
- }\r
- if ((resetcrsr) && (this.cursoractive)) this.cursorOn();\r
- // and actually rebuild\r
- this._makeTerm(true);\r
- for (var r=0; r<rl; r++) {\r
- this.redraw(r);\r
- }\r
-}\r
-\r
-Terminal.prototype.moveTo=function(x,y) {\r
- TermGlobals.setElementXY(this.termDiv,x,y);\r
-}\r
-\r
-Terminal.prototype.resizeTo=function(x,y) {\r
- if (this.termDivReady()) {\r
- x=parseInt(x,10);\r
- y=parseInt(y,10);\r
- if ((isNaN(x)) || (isNaN(y)) || (x<4) || (y<2)) return false;\r
- this.maxCols=this.conf.cols=x;\r
- this.maxLines=this.conf.rows=y;\r
- this._makeTerm();\r
- this.clear();\r
- return true;\r
- }\r
- else return false;\r
-}\r
-\r
-Terminal.prototype.redraw=function(r) {\r
- var s=TermGlobals.termStringStart;\r
- var curStyle=0;\r
- var tstls=TermGlobals.termStyles;\r
- var tscls=TermGlobals.termStyleClose;\r
- var tsopn=TermGlobals.termStyleOpen;\r
- var tspcl=TermGlobals.termSpecials;\r
- var t_cb=this.charBuf;\r
- var t_sb=this.styleBuf;\r
- for (var i=0; i<this.conf.cols; i++) {\r
- var c=t_cb[r][i];\r
- var cs=t_sb[r][i];\r
- if (cs!=curStyle) {\r
- if (curStyle) {\r
- for (var k=tstls.length-1; k>=0; k--) {\r
- var st=tstls[k];\r
- if (curStyle&st) s+=tscls[st];\r
- }\r
- }\r
- curStyle=cs;\r
- for (var k=0; k<tstls.length; k++) {\r
- var st=tstls[k];\r
- if (curStyle&st) s+=tsopn[st];\r
- }\r
- }\r
- s+= (tspcl[c])? tspcl[c] : String.fromCharCode(c);\r
- }\r
- if (curStyle>0) {\r
- for (var k=tstls.length-1; k>=0; k--) {\r
- var st=tstls[k];\r
- if (curStyle&st) s+=tscls[st];\r
- }\r
- }\r
- s+=TermGlobals.termStringEnd;\r
- TermGlobals.writeElement(this.termDiv+'_r'+r,s,this.ns4ParentDoc);\r
-}\r
-\r
-Terminal.prototype.guiReady=function() {\r
- ready=true;\r
- if (TermGlobals.guiElementsReady(this.termDiv, self.document)) {\r
- for (var r=0; r<this.conf.rows; r++) {\r
- if (TermGlobals.guiElementsReady(this.termDiv+'_r'+r,this.ns4ParentDoc)==false) {\r
- ready=false;\r
- break;\r
- }\r
- }\r
- }\r
- else ready=false;\r
- return ready;\r
-}\r
-\r
-Terminal.prototype.termDivReady=function() {\r
- if (document.layers) {\r
- return (document.layers[this.termDiv])? true:false;\r
- }\r
- else if (document.getElementById) {\r
- return (document.getElementById(this.termDiv))? true:false;\r
- }\r
- else if (document.all) {\r
- return (document.all[this.termDiv])? true:false;\r
- }\r
- else {\r
- return false;\r
- }\r
-}\r
-\r
-Terminal.prototype.getDimensions=function() {\r
- var w=0;\r
- var h=0;\r
- var d=this.termDiv;\r
- if (document.layers) {\r
- if (document.layers[d]) {\r
- w=document.layers[d].clip.right;\r
- h=document.layers[d].clip.bottom;\r
- }\r
- }\r
- else if (document.getElementById) {\r
- var obj=document.getElementById(d);\r
- if ((obj) && (obj.firstChild)) {\r
- w=parseInt(obj.firstChild.offsetWidth,10);\r
- h=parseInt(obj.firstChild.offsetHeight,10);\r
- }\r
- else if ((obj) && (obj.children) && (obj.children[0])) {\r
- w=parseInt(obj.children[0].offsetWidth,10);\r
- h=parseInt(obj.children[0].offsetHeight,10);\r
- }\r
- }\r
- else if (document.all) {\r
- var obj=document.all[d];\r
- if ((obj) && (obj.children) && (obj.children[0])) {\r
- w=parseInt(obj.children[0].offsetWidth,10);\r
- h=parseInt(obj.children[0].offsetHeight,10);\r
- }\r
- }\r
- return { width: w, height: h };\r
-}\r
-\r
-// basic dynamics\r
-\r
-TermGlobals.writeElement=function(e,t,d) {\r
- if (document.layers) {\r
- var doc=(d)? d : self.document;\r
- doc.layers[e].document.open();\r
- doc.layers[e].document.write(t);\r
- doc.layers[e].document.close();\r
- }\r
- else if (document.getElementById) {\r
- var obj=document.getElementById(e);\r
- obj.innerHTML=t;\r
- }\r
- else if (document.all) {\r
- document.all[e].innerHTML=t;\r
- }\r
-}\r
-\r
-TermGlobals.setElementXY=function(d,x,y) {\r
- if (document.layers) {\r
- document.layers[d].moveTo(x,y);\r
- }\r
- else if (document.getElementById) {\r
- var obj=document.getElementById(d);\r
- obj.style.left=x+'px';\r
- obj.style.top=y+'px';\r
- }\r
- else if (document.all) {\r
- document.all[d].style.left=x+'px';\r
- document.all[d].style.top=y+'px';\r
- }\r
-}\r
-\r
-TermGlobals.setVisible=function(d,v) {\r
- if (document.layers) {\r
- document.layers[d].visibility= (v)? 'show':'hide';\r
- }\r
- else if (document.getElementById) {\r
- var obj=document.getElementById(d);\r
- obj.style.visibility= (v)? 'visible':'hidden';\r
- }\r
- else if (document.all) {\r
- document.all[d].style.visibility= (v)? 'visible':'hidden';\r
- }\r
-}\r
-\r
-TermGlobals.setDisplay=function(d,v) {\r
- if (document.getElementById) {\r
- var obj=document.getElementById(d);\r
- obj.style.display=v;\r
- }\r
- else if (document.all) {\r
- document.all[d].style.display=v;\r
- }\r
-}\r
-\r
-TermGlobals.guiElementsReady=function(e,d) {\r
- if (document.layers) {\r
- var doc=(d)? d : self.document;\r
- return ((doc) && (doc.layers[e]))? true:false;\r
- }\r
- else if (document.getElementById) {\r
- return (document.getElementById(e))? true:false;\r
- }\r
- else if (document.all) {\r
- return (document.all[e])? true:false;\r
- }\r
- else return false;\r
-}\r
-\r
-\r
-// constructor mods (ie4 fix)\r
-\r
-var termString_keyref;\r
-var termString_keycoderef;\r
-\r
-function termString_makeKeyref() {\r
- termString_keyref= new Array();\r
- termString_keycoderef= new Array();\r
- var hex= new Array('A','B','C','D','E','F');\r
- for (var i=0; i<=15; i++) {\r
- var high=(i<10)? i:hex[i-10];\r
- for (var k=0; k<=15; k++) {\r
- var low=(k<10)? k:hex[k-10];\r
- var cc=i*16+k;\r
- if (cc>=32) {\r
- var cs=unescape("%"+high+low);\r
- termString_keyref[cc]=cs;\r
- termString_keycoderef[cs]=cc;\r
- }\r
- }\r
- }\r
-}\r
-\r
-if (!String.fromCharCode) {\r
- termString_makeKeyref();\r
- String.fromCharCode=function(cc) {\r
- return (cc!=null)? termString_keyref[cc] : '';\r
- };\r
-}\r
-if (!String.prototype.charCodeAt) {\r
- if (!termString_keycoderef) termString_makeKeyref();\r
- String.prototype.charCodeAt=function(n) {\r
- cs=this.charAt(n);\r
- return (termString_keycoderef[cs])? termString_keycoderef[cs] : 0;\r
- };\r
-}\r
-\r
-// eof
\ No newline at end of file
+++ /dev/null
-/*\r
- termlib_parser.js v.1.0\r
- command line parser for termlib.js\r
- (c) Norbert Landsteiner 2005\r
- mass:werk - media environments\r
- <http://www.masswerk.at>\r
-\r
- you are free to use this parser under the "termlib.js" license.\r
-\r
- usage: call "parseLine(this)" from your Terminal handler\r
- parsed args in this.argv\r
- quoting levels per arg in this.argQL (value: quote char)\r
- this.argc: pointer to this.argv and this.argQL (used by parserGetopt)\r
- call parseretopt(this, "<options>") from your handler to get opts\r
- (returns an object with properties for every option flag. any float\r
- values are stored in Object.<flag>.value; illegal opts in array\r
- Object.illegals)\r
-\r
- configuration: you may want to overide the follow objects (or add properties):\r
- parserWhiteSpace: chars to be parsed as whitespace\r
- parserQuoteChars: chars to be parsed as quotes\r
- parserSingleEscapes: chars to escape a quote or escape expression\r
- parserOptionChars: chars that start an option\r
- parserEscapeExpressions: chars that start escape expressions\r
-*/\r
-\r
-// chars to be parsed as white space\r
-var parserWhiteSpace = {\r
- ' ': true,\r
- '\t': true\r
-}\r
-\r
-// chars to be parsed as quotes\r
-var parserQuoteChars = {\r
- '"': true,\r
- "'": true,\r
- '`': true\r
-};\r
-\r
-// chars to be parsed as escape char\r
-var parserSingleEscapes = {\r
- '\\': true\r
-};\r
-\r
-// chars that mark the start of an option-expression\r
-// for use with parserGetopt\r
-var parserOptionChars = {\r
- '-': true\r
-}\r
-\r
-// chars that start escape expressions (value = handler)\r
-// plugin handlers for ascii escapes or variable substitution\r
-var parserEscapeExpressions = {\r
- '%': parserHexExpression\r
-}\r
-\r
-function parserHexExpression(termref, pointer, echar, quotelevel) {\r
- /* example for parserEscapeExpressions\r
- params:\r
- termref: ref to Terminal instance\r
- pointer: position in termref.lineBuffer (echar)\r
- echar: escape character found\r
- quotelevel: current quoting level (quote char or empty)\r
- char under pointer will be ignored\r
- the return value is added to the current argument\r
- */\r
- // convert hex values to chars (e.g. %20 => <SPACE>)\r
- if (termref.lineBuffer.length > pointer+2) {\r
- // get next 2 chars\r
- var hi = termref.lineBuffer.charAt(pointer+1);\r
- var lo = termref.lineBuffer.charAt(pointer+2);\r
- lo = lo.toUpperCase();\r
- hi = hi.toUpperCase();\r
- // check for valid hex digits\r
- if ((((hi>='0') && (hi<='9')) || ((hi>='A') && ((hi<='F')))) &&\r
- (((lo>='0') && (lo<='9')) || ((lo>='A') && ((lo<='F'))))) {\r
- // next 2 chars are valid hex, so strip them from lineBuffer\r
- parserEscExprStrip(termref, pointer+1, pointer+3);\r
- // and return the char\r
- return String.fromCharCode(parseInt(hi+lo, 16));\r
- }\r
- }\r
- // if not handled return the escape character (=> no conversion)\r
- return echar;\r
-}\r
-\r
-function parserEscExprStrip(termref, from, to) {\r
- // strip characters from termref.lineBuffer (for use with escape expressions)\r
- termref.lineBuffer =\r
- termref.lineBuffer.substring(0, from) +\r
- termref.lineBuffer.substring(to);\r
-}\r
-\r
-function parserGetopt(termref, optsstring) {\r
- // scans argv form current position of argc for opts\r
- // arguments in argv must not be quoted\r
- // returns an object with a property for every option flag found\r
- // option values (absolute floats) are stored in Object.<opt>.value (default -1)\r
- // the property "illegals" contains an array of all flags found but not in optstring\r
- // argc is set to first argument that is not an option\r
- var opts = { 'illegals':[] };\r
- while ((termref.argc < termref.argv.length) && (termref.argQL[termref.argc]=='')) {\r
- var a = termref.argv[termref.argc];\r
- if ((a.length>0) && (parserOptionChars[a.charAt(0)])) {\r
- var i = 1;\r
- while (i<a.length) {\r
- var c=a.charAt(i);\r
- var v = '';\r
- while (i<a.length-1) {\r
- var nc=a.charAt(i+1);\r
- if ((nc=='.') || ((nc>='0') && (nc<='9'))) {\r
- v += nc;\r
- i++;\r
- }\r
- else break;\r
- }\r
- if (optsstring.indexOf(c)>=0) {\r
- opts[c] = (v == '')? {value:-1} : (isNaN(v))? {value:0} : {value:parseFloat(v)};\r
- }\r
- else {\r
- opts.illegals[opts.illegals.length]=c;\r
- }\r
- i++;\r
- }\r
- termref.argc++;\r
- }\r
- else break;\r
- }\r
- return opts;\r
-}\r
-\r
-function parseLine(termref) {\r
- // stand-alone parser, takes a Terminal instance as argument\r
- // parses the command line and stores results as instance properties\r
- // argv: list of parsed arguments\r
- // argQL: argument's quoting level (<empty> or quote character)\r
- // argc: cursur for argv, set initinally to zero (0)\r
- // open quote strings are not an error but automatically closed.\r
- var argv = ['']; // arguments vector\r
- var argQL = ['']; // quoting level\r
- var argc = 0; // arguments cursor\r
- var escape = false ; // escape flag\r
- for (var i=0; i<termref.lineBuffer.length; i++) {\r
- var ch= termref.lineBuffer.charAt(i);\r
- if (escape) {\r
- argv[argc] += ch;\r
- escape = false;\r
- }\r
- else if (parserEscapeExpressions[ch]) {\r
- var v = parserEscapeExpressions[ch](termref, i, ch, argQL[argc]);\r
- if (typeof v != 'undefined') argv[argc] += v;\r
- }\r
- else if (parserQuoteChars[ch]) {\r
- if (argQL[argc]) {\r
- if (argQL[argc] == ch) {\r
- argc ++;\r
- argv[argc] = argQL[argc] = '';\r
- }\r
- else {\r
- argv[argc] += ch;\r
- }\r
- }\r
- else {\r
- if (argv[argc] != '') {\r
- argc ++;\r
- argv[argc] = '';\r
- argQL[argc] = ch;\r
- }\r
- else {\r
- argQL[argc] = ch;\r
- }\r
- }\r
- }\r
- else if (parserWhiteSpace[ch]) {\r
- if (argQL[argc]) {\r
- argv[argc] += ch;\r
- }\r
- else if (argv[argc] != '') {\r
- argc++;\r
- argv[argc] = argQL[argc] = '';\r
- }\r
- }\r
- else if (parserSingleEscapes[ch]) {\r
- escape = true;\r
- }\r
- else {\r
- argv[argc] += ch;\r
- }\r
- }\r
- if ((argv[argc] == '') && (!argQL[argc])) {\r
- argv.length--;\r
- argQL.length--;\r
- }\r
- termref.argv = argv;\r
- termref.argQL = argQL;\r
- termref.argc = 0;\r
-}\r
-\r
-// eof
\ No newline at end of file
+++ /dev/null
-Web interface for Factor to Javascript compiler
+++ /dev/null
-Slava Pestov
+++ /dev/null
-! Copyright (C) 2005, 2007 Slava Pestov.
-! See http://factorcode.org/license.txt for BSD license.
-USING: kernel furnace furnace.validator http.server.responders
- help help.topics html splitting sequences words strings
- quotations macros vocabs tools.browser combinators
- arrays io.files ;
-IN: webapps.help
-
-! : string>topic ( string -- topic )
- ! " " split dup length 1 = [ first ] when ;
-
-: show-help ( topic -- )
- serving-html
- dup article-title [
- [ help ] with-html-stream
- ] simple-html-document ;
-
-\ show-help {
- { "topic" }
-} define-action
-\ show-help { { "topic" "handbook" } } default-values
-
-M: link browser-link-href
- link-name
- dup word? over f eq? or [
- browser-link-href
- ] [
- dup array? [ " " join ] when
- [ show-help ] curry quot-link
- ] if ;
-
-: show-word ( word vocab -- )
- lookup show-help ;
-
-\ show-word {
- { "word" }
- { "vocab" }
-} define-action
-\ show-word { { "word" "call" } { "vocab" "kernel" } } default-values
-
-M: f browser-link-href
- drop \ f browser-link-href ;
-
-M: word browser-link-href
- dup word-name swap word-vocabulary
- [ show-word ] 2curry quot-link ;
-
-: show-vocab ( vocab -- )
- f >vocab-link show-help ;
-
-\ show-vocab {
- { "vocab" }
-} define-action
-
-\ show-vocab { { "vocab" "kernel" } } default-values
-
-M: vocab-spec browser-link-href
- vocab-name [ show-vocab ] curry quot-link ;
-
-: show-vocabs-tagged ( tag -- )
- <vocab-tag> show-help ;
-
-\ show-vocabs-tagged {
- { "tag" }
-} define-action
-
-M: vocab-tag browser-link-href
- vocab-tag-name [ show-vocabs-tagged ] curry quot-link ;
-
-: show-vocabs-by ( author -- )
- <vocab-author> show-help ;
-
-\ show-vocabs-by {
- { "author" }
-} define-action
-
-M: vocab-author browser-link-href
- vocab-author-name [ show-vocabs-by ] curry quot-link ;
-
-"help" "show-help" "extra/webapps/help" web-app
-
-! Hard-coding for factorcode.org
-PREDICATE: pathname resource-pathname
- pathname-string "resource:" head? ;
-
-M: resource-pathname browser-link-href
- pathname-string
- "resource:" ?head drop
- "/responder/source/" swap append ;
+++ /dev/null
-Chris Double
+++ /dev/null
-! cont-number-guess
-!
-! Copyright (C) 2004 Chris Double.
-!
-! Redistribution and use in source and binary forms, with or without
-! modification, are permitted provided that the following conditions are met:
-!
-! 1. Redistributions of source code must retain the above copyright notice,
-! this list of conditions and the following disclaimer.
-!
-! 2. Redistributions in binary form must reproduce the above copyright notice,
-! this list of conditions and the following disclaimer in the documentation
-! and/or other materials provided with the distribution.
-!
-! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
-! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
-! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
-! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
-! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
-! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
-! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
-! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-!
-! This example modifies the console based 'numbers-game' example
-! in a very minimal way to demonstrate conversion of a console
-! program to a web based application.
-!
-! All that was required was changing the input and output functions
-! to use HTML. The remaining code was untouched.
-!
-! The result is not that pretty but it shows the basic idea.
-USING: kernel math parser html html.elements io namespaces
-math.parser random webapps.continuation ;
-
-IN: webapps.numbers
-
-: web-print ( str -- )
- #! Display the string in a web page.
- [
- swap dup
- <html>
- <head> <title> write </title> </head>
- <body>
- <p> write </p>
- <p> <a =href a> "Press to continue" write </a> </p>
- </body>
- </html>
- ] show 2drop ;
-
-: read-number ( -- )
- [
- <html>
- <head> <title> "Enter a number" write </title> </head>
- <body>
- <form =action "post" =method form>
- <p>
- "Enter a number:" write
- <input "text" =type "num" =name "20" =size input/>
- <input "submit" =type "Press to continue" =value input/>
- </p>
- </form>
- </body>
- </html>
- ] show [ "num" get ] bind string>number ;
-
-: guess-banner
- "I'm thinking of a number between 0 and 100." web-print ;
-: guess-prompt ;
-: too-high "Too high" web-print ;
-: too-low "Too low" web-print ;
-: correct "Correct - you win!" web-print ;
-: inexact-guess ( actual guess -- )
- < [ too-high ] [ too-low ] if ;
-
-: judge-guess ( actual guess -- ? )
- 2dup = [
- 2drop correct f
- ] [
- inexact-guess t
- ] if ;
-
-: number-to-guess ( -- n ) 100 random ;
-
-: numbers-game-loop ( actual -- )
- dup guess-prompt read-number judge-guess [
- numbers-game-loop
- ] [
- drop
- ] if ;
-
-: numbers-game number-to-guess numbers-game-loop ;
-
-"numbers-game" [ numbers-game ] install-cont-responder
+++ /dev/null
-<% USING: io math math.parser namespaces furnace ; %>
-
-<h1>Annotate</h1>
-
-<form method="POST" action="/responder/pastebin/annotate-paste">
-
-<table>
-
-<tr>
-<th align="right">Summary:</th>
-<td><input type="TEXT" name="summary" value="<% "summary" render %>" /></td>
-<td align="left" class="error"><% "summary" "*Required" render-error %></td>
-</tr>
-
-<tr>
-<th align="right">Your name:</th>
-<td><input type="TEXT" name="author" value="<% "author" render %>" /></td>
-<td class="error"><% "author" "*Required" render-error %></td>
-</tr>
-
-<tr>
-<th align="right">File type:</th>
-<td><% "modes" render-template %></td>
-</tr>
-
-<!--
-<tr>
-<th align="right">Channel:</th>
-<td><input type="TEXT" name="channel" value="#concatenative" /></td>
-</tr>
--->
-
-<tr>
-<td></td>
-<td colspan="2" class="error" align="left"><% "contents" "*Required" render-error %></td>
-</tr>
-
-<tr>
-<th align="right" valign="top">Content:</th>
-<td colspan="2"><textarea rows="24" cols="60" name="contents"><% "contents" render %></textarea></td>
-</tr>
-</table>
-
-<input type="hidden" name="n" value="<% "n" get number>string write %>" />
-<input type="hidden" name="furnace-form-submitted" value="annotate-paste"/>
-<input type="SUBMIT" value="Annotate" />
-</form>
+++ /dev/null
-<% USING: namespaces io furnace calendar ; %>
-
-<h2>Annotation: <% "summary" get write %></h2>
-
-<table>
-<tr><th align="right">Annotation by:</th><td><% "author" get write %></td></tr>
-<tr><th align="right">File type:</th><td><% "mode" get write %></td></tr>
-<tr><th align="right">Created:</th><td><% "date" get timestamp>string write %></td></tr>
-</table>
-
-<% "syntax" render-template %>
+++ /dev/null
-Slava Pestov
+++ /dev/null
-</body>
-
-</html>
+++ /dev/null
-<% USING: namespaces io furnace sequences xmode.code2html webapps.pastebin ; %>
-
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
- <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-
- <title><% "title" get write %></title>
- <link rel="stylesheet" href="style.css" type="text/css" media="screen" title="no title" charset="utf-8" />
- <% default-stylesheet %>
- <link rel="alternate" type="application/atom+xml" title="Pastebin - Atom" href="feed.xml" />
-</head>
-
-<body id="index">
-
- <div class="navbar">
- <% [ paste-list ] "Paste list" render-link %> |
- <% [ new-paste ] "New paste" render-link %> |
- <% [ feed.xml ] "Syndicate" render-link %>
- </div>
- <h1 class="pastebin-title"><% "title" get write %></h1>
+++ /dev/null
-<% USING: furnace xmode.catalog sequences kernel html.elements assocs io sorting continuations ; %>
-
-<select name="mode">
- <% modes keys natural-sort [
- <option dup "mode" session-var = [ "true" =selected ] when option> write </option>
- ] each %>
-</select>
+++ /dev/null
-<% USING: continuations furnace namespaces ; %>
-
-<%
- "New paste" "title" set
- "header" render-template
-%>
-
-<form method="POST" action="/responder/pastebin/submit-paste">
-
-<table>
-
-<tr>
-<th align="right">Summary:</th>
-<td><input type="TEXT" name="summary" value="<% "summary" render %>" /></td>
-<td align="left" class="error"><% "summary" "*Required" render-error %></td>
-</tr>
-
-<tr>
-<th align="right">Your name:</th>
-<td><input type="TEXT" name="author" value="<% "author" render %>" /></td>
-<td class="error"><% "author" "*Required" render-error %></td>
-</tr>
-
-<tr>
-<th align="right">File type:</th>
-<td><% "modes" render-template %></td>
-</tr>
-
-<!--
-<tr>
-<th align="right">Channel:</th>
-<td><input type="TEXT" name="channel" value="#concatenative" /></td>
-</tr>
--->
-
-<tr>
-<td></td>
-<td colspan="2" class="error" align="left"><% "contents" "*Required" render-error %></td>
-</tr>
-
-<tr>
-<th align="right" valign="top">Content:</th>
-<td colspan="2"><textarea rows="24" cols="60" name="contents"><% "contents" render %></textarea></td>
-</tr>
-</table>
-
-<input type="hidden" name="furnace-form-submitted" value="new-paste"/>
-<input type="SUBMIT" value="Submit paste" />
-</form>
-
-<% "footer" render-template %>
+++ /dev/null
-<% USING: namespaces furnace sequences ; %>
-
-<%
- "Pastebin" "title" set
- "header" render-template
-%>
-
-<table width="100%" cellspacing="10">
- <tr>
- <td valign="top">
- <table width="100%">
- <tr align="left" class="pastebin-headings">
- <th width="50%">Summary:</th>
- <th width="100">Paste by:</th>
- <th width="200">Date:</th>
- </tr>
- <% "pastes" get <reversed> [ "paste-summary" render-component ] each %>
- </table>
- </td>
- <td valign="top" width="25%">
- <div class="infobox">
- <p>This pastebin is written in <a href="http://factorcode.org/">Factor</a>. It is inspired by <a href="http://paste.lisp.org">lisppaste</a>.
- </p>
- <p>It can be used for collaborative development over IRC. You can post code for review, and annotate other people's code. Syntax highlighting for over a hundred file types is supported.
- </p>
- <p>
- <% "webapps.pastebin" browse-webapp-source %></p>
- </div>
- </td>
- </tr>
-</table>
-
-<% "footer" render-template %>
+++ /dev/null
-<% USING: continuations namespaces io kernel math math.parser
-furnace webapps.pastebin calendar sequences ; %>
-
-<tr>
- <td>
- <a href="<% model get paste-link write %>">
- <% "summary" get write %>
- </a>
- </td>
- <td><% "author" get write %></td>
- <td><% "date" get timestamp>string write %></td>
-</tr>
+++ /dev/null
-USING: calendar furnace furnace.validator io.files kernel
-namespaces sequences http.server.responders html math.parser rss
-xml.writer xmode.code2html math calendar.format ;
-IN: webapps.pastebin
-
-TUPLE: pastebin pastes ;
-
-: <pastebin> ( -- pastebin )
- V{ } clone pastebin construct-boa ;
-
-<pastebin> pastebin set-global
-
-TUPLE: paste
-summary author channel mode contents date
-annotations n ;
-
-: <paste> ( summary author channel mode contents -- paste )
- f V{ } clone f paste construct-boa ;
-
-TUPLE: annotation summary author mode contents ;
-
-C: <annotation> annotation
-
-: get-paste ( n -- paste )
- pastebin get pastebin-pastes nth ;
-
-: show-paste ( n -- )
- serving-html
- get-paste
- [ "show-paste" render-component ] with-html-stream ;
-
-\ show-paste { { "n" v-number } } define-action
-
-: new-paste ( -- )
- serving-html
- [ "new-paste" render-template ] with-html-stream ;
-
-\ new-paste { } define-action
-
-: paste-list ( -- )
- serving-html
- [
- [ show-paste ] "show-paste-quot" set
- [ new-paste ] "new-paste-quot" set
- pastebin get "paste-list" render-component
- ] with-html-stream ;
-
-\ paste-list { } define-action
-
-: paste-link ( paste -- link )
- paste-n number>string [ show-paste ] curry quot-link ;
-
-: safe-head ( seq n -- seq' )
- over length min head ;
-
-: paste-feed ( -- entries )
- pastebin get pastebin-pastes <reversed> 20 safe-head [
- {
- paste-summary
- paste-link
- paste-date
- } get-slots timestamp>rfc3339 f swap <entry>
- ] map ;
-
-: feed.xml ( -- )
- "text/xml" serving-content
- "pastebin"
- "http://pastebin.factorcode.org"
- paste-feed <feed> feed>xml write-xml ;
-
-\ feed.xml { } define-action
-
-: add-paste ( paste pastebin -- )
- >r now over set-paste-date r>
- pastebin-pastes 2dup length swap set-paste-n push ;
-
-: submit-paste ( summary author channel mode contents -- )
- <paste> [ pastebin get add-paste ] keep
- paste-link permanent-redirect ;
-
-\ new-paste
-\ submit-paste {
- { "summary" v-required }
- { "author" v-required }
- { "channel" }
- { "mode" v-required }
- { "contents" v-required }
-} define-form
-
-\ new-paste {
- { "channel" "#concatenative" }
- { "mode" "factor" }
-} default-values
-
-: annotate-paste ( n summary author mode contents -- )
- <annotation> swap get-paste
- [ paste-annotations push ] keep
- paste-link permanent-redirect ;
-
-[ "n" show-paste ]
-\ annotate-paste {
- { "n" v-required v-number }
- { "summary" v-required }
- { "author" v-required }
- { "mode" v-required }
- { "contents" v-required }
-} define-form
-
-\ show-paste {
- { "mode" "factor" }
-} default-values
-
-: style.css ( -- )
- "text/css" serving-content
- "style.css" send-resource ;
-
-\ style.css { } define-action
-
-"pastebin" "paste-list" "extra/webapps/pastebin" web-app
+++ /dev/null
-<% USING: namespaces io furnace sequences xmode.code2html calendar ; %>
-
-<%
- "Paste: " "summary" get append "title" set
- "header" render-template
-%>
-
-<table>
-<tr><th>Paste by:</th><td><% "author" get write %></td></tr>
-<!-- <tr><th>Channel:</th><td><% "channel" get write %></td></tr> -->
-<tr><th>Created:</th><td><% "date" get timestamp>string write %></td></tr>
-<tr><th>File type:</th><td><% "mode" get write %></td></tr>
-</table>
-
-<% "syntax" render-template %>
-
-<% "annotations" get [ "annotation" render-component ] each %>
-
-<% model get "annotate-paste" render-component %>
-
-<% "footer" render-template %>
+++ /dev/null
-body {
- font:75%/1.6em "Lucida Grande", "Lucida Sans Unicode", verdana, geneva, sans-serif;
- color:#888;
-}
-
-h1.pastebin-title {
- font-size:300%;
-}
-
-a {
- color:#222;
- border-bottom:1px dotted #ccc;
- text-decoration:none;
-}
-
-a:hover {
- border-bottom:1px solid #ccc;
-}
-
-pre.code {
- border:1px dashed #ccc;
- background-color:#f5f5f5;
- padding:5px;
- font-size:150%;
- color:#000000;
-}
-
-.navbar {
- background-color:#eeeeee;
- padding:5px;
- border:1px solid #ccc;
-}
-
-.infobox {
- border: 1px solid #C1DAD7;
- padding: 10px;
-}
-
-.error {
- color: red;
-}
+++ /dev/null
-<% USING: xmode.code2html splitting namespaces ; %>
-
-<pre class="code"><% "contents" get string-lines "mode" get htmlize-lines %></pre>
+++ /dev/null
-Slava Pestov
+++ /dev/null
-USING: sequences rss arrays concurrency.combinators kernel
-sorting html.elements io assocs namespaces math threads vocabs
-html furnace http.server.templating calendar math.parser
-splitting continuations debugger system http.server.responders
-xml.writer prettyprint logging calendar.format ;
-IN: webapps.planet
-
-: print-posting-summary ( posting -- )
- <p "news" =class p>
- <b> dup entry-title write </b> <br/>
- <a entry-link =href "more" =class a>
- "Read More..." write
- </a>
- </p> ;
-
-: print-posting-summaries ( postings -- )
- [ print-posting-summary ] each ;
-
-: print-blogroll ( blogroll -- )
- <ul "description" =class ul>
- [
- <li> <a dup third =href a> first write </a> </li>
- ] each
- </ul> ;
-
-: format-date ( date -- string )
- rfc3339>timestamp timestamp>string ;
-
-: print-posting ( posting -- )
- <h2 "posting-title" =class h2>
- <a dup entry-link =href a>
- dup entry-title write-html
- </a>
- </h2>
- <p "posting-body" =class p>
- dup entry-description write-html
- </p>
- <p "posting-date" =class p>
- entry-pub-date format-date write
- </p> ;
-
-: print-postings ( postings -- )
- [ print-posting ] each ;
-
-SYMBOL: default-blogroll
-SYMBOL: cached-postings
-
-: safe-head ( seq n -- seq' )
- over length min head ;
-
-: mini-planet-factor ( -- )
- cached-postings get 4 safe-head print-posting-summaries ;
-
-: planet-factor ( -- )
- serving-html [ "planet" render-template ] with-html-stream ;
-
-\ planet-factor { } define-action
-
-: planet-feed ( -- feed )
- "[ planet-factor ]"
- "http://planet.factorcode.org"
- cached-postings get 30 safe-head <feed> ;
-
-: feed.xml ( -- )
- "text/xml" serving-content
- planet-feed feed>xml write-xml ;
-
-\ feed.xml { } define-action
-
-: style.css ( -- )
- "text/css" serving-content
- "style.css" send-resource ;
-
-\ style.css { } define-action
-
-SYMBOL: last-update
-
-: <posting> ( author entry -- entry' )
- clone
- [ ": " swap entry-title 3append ] keep
- [ set-entry-title ] keep ;
-
-: fetch-feed ( url -- feed )
- download-feed feed-entries ;
-
-\ fetch-feed DEBUG add-error-logging
-
-: fetch-blogroll ( blogroll -- entries )
- dup 0 <column> swap 1 <column>
- [ fetch-feed ] parallel-map
- [ [ <posting> ] with map ] 2map concat ;
-
-: sort-entries ( entries -- entries' )
- [ [ entry-pub-date ] compare ] sort <reversed> ;
-
-: update-cached-postings ( -- )
- default-blogroll get
- fetch-blogroll sort-entries
- cached-postings set-global ;
-
-: update-thread ( -- )
- millis last-update set-global
- [ update-cached-postings ] "RSS feed update slave" spawn drop
- 10 60 * 1000 * sleep
- update-thread ;
-
-: start-update-thread ( -- )
- [
- "webapps.planet" [
- update-thread
- ] with-logging
- ] "RSS feed update master" spawn drop ;
-
-"planet" "planet-factor" "extra/webapps/planet" web-app
-
-{
- { "Berlin Brown" "http://factorlang-fornovices.blogspot.com/feeds/posts/default" "http://factorlang-fornovices.blogspot.com" }
- { "Chris Double" "http://www.blogger.com/feeds/18561009/posts/full/-/factor" "http://www.bluishcoder.co.nz/" }
- { "Elie Chaftari" "http://fun-factor.blogspot.com/feeds/posts/default" "http://fun-factor.blogspot.com/" }
- { "Doug Coleman" "http://code-factor.blogspot.com/feeds/posts/default" "http://code-factor.blogspot.com/" }
- { "Daniel Ehrenberg" "http://useless-factor.blogspot.com/feeds/posts/default" "http://useless-factor.blogspot.com/" }
- { "Gavin Harrison" "http://gmh33.blogspot.com/feeds/posts/default" "http://gmh33.blogspot.com/" }
- { "Kio M. Smallwood"
- "http://sekenre.wordpress.com/feed/atom/"
- "http://sekenre.wordpress.com/" }
- { "Phil Dawes" "http://www.phildawes.net/blog/category/factor/feed/atom" "http://www.phildawes.net/blog/" }
- { "Samuel Tardieu" "http://www.rfc1149.net/blog/tag/factor/feed/atom/" "http://www.rfc1149.net/blog/tag/factor/" }
- { "Slava Pestov" "http://factor-language.blogspot.com/atom.xml" "http://factor-language.blogspot.com/" }
-} default-blogroll set-global
+++ /dev/null
-<% USING: namespaces html.elements webapps.planet sequences
-furnace ; %>
-
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml">
-<head>
- <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
-
- <title>planet-factor</title>
- <link rel="stylesheet" href="style.css" type="text/css" media="screen" title="no title" charset="utf-8" />
- <link rel="alternate" type="application/atom+xml" title="Planet Factor - Atom" href="feed.xml" />
-</head>
-
-<body id="index">
- <h1 class="planet-title">[ planet-factor ]</h1>
- <table width="100%" cellpadding="10">
- <tr>
- <td> <% cached-postings get 20 safe-head print-postings %> </td>
- <td valign="top" width="25%" class="infobox">
- <p>
- <b>planet-factor</b> is an Atom/RSS aggregator that collects the
- contents of <a href="http://factorcode.org/">Factor</a>-related blogs. It is inspired by
- <a href="http://planet.lisp.org">Planet Lisp</a>.
- </p>
- <p>
- <img src="http://planet.lisp.org/feed-icon-14x14.png" />
- <a href="feed.xml"> Syndicate </a>
- </p>
- <p>
- This webapp is written in <a href="http://factorcode.org/">Factor</a>.<br/>
- <% "webapps.planet" browse-webapp-source %>
- </p>
- <h2 class="blogroll-title">Blogroll</h2>
- <% default-blogroll get print-blogroll %>
- <p>
- If you want your weblog added to the blogroll, <a href="http://factorcode.org/gethelp.fhtml">just ask</a>.
- </p>
- </td>
- </tr>
- </table>
-</body>
-
-</html>
+++ /dev/null
-body {
- font:75%/1.6em "Lucida Grande", "Lucida Sans Unicode", verdana, geneva, sans-serif;
- color:#888;
-}
-
-h1.planet-title {
- font-size:300%;
-}
-
-a {
- color:#222;
- border-bottom:1px dotted #ccc;
- text-decoration:none;
-}
-
-a:hover {
- border-bottom:1px solid #ccc;
-}
-
-.posting-title {
- background-color:#f5f5f5;
-}
-
-pre, code {
- color:#000000;
- font-size:120%;
-}
-
-.infobox {
- border-left: 1px solid #C1DAD7;
-}
-
-.posting-date {
- text-align: right;
- font-size:90%;
-}
-
-a.more {
- display:block;
- padding:0 0 5px 0;
- color:#333;
- text-decoration:none;
- text-align:right;
- border:none;
-}
-USING: calendar calendar.windows kernel tools.test ;\r
-\r
-[ t ] [ windows-1601 [ timestamp>FILETIME FILETIME>timestamp ] keep = ] unit-test\r
-[ t ] [ windows-time [ windows-time>FILETIME FILETIME>windows-time ] keep = ] unit-test\r
-[ t ] [ windows-1601 400 years time+ [ timestamp>FILETIME FILETIME>timestamp ] keep = ] unit-test\r
-\r
+USING: calendar calendar.windows kernel tools.test
+windows.time ;
+IN: windows.time.tests
+
+[ t ] [ windows-1601 [ timestamp>FILETIME FILETIME>timestamp ] keep = ] unit-test
+[ t ] [ windows-time [ windows-time>FILETIME FILETIME>windows-time ] keep = ] unit-test
+[ t ] [ windows-1601 400 years time+ [ timestamp>FILETIME FILETIME>timestamp ] keep = ] unit-test
+
+++ /dev/null
-source misc/version.sh
-
-TARGET=$1
-
-if [ "$1" = "x86" ]; then
- CPU="x86.32"
- TARGET=macosx-x86-32
-else
- CPU="macosx-ppc"
- TARGET=macosx-ppc
-fi
-
-BOOT_IMAGE=boot.$CPU.image
-wget http://factorcode.org/images/$VERSION/$BOOT_IMAGE
-
-make $TARGET
-Factor.app/Contents/MacOS/factor -i=$BOOT_IMAGE -no-user-init
-
-DISK_IMAGE_DIR=Factor-$VERSION
-DISK_IMAGE=Factor-$VERSION-$TARGET.dmg
-
-rm -f $DISK_IMAGE
-rm -rf $DISK_IMAGE_DIR
-mkdir $DISK_IMAGE_DIR
-mkdir -p $DISK_IMAGE_DIR/Factor/
-cp -R Factor.app $DISK_IMAGE_DIR/Factor/Factor.app
-chmod +x cp_dir
-cp factor.image license.txt README.txt $DISK_IMAGE_DIR/Factor/
-find core extra fonts misc unmaintained -type f \
- -exec ./cp_dir {} $DISK_IMAGE_DIR/Factor/{} \;
-hdiutil create -srcfolder "$DISK_IMAGE_DIR" -fs HFS+ \
- -volname "$DISK_IMAGE_DIR" "$DISK_IMAGE"
-
-ssh linode mkdir -p w/downloads/$VERSION/
-scp $DISK_IMAGE linode:w/downloads/$VERSION/
+++ /dev/null
-source misc/version.sh
-rm -rf .git .gitignore
-cd ..
-tar cfz Factor-$VERSION.tar.gz factor/
-
-ssh linode mkdir -p w/downloads/$VERSION/
-scp Factor-$VERSION.tar.gz linode:w/downloads/$VERSION/
+++ /dev/null
-source misc/version.sh
-
-CPU=$1
-
-if [ "$CPU" = "x86" ]; then
- FLAGS="-no-sse2"
-fi
-
-make windows-nt-x86-32
-
-wget http://factorcode.org/dlls/freetype6.dll
-wget http://factorcode.org/dlls/zlib1.dll
-wget http://factorcode.org/images/$VERSION/boot.x86.32.image
-
-CMD="./factor-nt -i=boot.x86.32.image -no-user-init $FLAGS"
-echo $CMD
-$CMD
-rm -rf .git/ .gitignore
-rm -rf Factor.app/
-rm -rf vm/
-rm -f Makefile
-rm -f cp_dir
-rm -f boot.*.image
-
-FILE=Factor-$VERSION-win32-$CPU.zip
-
-cd ..
-zip -r $FILE Factor/
-
-ssh linode mkdir -p w/downloads/$VERSION/
-scp $FILE linode:w/downloads/$VERSION/
QUALIFIED: unix
IN: io.sniffer.bsd
-M: unix-io destruct-handle ( obj -- ) unix:close drop ;
+M: unix-io destruct-handle ( obj -- ) unix:close ;
C-UNION: ifreq_props "sockaddr-in" "short" "int" "caddr_t" ;
C-STRUCT: ifreq { { "char" 16 } "name" } { "ifreq_props" "props" } ;
--- /dev/null
+Chris Double
--- /dev/null
+! Copyright (C) 2006 Chris Double. All Rights Reserved.
+! See http://factorcode.org/license.txt for BSD license.
+!
+USING: kernel furnace fjsc peg namespaces
+ lazy-lists io io.files furnace.validator sequences
+ http.client http.server http.server.responders
+ webapps.file html ;
+IN: webapps.fjsc
+
+: compile ( code -- )
+ #! Compile the factor code as a string, outputting the http
+ #! response containing the javascript.
+ serving-text
+ 'expression' parse parse-result-ast fjsc-compile
+ write flush ;
+
+! The 'compile' action results in an URL that looks like
+! 'responder/fjsc/compile'. It takes one query or post
+! parameter called 'code'. It calls the 'compile' word
+! passing the parameter to it on the stack.
+\ compile {
+ { "code" v-required }
+} define-action
+
+: compile-url ( url -- )
+ #! Compile the factor code at the given url, return the javascript.
+ dup "http:" head? [ "Unable to access remote sites." throw ] when
+ "http://" "host" header-param rot 3append http-get compile "();" write flush ;
+
+\ compile-url {
+ { "url" v-required }
+} define-action
+
+: render-page* ( model body-template head-template -- )
+ [
+ [ render-component ] [ f rot render-component ] html-document
+ ] serve-html ;
+
+: repl ( -- )
+ #! The main 'repl' page.
+ f "repl" "head" render-page* ;
+
+! An action called 'repl'
+\ repl { } define-action
+
+: fjsc-web-app ( -- )
+ ! Create the web app, providing access
+ ! under '/responder/fjsc' which calls the
+ ! 'repl' action.
+ "fjsc" "repl" "extra/webapps/fjsc" web-app
+
+ ! An URL to the javascript resource files used by
+ ! the 'fjsc' responder.
+ "fjsc-resources" [
+ [
+ "extra/fjsc/resources/" resource-path doc-root set
+ file-responder
+ ] with-scope
+ ] add-simple-responder
+
+ ! An URL to the resource files used by
+ ! 'termlib'.
+ "fjsc-repl-resources" [
+ [
+ "extra/webapps/fjsc/resources/" resource-path doc-root set
+ file-responder
+ ] with-scope
+ ] add-simple-responder ;
+
+MAIN: fjsc-web-app
--- /dev/null
+<title>Factor to Javascript REPL</title>\r
+<link rel="stylesheet" type="text/css" href="/responder/fjsc-repl-resources/termlib/term_styles.css"/>\r
+<script type="text/javascript" src="/responder/fjsc-repl-resources/termlib/termlib.js"></script>\r
+<script type="text/javascript" src="/responder/fjsc-resources/jquery.js"></script>\r
+<script type="text/javascript" src="/responder/fjsc-resources/bootstrap.js"></script>\r
+<script type="text/javascript" src="/responder/fjsc-repl-resources/repl.js"></script>\r
+<script type="text/javascript" src="/responder/fjsc/compile-url?url=/responder/fjsc-resources/bootstrap.factor"></script>\r
--- /dev/null
+<table border="0">
+<tr><td valign="top">
+<div id="repl" style="position:relative;"></div>
+<p>More information on the Factor to Javascript compiler can be found at these blog posts:
+<ul>
+<li><a href="http://www.bluishcoder.co.nz/2006/12/compiling-factor-to-javascript.html">Factor to Javascript Compiler</a></li>
+<li><a href="http://www.bluishcoder.co.nz/2006/12/factor-to-javascript-compiler-updates.html">Factor to Javascript Compiler Updates</a></li>
+<li><a href="http://www.bluishcoder.co.nz/2006/12/continuations-added-to-fjsc.html">Continuations added to fjsc</a></li>
+<li><a href="http://www.bluishcoder.co.nz/2006/12/cross-domain-json-with-fjsc.html">Cross Domain JSON with fjsc</a></li>
+<li><a href="http://www.bluishcoder.co.nz/2007/02/factor-to-javascript-compiler-makeover.html">Factor to Javascript Compiler Makeover</a></li>
+</ul>
+</p>
+<p>The terminal emulation code for the Factor REPL is provided by the awesome <a href="http://www.masswerk.at/termlib/index.html">termlib</a> library by Norbert Landsteiner. Documentation for termlib is <a href="/responder/fjsc-repl-resources/termlib/">available here</a>. Please note the license of 'termlib':</p>
+<blockquote>This JavaScript-library is free for private and academic use. Please include a readable copyright statement and a backlink to <http://www.masswerk.at> in the web page. The library should always be accompanied by the "readme.txt" and the sample HTML-documents.
+
+The term "private use" includes any personal or non-commercial use, which is not related to commercial activites, but excludes intranet, extranet and/or public net applications that are related to any kind of commercial or profit oriented activity.
+
+For commercial use see <a href="http://www.masswerk.at">http://www.masswerk.at</a> for contact information.</blockquote>
+</td>
+<td valign="top">
+<p><b>Stack</b></p>
+<div id="stack">
+</div>
+<p><b>Playground</b></p>
+<div id="playground">
+</div>
+<h3>Compiled Code</h3>
+<textarea id="compiled" cols="40" rows="10">
+</textarea>
+<p>Some useful words:
+<dl>
+<dt>vocabs ( -- seq )</dt>
+<dd>Return a sequence of available vocabularies</dd>
+<dt>words ( string -- seq )</dt>
+<dd>Return a sequence of words in the given vocabulary</dd>
+<dt>all-words ( -- seq )</dt>
+<dd>Return a sequence of all words</dd>
+</dl>
+</p>
+<p>The contents of <a href="/responder/fjsc-resources/bootstrap.factor">bootstrap.factor</a> have been loaded on startup.</p>
+</td>
+</tr>
+</table>
--- /dev/null
+/* Copyright (C) 2007 Chris Double. All Rights Reserved.\r
+ See http://factorcode.org/license.txt for BSD license. */\r
+\r
+var fjsc_repl = false;\r
+\r
+function fjsc_repl_handler() {\r
+ var my_term = this;\r
+ this.newLine();\r
+ if(this.lineBuffer != '') {\r
+ factor.server_eval(\r
+ this.lineBuffer, \r
+ function(text, result) { \r
+ document.getElementById("compiled").value = result;\r
+ display_datastack(); \r
+ }, \r
+ function() { my_term.prompt(); });\r
+ }\r
+ else\r
+ my_term.prompt();\r
+}\r
+\r
+function fjsc_init_handler() {\r
+ this.write(\r
+ [\r
+ TermGlobals.center('********************************************************'),\r
+ TermGlobals.center('* *'),\r
+ TermGlobals.center('* Factor to Javascript Compiler Example *'),\r
+ TermGlobals.center('* *'),\r
+ TermGlobals.center('********************************************************')\r
+ ]);\r
+ \r
+ this.prompt();\r
+}\r
+\r
+function startup() {\r
+ var conf = {\r
+ x: 0,\r
+ y: 0,\r
+ cols: 64,\r
+ rows: 18,\r
+ termDiv: "repl",\r
+ crsrBlinkMode: true,\r
+ ps: "scratchpad ",\r
+ initHandler: fjsc_init_handler,\r
+ handler: fjsc_repl_handler\r
+ };\r
+ fjsc_repl = new Terminal(conf);\r
+ fjsc_repl.open();\r
+}\r
+\r
+function display_datastack() {\r
+ var html=[];\r
+ html.push("<table border='1'>")\r
+ for(var i = 0; i < factor.cont.data_stack.length; ++i) {\r
+ html.push("<tr><td>")\r
+ html.push(factor.cont.data_stack[i])\r
+ html.push("</td></tr>")\r
+ }\r
+ html.push("</table>")\r
+ document.getElementById('stack').innerHTML=html.join("");\r
+}\r
+\r
+jQuery(function() {\r
+ startup();\r
+ display_datastack();\r
+});\r
+\r
+factor.add_word("kernel", ".s", "primitive", function(next) { \r
+ var stack = factor.cont.data_stack;\r
+ var term = fjsc_repl;\r
+ for(var i=0; i<stack.length; ++i) {\r
+ term.type(""+stack[i]);\r
+ term.newLine();\r
+ }\r
+ factor.call_next(next);\r
+});\r
+\r
+factor.add_word("io", "print", "primitive", function(next) { \r
+ var stack = factor.cont.data_stack;\r
+ var term = fjsc_repl;\r
+ term.type(""+stack.pop());\r
+ term.newLine();\r
+ factor.call_next(next);\r
+});\r
+\r
+factor.add_word("io", "write", "primitive", function(next) { \r
+ var stack = factor.cont.data_stack;\r
+ var term = fjsc_repl;\r
+ term.type(""+stack.pop());\r
+ factor.call_next(next);\r
+});\r
+\r
+factor.add_word("io", ".", "primitive", function(next) { \r
+ var stack = factor.cont.data_stack;\r
+ var term = fjsc_repl;\r
+ term.type(""+stack.pop());\r
+ term.newLine();\r
+ factor.call_next(next);\r
+});\r
--- /dev/null
+<HTML>\r
+<HEAD>\r
+ <TITLE>mass:werk termlib faq</TITLE>\r
+\r
+<STYLE TYPE="text/css">\r
+body,p,a,td {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 12px;\r
+ color: #cccccc;\r
+}\r
+.lh13 {\r
+ line-height: 13px;\r
+}\r
+.lh15 {\r
+ line-height: 15px;\r
+}\r
+pre {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ color: #ccffaa;\r
+ font-size: 12px;\r
+ line-height: 15px;\r
+}\r
+.prop {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ color: #bbee99;\r
+ font-size: 12px;\r
+ line-height: 15px;\r
+}\r
+h1 {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 16px;\r
+ color: #cccccc;\r
+}\r
+b.quest {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 14px;\r
+ font-weight: bold;\r
+ color: #bbee99;\r
+}\r
+a,a:link,a:visited {\r
+ text-decoration: none;\r
+ color: #77dd11;\r
+}\r
+a:hover {\r
+ text-decoration: underline;\r
+ color: #77dd11;\r
+}\r
+a:active {\r
+ text-decoration: underline;\r
+ color: #dddddd;\r
+}\r
+\r
+@media print {\r
+ body { background-color: #ffffff; }\r
+ body,p,a,td {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 12px;\r
+ color: #000000;\r
+ }\r
+ .lh13 {\r
+ line-height: 13px;\r
+ }\r
+ .lh15 {\r
+ line-height: 15px;\r
+ }\r
+ pre,.prop {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 12px;\r
+ color: #000000;\r
+ line-height: 15px;\r
+ }\r
+ h1 {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 16px;\r
+ color: #000000;\r
+ }\r
+ b.quest {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 14px;\r
+ font-weight: bold;\r
+ color: #000000;\r
+ }\r
+ a,a:link,a:visited {\r
+ text-decoration: none;\r
+ color: #000000;\r
+ }\r
+ a:hover {\r
+ text-decoration: underline;\r
+ color: #000000;\r
+ }\r
+ a:active {\r
+ text-decoration: underline;\r
+ color: #000000;\r
+ }\r
+}\r
+</STYLE>\r
+</HEAD>\r
+\r
+\r
+<BODY BGCOLOR="#222222" LINK="#77dd11" TEXT="#cccccc" ALINK="#dddddd" VLINK="#77dd11"\r
+TOPMARGIN="0" BOTTOMMARGIN="0" LEFTMARGIN="0" RIGHTMARGIN="0" MARGINHEIGHT="0" MARGINWIDTH="0"><A NAME="top"></A>\r
+\r
+<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0" ALIGN="center">\r
+<TR>\r
+ <TD NOWRAP><A HREF="index.html">termlib.js home</A></TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP><A HREF="multiterm_test.html">multiple terminal test</A></TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP><A HREF="parser_sample.html">sample parser</A></TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP>faq</TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP><A HREF="readme.txt" TITLE="readme.txt (text/plain)">documentation</A></TD>\r
+</TR>\r
+</TABLE>\r
+\r
+<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0" WIDTH="700" ALIGN="center">\r
+ <TR><TD>\r
+ <H1>frequently asked questions</H1>\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ <BR>\r
+ <UL>\r
+ <LI CLASS="lh15"><A HREF="#chrome">Can I add chrome to the terminal? (e.g. a window header, a close box)</A></LI>\r
+ <LI CLASS="lh15"><A HREF="#embed">How can I embed a terminal relative to my HTML layout?</A></LI>\r
+ <LI CLASS="lh15"><A HREF="#syntax">I pasted your sample code and just got an error. - ???</A></LI>\r
+ <LI CLASS="lh15"><A HREF="#keyboard">I can't get any input, but I don't get any erros too.</A></LI>\r
+ <LI CLASS="lh15"><A HREF="#keylock">How can I temporary disable the keyboard handlers?</A></LI>\r
+ <LI CLASS="lh15"><A HREF="#linesranges">How can I set the cusor to the start / the end of the command line?</A></LI>\r
+ <LI CLASS="lh15"><A HREF="#historyunique">How can I limit the command history to unique entries only?</A></LI>\r
+ <LI CLASS="lh15"><A HREF="#rebuild">How can I change my color theme on the fly?</A></LI>\r
+ <LI CLASS="lh15"><A HREF="#connect">How can I connect to a server?</A></LI>\r
+ </UL>\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13"><A NAME="chrome"></A>\r
+ <BR>\r
+<B CLASS="quest">Can I add chrome to the terminal? (e.g. a window header, a close box)</B><BR><BR>\r
+\r
+Not by the means of the Terminal object's interface (since there are way too many things that you may possibly want to add).<BR>\r
+The Terminal object allows you to specify the background color, the frame color, the frame's width and the font class used. If you want to add more chrome, you must align this in a separate division element.<BR><BR>\r
+\r
+To calculate the dimensions of the terminal use this formula:<BR><BR>\r
+\r
+width: 2 * frameWidth + conf.cols * <width of > + 2 * 2px padding (left and right)<BR>\r
+height: 2 * frameWidth + conf.rows * conf.rowHeight + 2 * 2px padding (top and bottom).<BR><BR>\r
+\r
+Or you could get the empirical values for width and height by calling a terminal's `<SPAN CLASS="prop">getDimensions()</SPAN>' method, once the terminal is open. (see documentation in "readme.txt").<BR><BR>\r
+\r
+Finnally, you could obviously embed the terminal's division element in your custom chrome layout (see below). [This will not be compatible to Netscape 4.]<BR><BR>\r
+\r
+p.e.:<PRE>\r
+ <div id="myTerminal1" style="position:absolute; top:100px; left:100px;">\r
+ <table class="termChrome">\r
+ <tbody>\r
+ <tr>\r
+ <td class="termTitle">terminal 1</td>\r
+ </tr>\r
+ <tr>\r
+ <td class="termBody"><div id="termDiv1" style="position:relative"></div></td>\r
+ </tr>\r
+ </tbody>\r
+ </table>\r
+ </div>\r
+\r
+ // get a terminal for this\r
+\r
+ var term1 = new Terminal(\r
+ {\r
+ x: 0,\r
+ y: 0,\r
+ id: 1,\r
+ termDiv: "termDiv1",\r
+ handler: myTermHandler\r
+ }\r
+ );\r
+ term1.open();\r
+ \r
+ // and this is how to move the chrome and the embedded terminal\r
+\r
+ TermGlobals.setElementXY( "myTerminal1", 200, 80 );\r
+</PRE>\r
+To keep track of the instance for any widgets use the terminal's `id' property. (You must set this in the configuration object to a unique value for this purpose.)<BR><BR>\r
+\r
+For a demonstration see the <A HREF="chrome_sample.html">Chrome Sample Page</A>.\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13"><A NAME="embed"></A>\r
+ <BR>\r
+<B CLASS="quest">How can I embed a terminal relative to my HTML layout?</B><BR><BR>\r
+\r
+Define your devision element with attribute "position" set to "relative" and place this inside your layout. Call "new Terminal()" with config-values { x: 0, y: 0 } to leave it at its relative origin.\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13"><A NAME="syntax"></A>\r
+ <BR>\r
+<B CLASS="quest">I pasted your sample code and just got an error. - ???</B><BR><BR>\r
+\r
+The short examples are kept arbitrarily simple to show the syntax.<BR>\r
+Make sure that your divison element(s) is/are rendered by the browser before `Terminal.open()' is called.<BR><BR>\r
+\r
+Does not work:\r
+<PRE> <head>\r
+ <script>\r
+ var term = new Terminal();\r
+ term.open();\r
+ </script>\r
+ </head>\r
+</PRE>\r
+Does work:\r
+<PRE> <head>\r
+ <script>\r
+ var term;\r
+ \r
+ function termOpen() {\r
+ // to be called from outside after compile time\r
+ term = new Terminal();\r
+ term.open();\r
+ }\r
+ </script>\r
+ </head>\r
+</PRE>\r
+c.f. "readme.txt"<BR>\r
+(Opening a terminal by clicking a link implies also that the page has currently focus.)<BR><BR>\r
+With v.1.01 and higher this doesn't cause an error any more.<BR>`Terminal.prototype.open()' now returns a value for success.\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13"><A NAME="keyboard"></A>\r
+ <BR>\r
+<B CLASS="quest">I can't get any input, but I don't get any erros too.</B><BR><BR>\r
+\r
+The Terminal object's functionality relies on the browsers ability to generate and handle keyboard events.<BR>\r
+Sadly some browsers lack a full implementation of the event model. (e.g. Konquerer [khtml] and early versions of Apple Safari, which is a descendant of khtml.)\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13"><A NAME="keylock"></A>\r
+ <BR>\r
+<B CLASS="quest">How can I temporary disable the keyboard handlers?</B><BR>\r
+<SPAN CLASS="prop">(The terminal is blocking my HTML form fields, etc.)</SPAN><BR><BR>\r
+\r
+With version 1.03 there's a global property `<SPAN CLASS="prop">TermGlobals.keylock</SPAN>'. Set this to `true' to disable the keyboard handlers without altering any other state. Reset it to `false' to continue with your terminal session(s).\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13"><A NAME="linesranges"></A>\r
+ <BR>\r
+<B CLASS="quest">How can I set the cusor to the start / the end of the command line?</B><BR><BR>\r
+\r
+In case you need to implement a shortcut (like ^A of some UN*X-shells) to jump to the beginning or the end of the current input line, there are two private instance methods you could utilize:<BR><BR>\r
+`<SPAN CLASS="prop">_getLineEnd(<row>, <col>)</SPAN>' returns an array [<row>, <col>] with the position of the last character in the logical input line with ASCII value >= 32 (0x20).<BR><BR>\r
+`<SPAN CLASS="prop">_getLineStart(<row>, <col>)</SPAN>' returns an array [<row>, <col>] with the position of the first character in the logical input line with ASCII value >= 32 (0x20).<BR><BR>\r
+Both take a row and a column of a cursor position as arguments.<BR><BR>\r
+\r
+p.e.:\r
+<PRE>\r
+ // jump to the start of the input line\r
+\r
+ myCtrlHandler() {\r
+ // catch ^A and jump to start of the line\r
+ if (this.inputChar == 1) {\r
+ var firstChar = this._getLineStart(this.r, this.c);\r
+ this.cursorSet(firstChar[0], firstChar[1]);\r
+ }\r
+ }</PRE>\r
+(Keep in mind that this is not exactly a good example, since some browser actually don't issue a keyboard event for \r
+"^A". And other browsers, which do catch such codes, are not very reliable in that.)\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13"><A NAME="historyunique"></A>\r
+ <BR>\r
+<B CLASS="quest">How can I limit the command history to unique entries only?</B><BR>\r
+ <SPAN CLASS="prop">(My application effords commands to be commonly repeated.)</SPAN><BR><BR>\r
+\r
+With version 1.05 there is a new configuration and control flag `<SPAN CLASS="prop">historyUnique</SPAN>'. All you need is setting this to `true' in your terminal's configuration object.\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13"><A NAME="rebuild"></A>\r
+ <BR>\r
+<B CLASS="quest">How can I change my color theme on the fly?</B><BR><BR>\r
+\r
+With version 1.07 there is a new method `<SPAN CLASS="prop">Terminal.rebuild()</SPAN>'.<BR>\r
+This method updates the GUI to current config settings while preserving all other state.<BR><BR>\r
+p.e.:\r
+<PRE>\r
+ // change color settings on the fly\r
+ // here: set bgColor to white and font style to class "termWhite"\r
+ // method rebuild() updates the GUI without side effects\r
+ // assume var term holds a referene to a Terminal object already active\r
+\r
+ term.conf.bgColor = '#ffffff';\r
+ term.conf.fontClass = 'termWhite';\r
+ term.rebuild();</PRE>\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13"><A NAME="connect"></A>\r
+ <BR>\r
+<B CLASS="quest">How can I connect to a server?</B><BR><BR>\r
+\r
+The Terminal object only provides an interface to handle console input and output.<BR>\r
+External connections have to be handled outside the Terminal object. You could use the XMLHttpRequest-Object (and use a communication model like AJAX or JSON) or connect via a frame or iframe element to a foreign host.<BR><BR>\r
+Handling connections is considered to be out of the realm of the "termlib.js" library.<BR>\r
+The code you need is in fact quite simple:\r
+<PRE>\r
+ function connectToHost(url) {\r
+ if (window.XMLHttpRequest) {\r
+ request = new XMLHttpRequest();\r
+ }\r
+ else if (window.ActiveXObject) {\r
+ request = new ActiveXObject('Microsoft.XMLHTTP');\r
+ }\r
+ if (request) {\r
+ request.onreadystatechange = requestChangeHandler;\r
+ request.open('GET', url);\r
+ request.send('');\r
+ }\r
+ else {\r
+ // XMLHttpRequest not implemented\r
+ }\r
+ }\r
+ \r
+ function requestChangeHandler() {\r
+ if (request.readyState == 4) {\r
+ // readyState 4: complete; now test for server's response status\r
+ if (request.status == 200) {\r
+ // response in request.responseText or request.responseXML if XML-code\r
+ // if it's JS-code we could get this by eval(request.responseText)\r
+ // by this we could import whole functions to be used via the terminal\r
+ }\r
+ else {\r
+ // connection error\r
+ // status code and message in request.status and request.statusText\r
+ }\r
+ }\r
+ }\r
+</PRE>\r
+You should use this only together with a timer (window.setTimeout()) to handle connection timeouts.<BR>\r
+Additionally you would need some syntax to authenticate and tell the server what you want.<BR>\r
+For this purpose you could use the following methods of the XMLHttpRequest object:<BR><BR>\r
+\r
+ <TABLE BORDER="0" CELLSPACING="0" CELLPADDING="3">\r
+ <TR VALIGN="top"><TD NOWRAP CLASS="prop">setRequestHeader("<I>headerLabel</I>", "<I>value</I>")</TD><TD>set a HTTP header to be sent to the server</TD></TR>\r
+ <TR VALIGN="top"><TD NOWRAP CLASS="prop">getResponseHeader("<I>headerLabel</I>")</TD><TD>get a HTTP header sent from the server</TD></TR>\r
+ <TR VALIGN="top"><TD NOWRAP CLASS="prop">open(<I>method</I>, "<I>url</I>" [, <I>asyncFlag</I> [,<BR> "<I>userid</I>" [, "<I>password</I>"]]])</TD><TD>assign the destination properties to the request.<BR>be aware that userid and password are not encrypted!</TD></TR>\r
+ <TR VALIGN="top"><TD NOWRAP CLASS="prop">send(<I>content</I>)</TD><TD>transmit a message body (post-string or DOM object)</TD></TR>\r
+ <TR VALIGN="top"><TD NOWRAP CLASS="prop">abort()</TD><TD>use this to stop a pending connection</TD></TR>\r
+ </TABLE>\r
+\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ <BR>\r
+ Norbert Landsteiner - August 2005<BR>\r
+ <A HREF="http://www.masswerk.at/" TARGET="_blank">http://www.masswerk.at</A>\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ <BR>\r
+ <A HREF="#top">> top of page</A>\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ \r
+ </TD></TR>\r
+</TABLE>\r
+\r
+<DIV ID="termDiv" STYLE="position:absolute; top:20px; left:100px;"></DIV>\r
+\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+<HTML>\r
+<HEAD>\r
+ <TITLE>mass:werk termlib</TITLE>\r
+\r
+<STYLE TYPE="text/css">\r
+body,p,a,td {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 12px;\r
+ color: #cccccc;\r
+}\r
+.lh13 {\r
+ line-height: 13px;\r
+}\r
+.lh15 {\r
+ line-height: 15px;\r
+}\r
+pre {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 12px;\r
+ color: #ccffaa;\r
+ line-height: 15px;\r
+}\r
+.prop {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ color: #bbee99;\r
+ font-size: 12px;\r
+ line-height: 15px;\r
+}\r
+h1 {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 16px;\r
+ color: #cccccc;\r
+}\r
+a,a:link,a:visited {\r
+ text-decoration: none;\r
+ color: #77dd11;\r
+}\r
+a:hover {\r
+ text-decoration: underline;\r
+ color: #77dd11;\r
+}\r
+a:active {\r
+ text-decoration: underline;\r
+ color: #dddddd;\r
+}\r
+\r
+@media print {\r
+ body { background-color: #ffffff; }\r
+ body,p,a,td {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 12px;\r
+ color: #000000;\r
+ }\r
+ .lh13 {\r
+ line-height: 13px;\r
+ }\r
+ .lh15 {\r
+ line-height: 15px;\r
+ }\r
+ pre,.prop {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 12px;\r
+ color: #000000;\r
+ line-height: 15px;\r
+ }\r
+ h1 {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 16px;\r
+ color: #000000;\r
+ }\r
+ a,a:link,a:visited {\r
+ text-decoration: none;\r
+ color: #000000;\r
+ }\r
+ a:hover {\r
+ text-decoration: underline;\r
+ color: #000000;\r
+ }\r
+ a:active {\r
+ text-decoration: underline;\r
+ color: #000000;\r
+ }\r
+}\r
+</STYLE>\r
+</HEAD>\r
+\r
+\r
+<BODY BGCOLOR="#222222" LINK="#77dd11" TEXT="#cccccc" ALINK="#dddddd" VLINK="#77dd11"\r
+TOPMARGIN="0" BOTTOMMARGIN="0" LEFTMARGIN="0" RIGHTMARGIN="0" MARGINHEIGHT="0" MARGINWIDTH="0"><A NAME="top"></A>\r
+\r
+<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0" ALIGN="center">\r
+<TR>\r
+ <TD NOWRAP>termlib.js home</TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP><A HREF="multiterm_test.html">multiple terminal test</A></TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP><A HREF="parser_sample.html">sample parser</A></TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP><A HREF="faq.html">faq</A></TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP><A HREF="readme.txt" TITLE="readme.txt (text/plain)">documentation</A></TD>\r
+</TR>\r
+</TABLE>\r
+\r
+<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0" WIDTH="700" ALIGN="center">\r
+ <TR><TD>\r
+ <H1>mass:werk termlib.js</H1>\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ The JavaScript library "termlib.js" provides a `Terminal' object, which\r
+ facillitates a simple and object oriented approach to generate and control a\r
+ terminal-like interface for web services.<BR><BR>\r
+ \r
+ "termlib.js" features direct keyboard input and powerful output methods\r
+ for multiple and simultanious instances of the `Terminal' object.<BR><BR>\r
+ \r
+ The library was written with the aim of simple usage and a maximum of compatibility\r
+ with minimal foot print in the global namespace.<BR><BR><BR>\r
+ \r
+ \r
+ A short example:<BR>\r
+ <PRE>\r
+ var term = new Terminal( {handler: termHandler} );\r
+ term.open();\r
+\r
+ function termHandler() {\r
+ this.newLine();\r
+ var line = this.lineBuffer;\r
+ if (line != "") {\r
+ this.write("You typed: "+line);\r
+ }\r
+ this.prompt();\r
+ }\r
+ </PRE>\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ <B>License</B><BR><BR>\r
+\r
+ This JavaScript-library is <U>free for private and academic use</U>.\r
+ Please include a readable copyright statement and a backlink to <http://www.masswerk.at> in the\r
+ web page. The library should always be accompanied by the "readme.txt" and the sample HTML-documents.<BR><BR>\r
+\r
+ The term "private use" includes any personal or non-commercial use, which is not related\r
+ to commercial activites, but excludes intranet, extranet and/or public net applications\r
+ that are related to any kind of commercial or profit oriented activity.<BR><BR>\r
+\r
+ For commercial use see <<A HREF="http://www.masswerk.at/" TARGET="_blank">http://www.masswerk.at</A>> for contact information.\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ <B>Distribution</B><BR><BR>\r
+\r
+ This JavaScript-library may be distributed freely as long it is distributed together with the "readme.txt" and the sample HTML-documents and this document.<BR><BR>\r
+\r
+ Any changes to the library should be commented and be documented in the readme-file.<BR>\r
+ Any changes must be reflected in the `Terminal.version' string as "Version.Subversion (compatibility)".\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ <B>Disclaimer</B><BR><BR>\r
+\r
+ This software is distributed AS IS and in the hope that it will be useful, but WITHOUT ANY\r
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
+ PURPOSE. The entire risk as to the quality and performance of the product is borne by the\r
+ user. No use of the product is authorized hereunder except under this disclaimer.\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ <B>History</B><BR><BR>\r
+\r
+ This library evolved from the terminal script "TermApp" ((c) N. Landsteiner 2003) and is in its\r
+ current form a down scaled spinn-off of the "JS/UIX" project. (JS/UIX is not a free software by now.)\r
+ c.f.: <<A HREF="http://www.masswerk.at/jsuix/" TARGET="_blank">http://www.masswerk.at/jsuix</A>><BR><BR>\r
+\r
+ For version history: see the <A HREF="readme.txt">readme.txt</A>.\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ <BR>\r
+ <B>Download</B><BR><BR>\r
+ Be sure to have read the license information and the disclamer and that you are willing to respect copyrights.<BR><BR>\r
+\r
+ <SPAN CLASS="prop">Download:</SPAN> <A HREF="termlib.zip">termlib.zip</A> (~ 40 KB, incl. docs)<BR><BR>\r
+ Current version is "1.07 (original)".<BR>\r
+ The files are now provided with line breaks in format <CRLF>.<BR>\r
+ \r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ <B>Author</B><BR><BR>\r
+ © Norbert Landsteiner 2003-2005<BR>\r
+ mass:werk – media environments<BR>\r
+ <A HREF="http://www.masswerk.at/" TARGET="_blank">http://www.masswerk.at</A>\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ <BR>\r
+ Author's note:<BR>\r
+ Please do not contact me on questions of simple usage. There is an extensive documentation (readme.txt) including plenty of sample code that should provide all information you need.\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ <BR>\r
+ <A HREF="#top">> top of page</A>\r
+ </TD></TR>\r
+ <TR><TD CLASS="lh13">\r
+ \r
+ </TD></TR>\r
+</TABLE>\r
+\r
+<DIV ID="termDiv" STYLE="position:absolute; top:20px; left:100px;"></DIV>\r
+\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+<HTML>\r
+<HEAD>\r
+ <TITLE>termlib Multiple Terminal Test</TITLE>\r
+ <SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript" SRC="termlib.js"></SCRIPT>\r
+\r
+<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">\r
+<!--\r
+\r
+/*\r
+ multiple terminal test for termlib.js\r
+\r
+ (c) Norbert Landsteiner 2003-2005\r
+ mass:werk - media environments\r
+ <http://www.masswerk.at>\r
+\r
+*/\r
+\r
+var term=new Array();\r
+\r
+var helpPage=[\r
+ '%CS%+r Terminal Help %-r%n',\r
+ ' This is just a tiny test for multiple terminals.',\r
+ ' use one of the following commands:',\r
+ ' clear .... clear the terminal',\r
+ ' exit ..... close the terminal (or <ESC>)',\r
+ ' id ....... show terminal\'s id',\r
+ ' switch ... switch to other terminal',\r
+ ' help ..... show this help page',\r
+ ' other input will be echoed to the terminal.',\r
+ ' '\r
+];\r
+\r
+function termOpen(n) {\r
+ if (!term[n]) {\r
+ var y=(n==1)? 70: 280;\r
+ term[n]=new Terminal(\r
+ {\r
+ x: 220,\r
+ y: y,\r
+ rows: 12,\r
+ greeting: '%+r +++ Terminal #'+n+' ready. +++ %-r%nType "help" for help.%n',\r
+ id: n,\r
+ termDiv: 'termDiv'+n,\r
+ crsrBlinkMode: true,\r
+ handler: termHandler,\r
+ exitHandler: termExitHandler\r
+ }\r
+ );\r
+ if (term[n]) term[n].open();\r
+ }\r
+ else if (term[n].closed) {\r
+ term[n].open();\r
+ }\r
+ else {\r
+ term[n].focus();\r
+ }\r
+}\r
+\r
+function termHandler() {\r
+ // called on <CR> or <ENTER>\r
+ this.newLine();\r
+ var cmd=this.lineBuffer;\r
+ if (cmd!='') {\r
+ if (cmd=='switch') {\r
+ var other=(this.id==1)? 2:1;\r
+ termOpen(other);\r
+ }\r
+ else if (cmd=='clear') {\r
+ this.clear();\r
+ }\r
+ else if (cmd=='exit') {\r
+ this.close();\r
+ }\r
+ else if (cmd=='help') {\r
+ this.write(helpPage);\r
+ }\r
+ else if (cmd=='id') {\r
+ this.write('terminal id: '+this.id);\r
+ }\r
+ else {\r
+ this.type('You typed: '+cmd);\r
+ this.newLine();\r
+ }\r
+ }\r
+ this.prompt();\r
+}\r
+\r
+function termExitHandler() {\r
+ // optional handler called on exit\r
+ // activate other terminal if open\r
+ var other=(this.id==1)? 2:1;\r
+ if ((term[other]) && (term[other].closed==false)) term[other].focus();\r
+}\r
+\r
+//-->\r
+</SCRIPT>\r
+\r
+<STYLE TYPE="text/css">\r
+body,p,a,td {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 12px;\r
+ color: #cccccc;\r
+}\r
+.lh15 {\r
+ line-height: 15px;\r
+}\r
+.term {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 12px;\r
+ color: #33d011;\r
+ background: none;\r
+}\r
+.termReverse {\r
+ color: #111111;\r
+ background: #33d011;\r
+}\r
+a,a:link,a:visited {\r
+ text-decoration: none;\r
+ color: #77dd11;\r
+}\r
+a:hover {\r
+ text-decoration: underline;\r
+ color: #77dd11;\r
+}\r
+a:active {\r
+ text-decoration: underline;\r
+ color: #dddddd;\r
+}\r
+\r
+a.termopen,a.termopen:link,a.termopen:visited {\r
+ text-decoration: none;\r
+ color: #77dd11;\r
+ background: none;\r
+}\r
+a.termopen:hover {\r
+ text-decoration: none;\r
+ color: #222222;\r
+ background: #77dd11;\r
+}\r
+a.termopen:active {\r
+ text-decoration: none;\r
+ color: #222222;\r
+ background: #dddddd;\r
+}\r
+\r
+</STYLE>\r
+</HEAD>\r
+\r
+\r
+<BODY BGCOLOR="#222222" LINK="#77dd11" TEXT="#cccccc" ALINK="#dddddd" VLINK="#77dd11"\r
+TOPMARGIN="0" BOTTOMMARGIN="0" LEFTMARGIN="0" RIGHTMARGIN="0" MARGINHEIGHT="0" MARGINWIDTH="0">\r
+\r
+<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0" ALIGN="center">\r
+<TR>\r
+ <TD NOWRAP><A HREF="index.html">termlib.js home</A></TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP>multiple terminal test</TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP><A HREF="parser_sample.html">sample parser</A></TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP><A HREF="faq.html">faq</A></TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP><A HREF="readme.txt" TITLE="readme.txt (text/plain)">documentation</A></TD>\r
+</TR>\r
+</TABLE>\r
+\r
+<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0">\r
+ <TR><TD NOWRAP>\r
+ Multiple Terminal Test<BR> \r
+ </TD></TR>\r
+ <TR><TD NOWRAP>\r
+ <A HREF="javascript:termOpen(1)" onfocus="if(this.blur)this.blur();" onmouseover="window.status='terminal 1'; return true" onmouseout="window.status=''; return true" CLASS="termopen">> open terminal 1 </A>\r
+ </TD></TR>\r
+ <TR><TD NOWRAP>\r
+ <A HREF="javascript:termOpen(2)" onfocus="if(this.blur)this.blur();" onmouseover="window.status='terminal 2'; return true" onmouseout="window.status=''; return true" CLASS="termopen">> open terminal 2 </A>\r
+ </TD></TR>\r
+ <TR><TD NOWRAP CLASS="lh15">\r
+ <BR>\r
+ (c) mass:werk,<BR>N. Landsteiner 2003-2005<BR>\r
+ <A HREF="http://www.masswerk.at/" TARGET="_blank">http://www.masswerk.at</A>\r
+ </TD></TR>\r
+</TABLE>\r
+\r
+<DIV ID="termDiv1" STYLE="position:absolute; top:20px; left:100px;"></DIV>\r
+<DIV ID="termDiv2" STYLE="position:absolute; top:20px; left:100px;"></DIV>\r
+\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+<HTML>\r
+<HEAD>\r
+ <TITLE>termlib Sample Parser</TITLE>\r
+ <SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript" SRC="termlib.js"></SCRIPT>\r
+ <SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript" SRC="termlib_parser.js"></SCRIPT>\r
+\r
+<SCRIPT LANGUAGE="JavaScript" TYPE="text/javascript">\r
+<!--\r
+\r
+/*\r
+ test sample for termlib.js and termlib_parser.js\r
+\r
+ (c) Norbert Landsteiner 2005\r
+ mass:werk - media environments\r
+ <http://www.masswerk.at>\r
+\r
+*/\r
+\r
+var term;\r
+\r
+var helpPage=[\r
+ '%CS%+r Terminal Help %-r%n',\r
+ ' This is just a sample to demonstrate command line parsing.',\r
+ ' ',\r
+ ' Use one of the following commands:',\r
+ ' clear [-a] .......... clear the terminal',\r
+ ' option "a" also removes the status line',\r
+ ' number -n<value> .... return value of option "n" (test for options)',\r
+ ' repeat -n<value> .... repeats the first argument n times (another test)',\r
+ ' login <username> .... sample login (test for raw mode)',\r
+ ' exit ................ close the terminal (same as <ESC>)',\r
+ ' help ................ show this help page',\r
+ ' ',\r
+ ' other input will be echoed to the terminal as a list of parsed arguments',\r
+ ' in the format <argument index> <quoting level> "<parsed value>".',\r
+ ' '\r
+];\r
+\r
+function termOpen() {\r
+ if (!term) {\r
+ term=new Terminal(\r
+ {\r
+ x: 220,\r
+ y: 70,\r
+ termDiv: 'termDiv',\r
+ ps: '[guest]$',\r
+ initHandler: termInitHandler,\r
+ handler: commandHandler\r
+ }\r
+ );\r
+ if (term) term.open();\r
+ }\r
+ else if (term.closed) {\r
+ term.open();\r
+ }\r
+ else {\r
+ term.focus();\r
+ }\r
+}\r
+\r
+function termInitHandler() {\r
+ // output a start up screen\r
+ this.write(\r
+ [\r
+ TermGlobals.center('####################################################', 80),\r
+ TermGlobals.center('# #', 80),\r
+ TermGlobals.center('# termlib.js - Sample Parser #', 80),\r
+ TermGlobals.center('# Input is echoed as a list of parsed arguments. #', 80),\r
+ TermGlobals.center('# #', 80),\r
+ TermGlobals.center('# Type "help" for commands. #', 80),\r
+ TermGlobals.center('# #', 80),\r
+ TermGlobals.center('# (c) N. Landsteiner 2005; www.masswerk.at #', 80),\r
+ TermGlobals.center('# #', 80),\r
+ TermGlobals.center('####################################################', 80),\r
+ '%n'\r
+ ]\r
+ );\r
+ // set a double status line\r
+ this.statusLine('', 8,2); // just a line of strike\r
+ this.statusLine(' +++ This is just a test sample for command parsing. Type "help" for help. +++');\r
+ this.maxLines -= 2;\r
+ // and leave with prompt\r
+ this.prompt();\r
+}\r
+\r
+function commandHandler() {\r
+ this.newLine();\r
+ // check for raw mode first (should not be parsed)\r
+ if (this.rawMode) {\r
+ if (this.env.getPassword) {\r
+ // sample password handler (lineBuffer == stored username ?)\r
+ if (this.lineBuffer == this.env.username) {\r
+ this.user = this.env.username;\r
+ this.ps = '['+this.user+']>';\r
+ }\r
+ else {\r
+ this.type('Sorry.');\r
+ }\r
+ this.env.username = '';\r
+ this.env.getPassword = false;\r
+ }\r
+ // leave in normal mode\r
+ this.rawMode = false;\r
+ this.prompt();\r
+ return;\r
+ }\r
+ // normal command parsing\r
+ // just call the termlib_parser with a reference of the calling Terminal instance\r
+ // parsed arguments will be imported in this.argv,\r
+ // quoting levels per argument in this.argQL (quoting character or empty)\r
+ // cursor for arguments is this.argc (used by parserGetopt)\r
+ // => see 'termlib_parse.js' for configuration and details\r
+ parseLine(this);\r
+ if (this.argv.length == 0) {\r
+ // no commmand line input\r
+ }\r
+ else if (this.argQL[0]) {\r
+ // first argument quoted -> error\r
+ this.write("Syntax error: first argument quoted.");\r
+ }\r
+ else {\r
+ var cmd = this.argv[this.argc++];\r
+ /*\r
+ process commands now\r
+ 1st argument: this.argv[this.argc]\r
+ */\r
+ if (cmd == 'help') {\r
+ this.write(helpPage);\r
+ }\r
+ else if (cmd == 'clear') {\r
+ // get options\r
+ var opts = parserGetopt(this, 'aA');\r
+ if (opts.a) {\r
+ // discard status line on opt "a" or "A"\r
+ this.maxLines = this.conf.rows;\r
+ }\r
+ this.clear();\r
+ }\r
+ else if (cmd == 'number') {\r
+ // test for value options\r
+ var opts = parserGetopt(this, 'n');\r
+ if (opts.illegals.length) this.type('illegal option. usage: number -n<value>')\r
+ else if ((opts.n) && (opts.n.value != -1)) this.type('option value: '+opts.n.value)\r
+ else this.type('usage: number -n<value>');\r
+ }\r
+ else if (cmd == 'repeat') {\r
+ // another test for value options\r
+ var opts = parserGetopt(this, 'n');\r
+ if (opts.illegals.length) this.type('illegal option. usage: repeat -n<value> <string>')\r
+ else if ((opts.n) && (opts.n.value != -1)) {\r
+ // first normal argument is again this.argv[this.argc]\r
+ var s = this.argv[this.argc];\r
+ if (typeof s != 'undefined') {\r
+ // repeat this string n times\r
+ var a = [];\r
+ for (var i=0; i<opts.n.value; i++) a[a.length] = s;\r
+ this.type(a.join(' '));\r
+ }\r
+ }\r
+ else this.type('usage: repeat -n<value> <string>');\r
+ }\r
+ else if (cmd == 'login') {\r
+ // sample login (test for raw mode)\r
+ if ((this.argc == this.argv.length) || (this.argv[this.argc] == '')) {\r
+ this.type('usage: login <username>');\r
+ }\r
+ else {\r
+ this.env.getPassword = true;\r
+ this.env.username = this.argv[this.argc];\r
+ this.write('%+iSample login: repeat username as password.%-i%n');\r
+ this.type('password: ');\r
+ // exit in raw mode (blind input)\r
+ this.rawMode = true;\r
+ this.lock = false;\r
+ return;\r
+ }\r
+ }\r
+ else if (cmd == 'exit') {\r
+ this.close();\r
+ return;\r
+ }\r
+ else {\r
+ // for test purpose just output argv as list\r
+ // assemble a string of style-escaped lines and output it in more-mode\r
+ s=' INDEX QL ARGUMENT%n';\r
+ for (var i=0; i<this.argv.length; i++) {\r
+ s += TermGlobals.stringReplace('%', '%%',\r
+ TermGlobals.fillLeft(i, 6) +\r
+ TermGlobals.fillLeft((this.argQL[i])? this.argQL[i]:'-', 4) +\r
+ ' "' + this.argv[i] + '"'\r
+ ) + '%n';\r
+ }\r
+ this.write(s, 1);\r
+ return;\r
+ }\r
+ }\r
+ this.prompt();\r
+}\r
+\r
+\r
+//-->\r
+</SCRIPT>\r
+\r
+<STYLE TYPE="text/css">\r
+body,p,a,td {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 12px;\r
+ color: #cccccc;\r
+}\r
+.lh15 {\r
+ line-height: 15px;\r
+}\r
+.term {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 12px;\r
+ color: #33d011;\r
+ background: none;\r
+}\r
+.termReverse {\r
+ color: #111111;\r
+ background: #33d011;\r
+}\r
+a,a:link,a:visited {\r
+ text-decoration: none;\r
+ color: #77dd11;\r
+}\r
+a:hover {\r
+ text-decoration: underline;\r
+ color: #77dd11;\r
+}\r
+a:active {\r
+ text-decoration: underline;\r
+ color: #dddddd;\r
+}\r
+\r
+a.termopen,a.termopen:link,a.termopen:visited {\r
+ text-decoration: none;\r
+ color: #77dd11;\r
+ background: none;\r
+}\r
+a.termopen:hover {\r
+ text-decoration: none;\r
+ color: #222222;\r
+ background: #77dd11;\r
+}\r
+a.termopen:active {\r
+ text-decoration: none;\r
+ color: #222222;\r
+ background: #dddddd;\r
+}\r
+\r
+</STYLE>\r
+</HEAD>\r
+\r
+\r
+<BODY BGCOLOR="#222222" LINK="#77dd11" TEXT="#cccccc" ALINK="#dddddd" VLINK="#77dd11"\r
+TOPMARGIN="0" BOTTOMMARGIN="0" LEFTMARGIN="0" RIGHTMARGIN="0" MARGINHEIGHT="0" MARGINWIDTH="0">\r
+\r
+<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0" ALIGN="center">\r
+<TR>\r
+ <TD NOWRAP><A HREF="index.html">termlib.js home</A></TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP><A HREF="multiterm_test.html">multiple terminal test</A></TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP>sample parser</TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP><A HREF="faq.html">faq</A></TD>\r
+ <TD>|</TD>\r
+ <TD NOWRAP><A HREF="readme.txt" TITLE="readme.txt (text/plain)">documentation</A></TD>\r
+</TR>\r
+</TABLE>\r
+\r
+<TABLE BORDER="0" CELLSPACING="20" CELLPADDING="0">\r
+ <TR><TD NOWRAP>\r
+ Sample Parser Test<BR> \r
+ </TD></TR>\r
+ <TR><TD NOWRAP>\r
+ <A HREF="javascript:termOpen()" onfocus="if(this.blur)this.blur();" onmouseover="window.status='terminal 1'; return true" onmouseout="window.status=''; return true" CLASS="termopen">> open terminal </A>\r
+ </TD></TR>\r
+ <TR><TD NOWRAP>\r
+ \r
+ </TD></TR>\r
+ <TR><TD NOWRAP CLASS="lh15">\r
+ <BR>\r
+ (c) mass:werk,<BR>N. Landsteiner 2003-2005<BR>\r
+ <A HREF="http://www.masswerk.at/" TARGET="_blank">http://www.masswerk.at</A>\r
+ </TD></TR>\r
+</TABLE>\r
+\r
+<DIV ID="termDiv" STYLE="position:absolute;"></DIV>\r
+\r
+</BODY>\r
+</HTML>
\ No newline at end of file
--- /dev/null
+**** mass:werk termlib.js - JS-WebTerminal Object v1.07 ****\r
+\r
+ (c) Norbert Landsteiner 2003-2005\r
+ mass:werk - media environments\r
+ <http://www.masswerk.at>\r
+\r
+\r
+\r
+\r
+Contents:\r
+\r
+ 1 About\r
+ 2 Creating a new Terminal Instance\r
+ 2.1 Configuration Values\r
+ 3 Using the Terminal\r
+ 3.1 The Default Handler\r
+ 3.2 Input Modes\r
+ 3.2.1 Normal Line Input (Command Line Mode)\r
+ 3.2.1.2 Special Keys (ctrlHandler)\r
+ 3.2.2 Raw Mode\r
+ 3.2.3 Character Mode\r
+ 3.3 Other Handlers\r
+ 3.3.1 initHandler\r
+ 3.3.2 exitHandler\r
+ 3.4 Flags for Behaviour Control\r
+ 4 Output Methods\r
+ 4.1 Terminal.type()\r
+ 4.2 Terminal.write()\r
+ 4.3 Terminal.typeAt()\r
+ 4.4 Terminal.setChar()\r
+ 4.5 Terminal.newLine()\r
+ 4.6 Terminal.clear()\r
+ 4.7 Terminal.statusLine()\r
+ 4.8 Terminal.printRowFromString()\r
+ 4.9 Terminal.redraw()\r
+ 5 Cursor Methods and Editing\r
+ 5.1 Terminal.cursorOn()\r
+ 5.2 Terminal.cursorOff()\r
+ 5.3 Terminal.cursorSet()\r
+ 5.4 Terminal.cursorLeft()\r
+ 5.5 Terminal.cursorRight()\r
+ 5.6 Terminal.backspace()\r
+ 5.7 Terminal.fwdDelete()\r
+ 5.8 Terminal.isPrintable()\r
+ 6 Other Methods of the Terminal Object\r
+ 6.1 Terminal.prompt()\r
+ 6.2 Terminal.reset()\r
+ 6.3 Terminal.open()\r
+ 6.4 Terminal.close()\r
+ 6.5 Terminal.focus()\r
+ 6.6 Terminal.moveTo()\r
+ 6.7 Terminal.resizeTo()\r
+ 6.8 Terminal.getDimensions()\r
+ 6.9 Terminal.rebuild()\r
+ 7 Global Static Methods (TermGlobals)\r
+ 7.1 TermGlobals.setFocus()\r
+ 7.2 TermGlobals.keylock (Global Locking Flag)\r
+ 7.3 TermGlobalsText Methods\r
+ 7.3.1 TermGlobals.normalize()\r
+ 7.3.2 TermGlobals.fillLeft()\r
+ 7.3.3 TermGlobals.center()\r
+ 7.3.4 TermGlobals.stringReplace()\r
+ 8 Localization\r
+ 9 Cross Browser Functions\r
+ 10 Architecture, Internals\r
+ 10.1 Global Entities\r
+ 10.2 I/O Architecture\r
+ 10.3 Compatibility\r
+ 11 History\r
+ 12 Example for a Command Line Parser\r
+ 13 License\r
+ 14 Disclaimer\r
+ 15 References\r
+\r
+\r
+\r
+\r
+1 About\r
+\r
+The Terminal library "termlib.js" provides an object oriented constructor and control\r
+methods for a terminal-like DHTML interface.\r
+\r
+"termlib.js" features direct keyboard input and powerful output methods for multiple\r
+instances of the `Terminal' object (including focus control).\r
+\r
+The library was written with the aim of simple usage and a maximum of compatibility with\r
+minimal foot print in the global namespace.\r
+\r
+\r
+A simple example:\r
+\r
+ // creating a terminal and using it\r
+\r
+ var term = new Terminal( {handler: termHandler} );\r
+ term.open();\r
+\r
+ function termHandler() {\r
+ var line = this.lineBuffer;\r
+ this.newLine();\r
+ if (line == "help") {\r
+ this.write(helpPage)\r
+ }\r
+ else if (line == "exit") {\r
+ this.close();\r
+ return;\r
+ }\r
+ else if (line != "") {\r
+ this.write("You typed: "+line);\r
+ }\r
+ this.prompt();\r
+ }\r
+\r
+ var helpPage = [\r
+ "This is the monstrous help page for my groovy terminal.",\r
+ "Commands available:",\r
+ " help ... print this monstrous help page",\r
+ " exit ... leave this groovy terminal",\r
+ " ",\r
+ "Have fun!"\r
+ ];\r
+\r
+\r
+You should provide CSS font definitions for the classes ".term" (normal video) and\r
+".termReverse" (reverse video) in a monospaced font.\r
+A sample stylesheet "term_styles.css" comes with this library.\r
+\r
+See the sample application "multiterm_test.html" for a demo of multiple terminals.\r
+\r
+v.1.01: If you configure to use another font class (see 2.1 Configuration Values),\r
+ you must provide a subclass ".termReverse" for reversed video.\r
+\r
+ p.e.: .myFontClass .termReverse {\r
+ /* your definitions for reverse video here */\r
+ }\r
+ \r
+ With the addition of `conf.fontClass' you can now create multiple\r
+ instances with independend appearences.\r
+\r
+\r
+\r
+\r
+2 Creating a new Terminal Instance\r
+\r
+Use the `new' constructor to create a new instance of the Terminal object. You will want\r
+to supply a configuration object as an argument to the constructor. If the `new'\r
+constructor is called without an object as its first argument, default values are used.\r
+\r
+p.e.:\r
+\r
+ // creating a new instance of Terminal\r
+\r
+ var conf= {\r
+ x: 100,\r
+ y: 100,\r
+ cols: 80,\r
+ rows: 24\r
+ }\r
+\r
+ var term = new Term(conf);\r
+ term.open();\r
+\r
+`Terminal.open()' initializes the terminal and makes it visible to the user.\r
+This is handled in by separate method to allow the re-initilization of instances\r
+previously closed.\r
+\r
+NOTE:\r
+The division element (or NS-layer) that holds the terminal must be present when calling\r
+`Terminal.open()'. So you must not call this method from the header of a HTML-document at\r
+compile time.\r
+\r
+\r
+\r
+2.1 Configuration Values\r
+\r
+Set any of these values in your configuration object to override:\r
+\r
+ \r
+ LABEL DEFAULT VALUE COMMENT\r
+ \r
+ x 100 terminal's position x in px\r
+ y 100 terminal's position y in px\r
+ divDiv 'termDiv' id of terminals CSS division\r
+ bgColor '#181818' background color (HTML hex value)\r
+ frameColor '#555555' frame color (HTML hex value)\r
+ frameWidth 1 frame border width in px\r
+ fontClass 'term' class name of CSS font definition to use\r
+ cols 80 number of cols per row\r
+ rows 24 number of rows\r
+ rowHeight 15 a row's line-height in px\r
+ blinkDelay 500 delay for cursor blinking in milliseconds\r
+ crsrBlinkMode false true for blinking cursor\r
+ crsrBlockMode true true for block-cursor else underscore\r
+ DELisBS false handle <DEL> as <BACKSPACE>\r
+ printTab true handle <TAB> as printable (prints as space)\r
+ printEuro true handle unicode 0x20AC (Euro sign) as printable\r
+ catchCtrlH true handle ^H as <BACKSPACE>\r
+ closeOnESC true close terminal on <ESC>\r
+ historyUnique false prevent consecutive and identical entries in history\r
+ id 0 terminal id\r
+ ps '>' prompt string\r
+ greeting '%+r Terminal ready. %-r' string for greeting if no initHandler is used\r
+ handler termDefaultHandler reference to handler for command interpretation\r
+ ctrlHandler null reference to handler called on uncatched special keys\r
+ initHandler null reference to handler called at end of init()\r
+ exitHandler null reference to handler called on close()\r
+\r
+\r
+At least you will want to specify `handler' to implement your own command parser.\r
+\r
+Note: While `id' is not used by the Termninal object, it provides an easy way to identify\r
+multiple terminals by the use of "this.id". (e.g.: "if (this.id == 1) startupterm = true;")\r
+\r
+p.e.:\r
+\r
+ // creating two individual Terminal instances\r
+\r
+ var term1 = new Terminal(\r
+ {\r
+ id: 1,\r
+ x: 200,\r
+ y: 10,\r
+ cols: 80,\r
+ rows: 12,\r
+ greeting: "*** This is Terminal 1 ***",\r
+ handler: myTerminalHandler\r
+ }\r
+ );\r
+ term1.open();\r
+\r
+ var term2 = new Terminal(\r
+ {\r
+ id: 2,\r
+ x, 200,\r
+ y: 220,\r
+ cols: 80\r
+ rows: 12,\r
+ greeting: "*** This is Terminal 2 ***",\r
+ handler: myTerminalHandler\r
+ }\r
+ );\r
+ term2.open();\r
+\r
+\r
+\r
+\r
+3 Using the Terminal\r
+\r
+There are 4 different handlers that are called by a Terminal instance to process input and\r
+some flags to control the input mode and behaviour.\r
+\r
+\r
+\r
+3.1 The Default Handler (a simlple example for input handling)\r
+\r
+If no handlers are defined in the configuration object, a default handler is called to\r
+handle a line of user input. The default command line handler `termDefaultHandler' just\r
+closes the command line with a new line and echos the input back to the user:\r
+\r
+ function termDefaultHandler() {\r
+ this.newLine();\r
+ if (this.lineBuffer != '') {\r
+ this.type('You typed: '+this.lineBuffer);\r
+ this.newLine();\r
+ }\r
+ this.prompt();\r
+ }\r
+\r
+First you may note that the instance is refered to as `this'. So you need not worry about\r
+which Terminal instance is calling your handler. As the handler is entered, the terminal\r
+is locked for user input and the cursor is off. The current input is available as a string\r
+value in `this.lineBuffer'.\r
+\r
+The method `type(<text>)' just does what it says and types a string at the current cursor\r
+position to the terminal screen.\r
+\r
+`newLine()' moves the cursor to a new line.\r
+\r
+The method `prompt()' adds a new line if the cursor isn't at the start of a line, outputs\r
+the prompt string (as specified in the configuration), activates the cursor, and unlocks\r
+the terminal for further input. While you're doing normal command line processing, always\r
+call `prompt()' when leaving your handler.\r
+\r
+In fact this is all you need to create your own terminal application. Please see at least\r
+the method `write()' for a more powerful output method.\r
+\r
+Below we will refer to all methods of the Terminal object as `Terminal.<method>()'.\r
+You can call them as `this.<method>()' in a handler or as methods of your named instance\r
+in other context (e.g.: "myTerminal.close()").\r
+\r
+[In technical terms these methods are methods of the Terminal's prototype object, while\r
+the properties are properties of a Termninal instance. Since this doesn't make any\r
+difference to your script, we'll refer to both as `Terminal.<method-or-property>'.]\r
+\r
+\r
+\r
+3.2 Input Modes\r
+\r
+3.2.1 Normal Line Input (Command Line Mode)\r
+\r
+By default the terminal is in normal input mode. Any printable characters in the range of\r
+ASCII 0x20 - 0xff are echoed to the terminal and may be edited with the use of the cursor\r
+keys and the <BACKSPACE> key.\r
+The cursor keys UP and DOWN let the user browse in the command line history (the list of\r
+all commands issued previously in this Terminal instance).\r
+\r
+If the user presses <CR> or <ENTER>, the line is read from the terminal buffer, converted\r
+to a string, and placed in `Terminal.lineBuffer' (-> `this.lineBuffer') for further use.\r
+The terminal is then locked for further input and the specified handler\r
+(`Terminal.handler') is called.\r
+\r
+\r
+3.2.1.2 Special Keys (ctrlHandler)\r
+\r
+If a special character (ASCII<0x20) or an according combination of <CTRL> and a key is\r
+pressed, which is not caught for editing or "enter", and a handler for `ctrlHandler' is\r
+specified, this handler is called.\r
+The ASCII value of the special character is available in `Terminal.inputChar'. Please note\r
+that the terminal is neither locked, nor is the cursor off - all further actions have to\r
+be controlled by `ctrlHandler'. (The tracking of <CTRL>-<key> combinations as "^C" usually\r
+works but cannot be taken for granted.)\r
+\r
+A named reference of the special control values in POSIX form (as well as the values of\r
+the cursor keys [LEFT, RIGHT, UP, DOWN]) is available in the `termKey' object.\r
+\r
+p.e.:\r
+\r
+ // a simple ctrlHandler\r
+\r
+ function myCtrlHandler() {\r
+ if (this.inputChar == termKey.ETX) {\r
+ // exit on ^C (^C == ASCII 0x03 == <ETX>)\r
+ this.close();\r
+ }\r
+ }\r
+\r
+If no `ctrlHandler' is specified, control keys are ignored (default).\r
+\r
+\r
+3.2.2 Raw Mode\r
+\r
+If the flag `Terminal.rawMode' is set to a value evaluating to `true', no special keys are\r
+tracked but <CR> and <ENTER> (and <ESC>, if the flag `Terminal.closeOnESC' is set).\r
+The input is NOT echoed to the terminal. All printable key values [0x20-0xff] are\r
+transformed to characters and added to `Terminal.lineBuffer' sequentially. The command\r
+line input is NOT added to the history.\r
+\r
+This mode is especially suitable for password input.\r
+\r
+p.e.:\r
+\r
+ // using raw mode for password input\r
+\r
+ function myTermHandler() {\r
+ this.newLine();\r
+ // we stored a flag in Terminal.env to track the status\r
+ if (this.env.getpassword) {\r
+ // leave raw mode\r
+ this.rawMode = false;\r
+ if (passwords[this.env.user] == this.lineBuffer) {\r
+ // matched\r
+ this.type('Welcome '+this.env.user);\r
+ this.env.loggedin = true;\r
+ }\r
+ else {\r
+ this.type('Sorry.');\r
+ }\r
+ this.env.getpassword = false;\r
+ }\r
+ else {\r
+ // simple parsing\r
+ var args = this.lineBuffer.split(' ');\r
+ var cmd = args[0];\r
+ if (cmd == 'login') {\r
+ var user = args[1];\r
+ if (!user) {\r
+ this.type('usage: login <username>');\r
+ }\r
+ else {\r
+ this.env.user = user;\r
+ this.env.getpassword = true;\r
+ this.type('password? ');\r
+ // enter raw mode\r
+ this.rawMode = true;\r
+ // leave without prompt so we must unlock first\r
+ this.lock = false;\r
+ return;\r
+ }\r
+ }\r
+ /*\r
+ other actions ...\r
+ */\r
+ }\r
+ this.prompt();\r
+ }\r
+\r
+In this example a handler is set up to process the command "login <username>" and ask for\r
+a password for the given user name in raw mode. Note the use of the object `Terminal.env'\r
+which is just an empty object set up at the creation of the Terminal instance. Its only\r
+purpose is to provide an individual namespace for private data to be stored by a Terminal\r
+instance.\r
+\r
+NOTE: The flag `Terminal.lock' is used to control the keyboard locking. If we would not\r
+set this to `false' before leaving in raw mode, we would be caught in dead-lock, since no\r
+input could be entered and our handler wouldn't be called again. - A dreadful end of our\r
+terminal session.\r
+\r
+NOTE: Raw mode utilizes the property `Terminal.lastLine' to collect the input string.\r
+This is normally emty, when a handler is called. This is not the case if your script left\r
+the input process on a call of ctrlHandler. You should clear `Terminal.lastLine' in such\r
+a case, if you're going to enter raw mode immediatly after this.\r
+\r
+\r
+3.2.3 Character Mode\r
+\r
+If the flag `Terminal.charMode' is set to a value evaluating to `true', the terminal is in\r
+character mode. In this mode the numeric ASCII value of the next key typed is stored in\r
+`Terminal.inputChar'. The input is NOT echoed to the terminal. NO locking or cursor\r
+control is performed and left to the handler.\r
+You can use this mode to implement your editor or a console game.\r
+`Terminal.charMode' takes precedence over `Terminal.rawMode'.\r
+\r
+p.e.: \r
+\r
+ // using char mode\r
+\r
+ function myTermHandler() {\r
+ // this is the normal handler\r
+ this.newLine();\r
+ // simple parsing\r
+ var args = this.lineBuffer.split(' ');\r
+ var cmd = args[0];\r
+ if (cmd == 'edit') {\r
+ // init the editor\r
+ myEditor(this);\r
+ // redirect the handler to editor\r
+ this.handler = myEditor;\r
+ // leave in char mode\r
+ this.charMode = true;\r
+ // show cursor\r
+ this.cursorOn();\r
+ // don't forget unlocking\r
+ this.lock = false;\r
+ return;\r
+ }\r
+ /*\r
+ other actions ...\r
+ */\r
+ this.prompt();\r
+ }\r
+\r
+ function myEditor(initterm) {\r
+ // our dummy editor (featuring modal behaviour)\r
+ if (initterm) {\r
+ // perform initialization tasks\r
+ initterm.clear();\r
+ initterm.write('this is a simple test editor; leave with <ESC> then "q"%n%n');\r
+ initterm.env.mode = '';\r
+ // store a reference of the calling handler\r
+ initterm.env.handler = initterm.handler;\r
+ return;\r
+ }\r
+ // called as handler -> lock first\r
+ this.lock=true;\r
+ // hide cursor\r
+ this.cursorOff();\r
+ var key = this.inputChar;\r
+ if (this.env.mode == 'ctrl') {\r
+ // control mode\r
+ if (key == 113) {\r
+ // "q" => quit\r
+ // leave charMode and reset the handler to normal\r
+ this.charMode = false;\r
+ this.handler = this.env.handler;\r
+ // clear the screen\r
+ this.clear();\r
+ // prompt and return\r
+ this.prompt();\r
+ return;\r
+ }\r
+ else {\r
+ // leave control mode\r
+ this.env.mode = '';\r
+ }\r
+ }\r
+ else {\r
+ // edit mode\r
+ if (key == termKey.ESC) {\r
+ // enter control mode\r
+ // we'd better indicate this in a status line ...\r
+ this.env.mode = 'ctrl';\r
+ }\r
+ else if (key == termKey.LEFT) {\r
+ // cursor left\r
+ }\r
+ else if (key == termKey.RIGHT) {\r
+ // cursor right\r
+ }\r
+ if (key == termKey.UP) {\r
+ // cursor up\r
+ }\r
+ else if (key == termKey.DOWN) {\r
+ // cursor down\r
+ }\r
+ else if (key == termKey.CR) {\r
+ // cr or enter\r
+ }\r
+ else if (key == termKey.BS) {\r
+ // backspace\r
+ }\r
+ else if (key == termKey.DEL) {\r
+ // fwd delete\r
+ // conf.DELisBS is not evaluated in charMode!\r
+ }\r
+ else if (this.isPrintable(key)) {\r
+ // printable char - just type it\r
+ var ch = String.fromCharCode(key);\r
+ this.type(ch);\r
+ }\r
+ }\r
+ // leave unlocked with cursor\r
+ this.lock = false;\r
+ this.cursorOn();\r
+ }\r
+\r
+\r
+Note the redirecting of the input handler to replace the command line handler by the\r
+editor. The method `Terminal.clear()' clears the terminal.\r
+`Terminal.cursorOn()' and `Terminal.cursorOff()' are used to show and hide the cursor.\r
+\r
+\r
+\r
+3.3 Other Handlers\r
+\r
+There are two more handlers that can be specified in the configuration object:\r
+\r
+\r
+3.3.1 initHandler\r
+\r
+`initHandler' is called at the end of the initialization triggered by `Terminal.open()'.\r
+The default action - if no `initHandler' is specified - is:\r
+\r
+ // default initilization\r
+\r
+ this.write(this.conf.greeting);\r
+ this.newLine();\r
+ this.prompt();\r
+\r
+Use `initHandler' to perform your own start up tasks (e.g. show a start up screen). Keep\r
+in mind that you should unlock the terminal and possibly show a cursor to give the\r
+impression of a usable terminal.\r
+\r
+\r
+3.3.2 exitHandler\r
+\r
+`exitHandler' is called by `Terminal.close()' just before hiding the terminal. You can use\r
+this handler to implement any tasks to be performed on exit. Note that this handler is\r
+called even if the terminal is closed on <ESC> outside of your inputHandlers control.\r
+\r
+See the file "multiterm_test.html" for an example.\r
+\r
+\r
+\r
+3.4 Overview: Flags for Behaviour Control\r
+\r
+These falgs are accessible as `Terminal.<flag>' at runtime. If not stated else, the\r
+initial value may be specified in the configuration object.\r
+The configuration object and its properties are accessible at runtime via `Terminal.conf'.\r
+\r
+\r
+ NAME DEFAULT VALUE MEANING\r
+\r
+ blink_delay 500 delay for cursor blinking in milliseconds.\r
+\r
+ crsrBlinkMode false true for blinking cursor.\r
+ if false, cursor is static.\r
+ \r
+ crsrBlockMode true true for block-cursor else underscore.\r
+\r
+ DELisBS false handle <DEL> as <BACKSPACE>.\r
+\r
+ printTab true handle <TAB> as printable (prints as space)\r
+ if false <TAB> is handled as a control character\r
+\r
+ printEuro true handle the euro sign as valid input char.\r
+ if false char 0x20AC is printed, but not accepted\r
+ in the command line\r
+\r
+ catchCtrlH true handle ^H as <BACKSPACE>.\r
+ if false, ^H must be tracked by a custom\r
+ ctrlHandler.\r
+\r
+ closeOnESC true close terminal on <ESC>.\r
+ if true, <ESC> is not available for ctrHandler.\r
+\r
+\r
+ historyUnique false unique history entries.\r
+ if true, entries that are identical to the last\r
+ entry in the user history will not be added.\r
+\r
+ charMode false terminal in character mode (tracks next key-code).\r
+ (runtime only)\r
+ \r
+ rawMode false terminal in raw mode (no echo, no editing).\r
+ (runtime only)\r
+\r
+\r
+Not exactly a flag but useful:\r
+\r
+ ps '>' prompt string.\r
+\r
+\r
+\r
+\r
+4 Output Methods\r
+\r
+Please note that any output to the terminal implies an advance of the cursor. This means,\r
+that if your output reaches the last column of your terminal, the cursor is advanced and\r
+a new line is opened automatically. This procedure may include scrolling to make room for\r
+the new line. While this is not of much interest for most purposes, please note that, if\r
+you output a string of length 80 to a 80-columns-terminal, and a new line, and another\r
+string, this will result in an empty line between the two strings.\r
+\r
+\r
+4.1 Terminal.type( <text> [,<stylevector>] )\r
+\r
+Types the string <text> at the current cursor position to the terminal. Long lines are\r
+broken where the last column of the terminal is reached and continued in the next line.\r
+`Terminal.write()' does not support any kind of arbitrary line breaks. (This is just a\r
+basic output routine. See `Terminal.write()' for a more powerful output method.)\r
+\r
+A bitvector may be supplied as an optional second argument to represent a style or a\r
+combination of styles. The meanings of the bits set are interpreted as follows:\r
+\r
+<stylevector>:\r
+\r
+ 1 ... reverse (2 power 0)\r
+ 2 ... underline (2 power 1)\r
+ 4 ... italics (2 power 2)\r
+ 8 ... strike (2 power 3)\r
+\r
+So "Terminal.type( 'text', 5 )" types "text" in italics and reverse video.\r
+\r
+Note:\r
+There is no bold, for most monospaced fonts (including Courier) tend to render wider in\r
+bold. Since this would bring the terminal's layout out of balance, we just can't use bold\r
+as a style. - Sorry.\r
+\r
+The HTML-representation of this styles are defined in "TermGlobals.termStyleOpen" and\r
+"TermGlobals.termStyleClose".\r
+\r
+\r
+4.2 Terminal.write( <text> [,<usemore>] )\r
+\r
+Writes a text with markup to the terminal. If an optional second argument evaluates to\r
+true, a UN*X-style utility like `more' is used to page the text. The text may be supplied\r
+as a single string (with newline character "\n") or as an array of lines. Any other input\r
+is transformed to a string value before output.\r
+\r
+4.2.1 Mark-up:\r
+\r
+`Terminal.write()' employs a simple mark-up with the following syntax:\r
+\r
+<markup>: %([+|-]<style>|n|CS|%)\r
+ \r
+ where "+" and '-' are used to switch on and off a style, where\r
+ \r
+ <style>:\r
+ \r
+ "i" ... italics\r
+ "r" ... reverse\r
+ "s" ... strike\r
+ "u" ... underline\r
+ \r
+ "p" ... reset to plain ("%+p" == "%-p")\r
+ \r
+ styles may be combined and may overlap. (e.g. "This is %+rREVERSE%-r, %+uUNDER%+iSCORE%-u%-i.")\r
+ \r
+ "%n" represents a new line (in fact "\n" is translated to "%n" before processing)\r
+ \r
+ "%CS" clears the terminal screen\r
+ \r
+ "%%" represents the percent character ('%')\r
+\r
+\r
+4.2.2 Buffering:\r
+\r
+`Terminal.write()' writes via buffered output to the terminal. This means that the\r
+provided text is rendered to a buffer first and then only the visible parts are transfered\r
+to the terminal display buffers. This avoids scrolling delays for long output.\r
+\r
+4.2.3 UseMore Mode:\r
+\r
+The buffering of `Terminal.write()' allows for pagewise output, which may be specified by\r
+a second boolean argument. If <usemore> evaluates to `true' and the output exceeds the\r
+range of empty rows on the terminal screen, `Terminal.write()' performs like the UN*X\r
+utility `more'. The next page may be accessed by hitting <SPACE> while <q> terminates\r
+paging and returns with the prompt (-> `Terminal.prompt()').\r
+\r
+To use this facillity make sure to return immediatly after calling `Terminal.write()' in\r
+order to allow the more-routine to track the user input.\r
+The terminal is set to "charMode == false" afterwards.\r
+\r
+p.e.:\r
+\r
+ // using Terminal.write as a pager\r
+\r
+ function myTermHandler() {\r
+ this.newLine();\r
+ var args = this.lineBuffer.split(' ');\r
+ var cmd = args[0];\r
+ if (cmd == 'more') {\r
+ var page = args[1];\r
+ if (myPages[page]) {\r
+ // Terminal.write as a pager\r
+ this.write(myPages[page], true);\r
+ return;\r
+ }\r
+ else {\r
+ // Terminal.write for simple output\r
+ this.write('no such page.');\r
+ }\r
+ }\r
+ /*\r
+ other actions ...\r
+ */\r
+ this.prompt();\r
+ }\r
+\r
+\r
+4.3 Terminal.typeAt( <r>, <c>, <text> [,<stylevector>] )\r
+\r
+Output the string <text> at row <r>, col <c>.\r
+For <stylevector> see `Terminal.type()'.\r
+`Terminal.typeAt()' does not move the cursor.\r
+\r
+\r
+4.4 Terminal.setChar( <charcode>, <r>, <c> [,<stylevector>] )\r
+\r
+Output a single character represented by the ASCII value of <charcode> at row <r>, col <c>.\r
+For <stylevector> see `Terminal.type()'.\r
+\r
+\r
+4.5 Terminal.newLine()\r
+\r
+Moves the cursor to the first column of the next line and performs scrolling, if needed.\r
+\r
+\r
+4.6 Terminal.clear()\r
+\r
+Clears the terminal screen. (Returns with cursor off.)\r
+\r
+\r
+4.7 Terminal.statusLine( <text> [,<stylevector> [,<lineoffset>]] )\r
+\r
+All output acts on a logical screen with the origin at row 0 / col 0. While the origin is\r
+fixed, the logical width and height of the terminal are defined by `Terminal.maxCols' and\r
+`Terminal.maxLines'. These are set to the configuration dimensions at initilization and by\r
+`Terminal.reset()', but may be altered at any moment. Please note that there are no bounds\r
+checked, so make sure that `Terminal.maxCols' and `Terminal.maxLines' are less or equal\r
+to the configuration dimensions.\r
+\r
+You may want to decrement `Terminal.maxLines' to keep space for a reserved status line.\r
+`Terminal.statusLine( <text>, <style> )' offers a simple way to type a text to the last\r
+line of the screen as defined by the configuration dimensions.\r
+\r
+ // using statusLine()\r
+\r
+ function myHandler() {\r
+ // ...\r
+ // reserve last line\r
+ this.maxLines = term.conf.rows-1;\r
+ // print to status line in reverse video\r
+ this.statusLine("Status: <none>", 1);\r
+ // ...\r
+ }\r
+\r
+For multiple status lines the optional argument <lineoffset> specifies the addressed row,\r
+where 1 is the line closest to the bottom, 2 the second line from the bottom and so on.\r
+(default: 1)\r
+\r
+\r
+4.8 Terminal.printRowFromString( <r> , <text> [,<stylevector>] )\r
+\r
+Outputs the string <text> to row <r> in the style of an optional <stylevector>.\r
+If the string's length exceeds the length of the row (up to `Terminal.conf.cols'), extra\r
+characteres are ignored, else any extra space is filled with character code 0 (prints as\r
+<SPACE>).\r
+The valid range for <row> is: 0 >= <row> < `Terminal.maxLines'.\r
+`Terminal.printRowFromString()' does not set the cursor.\r
+\r
+You could, for example, use this method to output a line of a text editor's buffer.\r
+\r
+p.e.:\r
+\r
+ // page refresh function of a text editor\r
+\r
+ function myEditorRefresh(termref, topline) {\r
+ // termref: reference to Terminal instance\r
+ // topline: index of first line to print\r
+ // lines of text are stored in termref.env.lines\r
+ for (var r=0; r<termref.maxLines; r++) {\r
+ var i = topline + r;\r
+ if (i < termref.env.lines.length) {\r
+ // output stored line\r
+ termref.printRowFromString(r, termref.env.lines[i]);\r
+ }\r
+ else {\r
+ // output <tilde> for empty line\r
+ termref.printRowFromString(r, '~');\r
+ }\r
+ }\r
+ // set cursor to origin\r
+ termref.r = termref.c = 0; // same as termref.cursorSet(0, 0);\r
+ }\r
+\r
+\r
+4.9 Terminal.redraw( <row> )\r
+\r
+Basic function to redraw a terminal row <row> according to screen buffer values.\r
+For hackers only. (e.g.: for a console game, hack screen buffers first and redraw all\r
+changed rows at once.)\r
+\r
+\r
+\r
+\r
+5 Cursor Methods and Editing\r
+\r
+\r
+5.1 Terminal.cursorOn()\r
+\r
+Show the cursor.\r
+\r
+\r
+5.2 Terminal.cursorOff()\r
+\r
+Hide the cursor.\r
+\r
+\r
+5.3 Terminal.cursorSet( <r>, <c> )\r
+\r
+Set the cursor position to row <r> column <c>.\r
+`Terminal.cursorSet()' preserves the cursor's active state (on/off).\r
+\r
+\r
+5.4 Terminal.cursorLeft()\r
+\r
+Move the cursor left. (Movement is restricted to the logical input line.)\r
+`Terminal.cursorLeft()' preserves the cursor's active state (on/off).\r
+\r
+\r
+5.5 Terminal.cursorRight()\r
+\r
+Move the cursor right. (Movement is restricted to the logical input line.)\r
+`Terminal.cursorRight()' preserves the cursor's active state (on/off).\r
+\r
+\r
+5.6 Terminal.backspace()\r
+\r
+Delete the character left from the cursor, if the cursor is not in first position of the\r
+logical input line.\r
+`Terminal.backspace()' preserves the cursor's active state (on/off).\r
+\r
+\r
+5.7 Terminal.fwdDelete()\r
+\r
+Delete the character under the cursor.\r
+`Terminal.fwdDelete()' preserves the cursor's active state (on/off).\r
+\r
+\r
+5.8 Terminal.isPrintable( <key code> [,<unicode page 1 only>] )\r
+\r
+Returns `true' if the character represented by <key code> is printable with the current\r
+settings. An optional second argument <unicode page 1 only> limits the range of valid\r
+values to 255 with the exception of the Euro sign, if the flag `Terminal.printEuro' is set.\r
+(This second flag is used for input methods but not for output methods. So you may only\r
+enter portable characters, but you may print others to the terminals screen.)\r
+\r
+\r
+\r
+\r
+6 Other Methods of the Terminal Object\r
+\r
+6.1 Terminal.prompt()\r
+\r
+Performes the following actions:\r
+\r
+ * advance the cursor to a new line, if the cursor is not at 1st column\r
+ * type the prompt string (as specified in the configuaration object)\r
+ * show the cursor\r
+ * unlock the terminal\r
+\r
+(The value of the prompt string can be accessed and changed in `Terminal.ps'.)\r
+\r
+\r
+6.2 Terminal.reset()\r
+\r
+Resets the terminal to sane values and clears the terminal screen.\r
+\r
+\r
+6.3 Terminal.open()\r
+\r
+Opens the terminal. If this is a fresh instance, the HTML code for the terminal is\r
+generated. On re-entry the terminal's visibility is set to `true'. Initialization tasks\r
+are performed and the optional initHandler called. If no initHandler is specified in the\r
+configuration object, the greeting (configuration or default value) is shown and the user\r
+is prompted for input.\r
+\r
+v.1.01: `Terminal.open()' now checks for the existence of the DHTML element as defined in\r
+ `Terminal.conf.termDiv' and returns success.\r
+\r
+\r
+6.4 Terminal.close()\r
+\r
+Closes the terminal and hides its visibility. An optional exitHandler (specified in the\r
+configuration object) is called, and finally the flag `Terminal.closed' is set to true. So\r
+you can check for existing terminal instances as you would check for a `window' object\r
+created by `window.open()'.\r
+\r
+p.e.:\r
+\r
+ // check for a terminals state\r
+ // let array "term" hold references to terminals\r
+\r
+ if (term[n]) {\r
+ if (term[n].closed) {\r
+ // terminal exists and is closed\r
+ // re-enter via "term[n].open()"\r
+ }\r
+ else {\r
+ // terminal exists and is currently open\r
+ }\r
+ }\r
+ else {\r
+ // no such terminal\r
+ // create it via "term[n] = new Terminal()"\r
+ }\r
+\r
+\r
+6.5 Terminal.focus()\r
+\r
+Set the keyboard focus to this instance of Terminal. (As `window.focus()'.)\r
+\r
+\r
+6.6 Terminal.moveTo( <x>, <y> )\r
+\r
+Move the terminal to position <x>/<y> in px.\r
+(As `window.moveTo()', but inside the HTML page.)\r
+\r
+\r
+6.7 Terminal.resizeTo( <x>, <y> )\r
+\r
+Resize the terminal to dimensions <x> cols and <y> rows.\r
+<x> must be at least 4, <y> at least 2.\r
+`Terminal.resizeTo()' resets `Terminal.conf.rows', `Terminal.conf.cols',\r
+`Terminal.maxLines', and `Terminal.maxCols' to <y> and <x>, but leaves the instance' state\r
+else unchanged. Clears the terminal's screen and returns success.\r
+\r
+(A bit like `window.resizeTo()', but with rows and cols instead of px.)\r
+\r
+\r
+6.8 Terminal.getDimensions()\r
+\r
+Returns an object with properties "width" and "height" with numeric values for the\r
+terminal's outer dimensions in px. Values are zero (0) if the element is not present or\r
+if the method fails otherwise.\r
+\r
+\r
+6.9 Terminal.rebuild()\r
+\r
+Rebuilds the Terminal object's GUI preserving its state and content.\r
+Use this to change the color theme on the fly.\r
+\r
+p.e.:\r
+\r
+ // change color settings on the fly\r
+ // here: set bgColor to white and font style to "termWhite"\r
+ // method rebuild() updates the GUI without side effects\r
+\r
+ term.conf.bgColor = '#ffffff';\r
+ term.conf.fontClass = 'termWhite';\r
+ term.rebuild();\r
+\r
+\r
+\r
+\r
+7 Global Static Methods (TermGlobals)\r
+\r
+\r
+7.1 TermGlobals.setFocus( <termref> )\r
+\r
+Sets the keyboard focus to the instance referenced by <termref>.\r
+The focus is controlled by `TermGlobals.activeTerm' which may be accessed directly.\r
+See also: `Terminal.focus()'\r
+\r
+\r
+7.2 TermGlobals.keylock (Global Locking Flag)\r
+\r
+The global flag `TermGlobals.keylock' allows temporary keyboard locking without any\r
+other change of state. Use this to free the keyboard for any other resources.\r
+(added in v.1.03)\r
+\r
+\r
+7.3 TermGlobals Text Methods\r
+\r
+There is a small set of methods for common terminal related string tasks:\r
+\r
+\r
+7.3.1 TermGlobals.normalize( <n>, <fieldlength> )\r
+\r
+Converts a number to a string, which is filled at its left with zeros ("0") to the total\r
+length of <filedlength>. (e.g.: "TermGlobals.normalize(1, 2)" => "01")\r
+\r
+\r
+7.3.2 TermGlobals.fillLeft( <value>, <fieldlength> )\r
+\r
+Converts a value to a string and fills it to the left with blanks to <fieldlength>.\r
+\r
+\r
+7.3.3 TermGlobals.center( <text>, <length> )\r
+\r
+Adds blanks at the left of the string <text> until the text would be centered at a line\r
+of length <length>. (No blanks are added to the the right.)\r
+\r
+\r
+7.3.4 TermGlobals.stringReplace( <string1>, <string2>, <text> )\r
+\r
+Replaces all occurences of the string <string1> in <text> with <string2>.\r
+This is just a tiny work around for browsers with no support of RegExp.\r
+\r
+\r
+\r
+\r
+8 Localization\r
+\r
+The strings and key-codes used by the more utility of `Terminal.write()' are the only\r
+properties of "termlib.js" that may need localization. These properties are defined in\r
+`TermGlobals'. You may override them as needed:\r
+\r
+PROPERTY STANDARD VALUE COMMENT\r
+\r
+TermGlobals.lcMorePrompt1 ' -- MORE -- ' 1st string\r
+TermGlobals.lcMorePromtp1Style 1 reverse\r
+TermGlobals.lcMorePrompt2 ' (Type: space to continue, \'q\' to quit)' appended string\r
+TermGlobals.lcMorePrompt2Style 0 plain\r
+TermGlobals.lcMoreKeyAbort 113 (key-code: q)\r
+TermGlobals.lcMoreKeyContinue 32 (key-code <SPACE>)\r
+\r
+\r
+As "TermGlobals.lcMorePrompt2" is appended to "TermGlobals.lcMorePrompt1" make sure that\r
+the length of the combined strings does not exceed `Terminal.conf.cols'.\r
+\r
+\r
+\r
+\r
+9 Cross Browser Functions\r
+\r
+For DHTML rendering some methods - as needed by the Terminal library - are provided.\r
+These may also be accessed for other purposes.\r
+\r
+\r
+9.1 TermGlobals.writeElement( <element id>, <text> [,<NS4 parent document>] )\r
+\r
+Writes <text> to the DHTML element with id/name <element id>. \r
+<NS4 parent document> is used for NS4 only and specifies an optional reference to a parent\r
+document (default `window.document').\r
+\r
+9.2 TermGlobals.setElementXY( <element id>, <x>, <y> )\r
+\r
+Sets the DHTML element with id/name <element id> to position <x>/<y>.\r
+For NS4 works only with children of the top document (window.document).\r
+\r
+\r
+9.3 TermGlobals.setVisible( <element id>, <value> )\r
+\r
+If <value> evaluates to `true' show DHTML element with id/name <element id> else hide it.\r
+For NS4 works only with children of the top document (window.document).\r
+\r
+\r
+9.4 Custom Fixes for Missing String Methods\r
+\r
+Although `String.fromCharCode' and `String.prototype.charCodeAt' are defined by ECMA-262-2\r
+specifications, a few number of browsers lack them in their JavaScript implementation. At\r
+compile time custom methods are installed to fix this. Please note that they work only\r
+with ASCII characters and values in the range of [0x20-0xff].\r
+\r
+\r
+9.5 TermGlobals.setDisplay( <element id>, <value> )\r
+\r
+Sets the style.display property of the element with id/name <element id> to the given\r
+<value>. (added with v. 1.06)\r
+\r
+\r
+\r
+\r
+10 Architecture, Internals\r
+\r
+10.1 Global Entities\r
+\r
+The library is designed to leave only a small foot print in the namespace while providing\r
+suitable usability:\r
+\r
+ Globals defined in this library:\r
+\r
+ Terminal (Terminal object, `new' constructor and prototype methods)\r
+ TerminalDefaults (default configuration, static object)\r
+ termDefaultHandler (default command line handler, static function)\r
+ TermGlobals (common vars and code for all instances, static object and methods)\r
+ termKey (named mappings for special keys, static object)\r
+ termDomKeyRef (special key mapping for DOM key constants, static object)\r
+\r
+\r
+ Globals defined for fixing String methods, if missing\r
+ (String.fromCharCode, String.prototype.charCodeAt):\r
+\r
+ termString_keyref, termString_keycoderef, termString_makeKeyref\r
+\r
+ \r
+ Required CSS classes for font definitions: ".term", ".termReverse".\r
+\r
+\r
+\r
+10.2 I/O Architecture\r
+\r
+The Terminal object renders keyboard input from keyCodes to a line buffer and/or to a\r
+special keyCode buffer. In normal input mode printable input is echoed to the screen\r
+buffers. Special characters like <LEFT>, <RIGHT>, <BACKSPACE> are processed for command\r
+line editing by the internal key-handler `TermGlobals.keyHandler' and act directly on the\r
+screen buffers. On <CR> or <ENTER> the start and end positions of the current line are\r
+evaluated (terminated by ASCII 0x01 at the beginning which separates the prompt from the\r
+user input, and any value less than ASCII 0x20 (<SPACE>) at the right end). Then the\r
+character representation for the buffer values in this range are evaluated and\r
+concatenated to a string stored in `Terminal.lineBuffer'. As this involves some\r
+ASCII-to-String-transformations, the range of valid printable input characters is limited\r
+to the first page of unicode characters (0x0020-0x00ff).\r
+\r
+There are two screen buffers for output, one for character codes (ASCII values) and one\r
+for style codes. Style codes represent combination of styles as a bitvector (see\r
+`Terminal.type' for bit values.) The method `Terminal.redraw(<row>)' finally renders the\r
+buffers values to a string of HTML code, which is written to the HTML entity holding the\r
+according terminal row. The character buffer is a 2 dimensional array\r
+`Terminal.charBuf[<row>][<col>]' with ranges for <row> from 0 to less than\r
+`Terminal.conf.rows' and for <col> from 0 to less than `Terminal.conf.cols'. The style\r
+buffer is a 2 dimensional array `Terminal.styleBuf[<row>][<col>]' with according ranges.\r
+\r
+So every single character is represented by a ASCII code in `Terminal.charBuf' and a\r
+style-vector in `Terminal.styleBuf'. The range of printable character codes is unlimitted\r
+but should be kept to the first page of unicode characters (0x0020-0x00ff) for\r
+compatibility purpose. (c.f. 8.4)\r
+\r
+Keyboard input is first handled on the `KEYDOWN' event by the handler `TermGlobals.keyFix'\r
+to remap the keyCodes of cursor keys to consistent values. (To make them distinctable from\r
+any other possibly printable values, the values of POSIX <IS4> to <IS1> where chosen.)\r
+The mapping of the cursor keys is stored in the properties LEFT, RIGHT, UP, and DOWN of\r
+the global static object `termKey'.\r
+\r
+The main keyboard handler `TermGlobals.keyHandler' (invoked on `KEYPRESS' or by\r
+`TermGlobals.keyFix') does some final mapping first. Then the input is evaluated as\r
+controlled by the flags `Terminal.rawMode' and `Terminal.charMode' with precedence of the\r
+latter. In dependancy of the mode defined and the handlers currently defined, the input\r
+either is ignored, or is internally processed for command line editing, or one of the\r
+handlers is called.\r
+\r
+In the case of the simultanous presecence of two instances of Terminal, the keyboard focus\r
+is controlled via a reference stored in `TermGlobals.activeTerm'. This reference is also\r
+used to evaluate the `this'-context of the key handlers which are methods of the static\r
+Object `TermGlobals'.\r
+\r
+A terminal's screen consists of a HTML-table element residing in the HTML/CSS division\r
+spcified in `Terminal.conf.termDiv'. Any output is handled on a per row bases. The\r
+individual rows are either nested sub-divisions of the main divisions (used for NS4 or\r
+browsers not compatible to the "Gecko" engine) or the indiviual table data elements (<TD>)\r
+of the terminal's inner table (used for browsers employing the "Gecko" engine).\r
+(This implementation was chosen for rendering speed and in order to minimize any screen\r
+flicker.) Any output or change of state in a raw results in the inner HTML contents of a\r
+row's HTML element to be rewritten. Please note that as a result of this a blinking cursor\r
+may cause a flicker in the line containing the cursor's position while displayed by a\r
+browser, which employs the "Gecko" engine.\r
+\r
+\r
+\r
+10.3 Compatibility\r
+\r
+Standard web browsers with a JavaScript implementation compliant to ECMA-262 2nd edition\r
+[ECMA262-2] and support for the anonymous array and object constructs and the anonymous\r
+function construct in the form of "myfunc = function(x) {}" (c.f. ECMA-262 3rd edion\r
+[ECMA262-3] for details). This comprises almost all current browsers but Konquerer (khtml)\r
+and versions of Apple Safari for Mac OS 10.0-10.28 (Safari < 1.1) which lack support for\r
+keyboard events.\r
+\r
+To provide a maximum of compatibilty the extend of language keywords used was kept to a\r
+minimum and does not exceed the lexical conventions of ECMA-262-2. Especially there is no\r
+use of the `switch' statement or the `RegExp' method of the global object. Also the use of\r
+advanced Array methods like `push', `shift', `splice' was avoided.\r
+\r
+\r
+\r
+\r
+11 History\r
+\r
+This library evolved from the terminal script "TermApp" ((c) N. Landsteiner 2003) and is\r
+in its current form a down scaled spinn-off of the "JS/UIX" project [JS/UIX] (evolution\r
+"JS/UIX v0.5"). c.f.: <http://www.masswerk.at/jsuix>\r
+\r
+v 1.01: added Terminal.prototype.resizeTo(x,y)\r
+ added Terminal.conf.fontClass (=> configureable class name)\r
+ Terminal.prototype.open() now checks for element conf.termDiv in advance\r
+ and returns success.\r
+\r
+v 1.02: added support for <TAB> and Euro sign\r
+ Terminal.conf.printTab\r
+ Terminal.conf.printEuro\r
+ and method Terminal.prototype.isPrintable(keycode)\r
+ added support for getopt to sample parser ("parser_sample.html")\r
+\r
+\r
+v 1.03: added global keyboard locking (TermGlobals.keylock)\r
+ modified Terminal.prototype.redraw for speed (use of locals)\r
+\r
+\r
+v 1.04: modified the key handler to fix a bug with MSIE5/Mac\r
+ fixed a bug in TermGlobals.setVisible with older MSIE-alike browsers without\r
+ DOM support.\r
+ moved the script of the sample parser to an individual document\r
+ => "termlib_parser.js" (HTML document is "parser_sample.html" as before)\r
+\r
+v 1.05: added config flag historyUnique.\r
+\r
+v 1.06: fixed CTRl+ALT (Windows alt gr) isn't CTRL any more\r
+ -> better support for international keyboards with MSIE/Win.\r
+ fixed double backspace bug for Safari;\r
+ added TermGlobals.setDisplay for setting style.display props\r
+ termlib.js now outputs lower case html (xhtml compatibility)\r
+ (date: 12'2006)\r
+\r
+v 1.07: added method Terminal.rebuild() to rebuild the GUI with new color settings.\r
+ (date: 01'2007)\r
+\r
+\r
+\r
+\r
+12 Example for a Command Line Parser\r
+\r
+ // parser example, splits command line to args with quoting and escape\r
+ // for use as `Terminal.handler'\r
+ \r
+ function commandHandler() {\r
+ this.newLine();\r
+ var argv = ['']; // arguments vector\r
+ var argQL = ['']; // quoting level\r
+ var argc = 0; // arguments cursor\r
+ var escape = false ; // escape flag\r
+ for (var i=0; i<this.lineBuffer.length; i++) {\r
+ var ch= this.lineBuffer.charAt(i);\r
+ if (escape) {\r
+ argv[argc] += ch;\r
+ escape = false;\r
+ }\r
+ else if ((ch == '"') || (ch == "'") || (ch == "`")) {\r
+ if (argQL[argc]) {\r
+ if (argQL[argc] == ch) {\r
+ argc ++;\r
+ argv[argc] = argQL[argc] = '';\r
+ }\r
+ else {\r
+ argv[argc] += ch;\r
+ }\r
+ }\r
+ else {\r
+ if (argv[argc] != '') {\r
+ argc ++;\r
+ argv[argc] = '';\r
+ argQL[argc] = ch;\r
+ }\r
+ else {\r
+ argQL[argc] = ch;\r
+ }\r
+ }\r
+ }\r
+ else if ((ch == ' ') || (ch == '\t')) {\r
+ if (argQL[argc]) {\r
+ argv[argc] += ch;\r
+ }\r
+ else if (argv[argc] != '') {\r
+ argc++;\r
+ argv[argc] = argQL[argc] = '';\r
+ }\r
+ }\r
+ else if (ch == '\\') {\r
+ escape = true;\r
+ }\r
+ else {\r
+ argv[argc] += ch;\r
+ }\r
+ }\r
+ if ((argv[argc] == '') && (!argQL[argc])) {\r
+ argv.length--;\r
+ argQL.length--;\r
+ }\r
+ if (argv.length == 0) {\r
+ // no commmand line input\r
+ }\r
+ else if (argQL[0]) {\r
+ // first argument quoted -> error\r
+ this.write("Error: first argument quoted by "+argQL[0]);\r
+ }\r
+ else {\r
+ argc = 0;\r
+ var cmd = argv[argc++];\r
+ /*\r
+ parse commands\r
+ 1st argument is argv[argc]\r
+ arguments' quoting levels in argQL[argc] are of (<empty> | ' | " | `)\r
+ */\r
+ if (cmd == 'help') {\r
+ this.write(helpPage);\r
+ }\r
+ else if (cmd == 'clear') {\r
+ this.clear();\r
+ }\r
+ else if (cmd == 'exit') {\r
+ this.close();\r
+ return;\r
+ }\r
+ else {\r
+ // for test purpose just output argv as list\r
+ // assemple a string of style-escaped lines and output it in more-mode\r
+ s=' ARG QL VALUE%n';\r
+ for (var i=0; i<argv.length; i++) {\r
+ s += TermGlobals.stringReplace('%', '%%',\r
+ TermGlobals.fillLeft(i, 6) +\r
+ TermGlobals.fillLeft((argQL[i])? argQL[i]:'-', 4) +\r
+ ' "' + argv[i] + '"'\r
+ ) + '%n';\r
+ }\r
+ this.write(s, 1);\r
+ return;\r
+ }\r
+ }\r
+ this.prompt();\r
+ }\r
+\r
+\r
+The file "parser_sample.html" features a stand-alone parser ("termlib_parser.js") very\r
+much like this. You are free to use it according to the termlib-license (see sect. 13).\r
+It provides configurable values for quotes and esape characters and imports the parsed\r
+argument list into a Terminal instance's namespace. ("parser_sample.html" and\r
+"termlib_parser.js" should accompany this file.)\r
+\r
+\r
+\r
+\r
+13 License\r
+\r
+This JavaScript-library is free for private and academic use.\r
+Please include a readable copyright statement and a backlink to <http://www.masswerk.at>\r
+in the web page. The library should always be accompanied by the 'readme.txt' and the\r
+sample HTML-documents.\r
+\r
+The term "private use" includes any personal or non-commercial use, which is not related\r
+to commercial activites, but excludes intranet, extranet and/or public net applications\r
+that are related to any kind of commercial or profit oriented activity.\r
+\r
+For commercial use see <http://www.masswerk.at> for contact information.\r
+\r
+Any changes to the library should be commented and be documented in the readme-file.\r
+Any changes must be reflected in the `Terminal.version' string as\r
+"Version.Subversion (compatibility)".\r
+\r
+\r
+\r
+\r
+14 Disclaimer\r
+\r
+This software is distributed AS IS and in the hope that it will be useful, but WITHOUT ANY\r
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR\r
+PURPOSE. The entire risk as to the quality and performance of the product is borne by the\r
+user. No use of the product is authorized hereunder except under this disclaimer.\r
+\r
+\r
+\r
+\r
+15 References\r
+\r
+[ECMA262-2] "ECMAScript Language Specification" Standard ECMA-262 2nd Edition\r
+ August 1998 (ISO/IEC 16262 - April 1998)\r
+\r
+[ECMA262-3] "ECMAScript Language Specification" Standard ECMA-262 3rd Edition Final\r
+ 24 March 2000\r
+\r
+[JS/UIX] JS/UIX - JavaScript Uniplexed Interface eXtension\r
+ <http://www.masswerk.at/jsuix>\r
+\r
+\r
+\r
+\r
+\r
+Norbert Landsteiner / Vienna, August 2005\r
+mass:werk - media environments\r
+<http://www.masswerk.at>\r
+See web site for contact information.\r
--- /dev/null
+.term {\r
+ font-family: courier,fixed,swiss,sans-serif;\r
+ font-size: 12px;\r
+ color: #33d011;\r
+ background: none;\r
+}\r
+\r
+.termReverse {\r
+ color: #111111;\r
+ background: #33d011;\r
+}\r
--- /dev/null
+/*\r
+ termlib.js - JS-WebTerminal Object v1.07\r
+\r
+ (c) Norbert Landsteiner 2003-2005\r
+ mass:werk - media environments\r
+ <http://www.masswerk.at>\r
+\r
+ Creates [multiple] Terminal instances.\r
+\r
+ Synopsis:\r
+\r
+ myTerminal = new Terminal(<config object>);\r
+ myTerminal.open();\r
+\r
+ <config object> overrides any values of object `TerminalDefaults'.\r
+ individual values of `id' must be supplied for multiple terminals.\r
+ `handler' specifies a function to be called for input handling.\r
+ (see `Terminal.prototype.termDefaultHandler()' and documentation.)\r
+\r
+ globals defined in this library:\r
+ Terminal (Terminal object)\r
+ TerminalDefaults (default configuration)\r
+ termDefaultHandler (default command line handler)\r
+ TermGlobals (common vars and code for all instances)\r
+ termKey (named mappings for special keys)\r
+ termDomKeyRef (special key mapping for DOM constants)\r
+\r
+ globals defined for fixing String methods, if missing\r
+ (String.fromCharCode, String.prototype.charCodeAt):\r
+ termString_keyref, termString_keycoderef, termString_makeKeyref\r
+\r
+ required CSS classes for font definitions: ".term", ".termReverse".\r
+\r
+ Compatibilty:\r
+ Standard web browsers with a JavaScript implementation compliant to\r
+ ECMA-262 2nd edition and support for the anonymous array and object\r
+ constructs and the anonymous function construct in the form of\r
+ "myfunc=function(x) {}" (c.f. ECMA-262 3rd edion for details).\r
+ This comprises almost all current browsers but Konquerer (khtml) and\r
+ versions of Apple Safari for Mac OS 10.0-10.28 (Safari 1.0) which\r
+ lack support for keyboard events.\r
+\r
+ License:\r
+ This JavaScript-library is free for private and academic use.\r
+ Please include a readable copyright statement and a backlink to\r
+ <http://www.masswerk.at> in the web page.\r
+ The library should always be accompanied by the 'readme.txt' and the\r
+ sample HTML-documents.\r
+ \r
+ The term "private use" includes any personal or non-commercial use, which\r
+ is not related to commercial activites, but excludes intranet, extranet\r
+ and/or public net applications that are related to any kind of commercial\r
+ or profit oriented activity.\r
+\r
+ For commercial use see <http://www.masswerk.at> for contact information.\r
+ \r
+ Any changes should be commented and must be reflected in `Terminal.version'\r
+ in the format: "Version.Subversion (compatibility)".\r
+\r
+ Disclaimer:\r
+ This software is distributed AS IS and in the hope that it will be useful,\r
+ but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. The entire risk as to\r
+ the quality and performance of the product is borne by the user. No use of\r
+ the product is authorized hereunder except under this disclaimer.\r
+\r
+ ### The sections above must not be removed. ###\r
+ \r
+ version 1.01: added Terminal.prototype.resizeTo(x,y)\r
+ added Terminal.conf.fontClass (=> configureable class name)\r
+ Terminal.prototype.open() now checks for element conf.termDiv\r
+ in advance and returns success.\r
+\r
+ version 1.02: added support for <TAB> and Euro sign\r
+ (Terminal.conf.printTab, Terminal.conf.printEuro)\r
+ and a method to evaluate printable chars:\r
+ Terminal.prototype.isPrintable(keycode)\r
+\r
+ version 1.03: added global keyboard locking (TermGlobals.keylock)\r
+ modified Terminal.prototype.redraw for speed (use of locals)\r
+\r
+ version 1.04: modified the key handler to fix a bug with MSIE5/Mac\r
+ fixed a bug in TermGlobals.setVisible with older MSIE-alike\r
+ browsers without DOM support.\r
+\r
+ version 1.05: added config flag historyUnique.\r
+ \r
+ version 1.06: fixed CTRl+ALT (Windows alt gr) isn't CTRL any more\r
+ fixed double backspace bug for Safari;\r
+ added TermGlobals.setDisplay for setting style.display props\r
+ termlib.js now outputs lower case html (xhtml compatibility)\r
+\r
+ version 1.07: added method rebuild() to rebuild with new color settings.\r
+\r
+*/\r
+\r
+var TerminalDefaults = {\r
+ // dimensions\r
+ cols:80,\r
+ rows:24,\r
+ // appearance\r
+ x:100,\r
+ y:100,\r
+ termDiv:'termDiv',\r
+ bgColor:'#181818',\r
+ frameColor:'#555555',\r
+ frameWidth:1,\r
+ rowHeight:15,\r
+ blinkDelay:500,\r
+ // css class\r
+ fontClass:'term',\r
+ // initial cursor mode\r
+ crsrBlinkMode:false,\r
+ crsrBlockMode:true,\r
+ // key mapping\r
+ DELisBS:false,\r
+ printTab:true,\r
+ printEuro:true,\r
+ catchCtrlH:true,\r
+ closeOnESC:true,\r
+ // prevent consecutive history doublets\r
+ historyUnique:false,\r
+ // optional id\r
+ id:0,\r
+ // strings\r
+ ps:'>',\r
+ greeting:'%+r Terminal ready. %-r',\r
+ // handlers\r
+ handler:termDefaultHandler,\r
+ ctrlHandler:null,\r
+ initHandler:null,\r
+ exitHandler:null\r
+}\r
+\r
+var Terminal = function(conf) {\r
+ if (typeof conf != 'object') conf=new Object();\r
+ else {\r
+ for (var i in TerminalDefaults) {\r
+ if (typeof conf[i] == 'undefined') conf[i]=TerminalDefaults[i];\r
+ }\r
+ }\r
+ this.conf=conf;\r
+ this.version='1.07 (original)';\r
+ this.isSafari= (navigator.userAgent.indexOf('Safari')>=0)? true:false;\r
+ this.setInitValues();\r
+}\r
+\r
+Terminal.prototype.setInitValues=function() {\r
+ this.id=this.conf.id;\r
+ this.maxLines=this.conf.rows;\r
+ this.maxCols=this.conf.cols;\r
+ this.termDiv=this.conf.termDiv;\r
+ this.crsrBlinkMode=this.conf.crsrBlinkMode;\r
+ this.crsrBlockMode=this.conf.crsrBlockMode;\r
+ this.blinkDelay=this.conf.blinkDelay;\r
+ this.DELisBS=this.conf.DELisBS;\r
+ this.printTab=this.conf.printTab;\r
+ this.printEuro=this.conf.printEuro;\r
+ this.catchCtrlH=this.conf.catchCtrlH;\r
+ this.closeOnESC=this.conf.closeOnESC;\r
+ this.historyUnique=this.conf.historyUnique;\r
+ this.ps=this.conf.ps;\r
+ this.closed=false;\r
+ this.r;\r
+ this.c;\r
+ this.charBuf=new Array();\r
+ this.styleBuf=new Array();\r
+ this.scrollBuf=null;\r
+ this.blinkBuffer=0;\r
+ this.blinkTimer;\r
+ this.cursoractive=false;\r
+ this.lock=true;\r
+ this.insert=false;\r
+ this.charMode=false;\r
+ this.rawMode=false;\r
+ this.lineBuffer='';\r
+ this.inputChar=0;\r
+ this.lastLine='';\r
+ this.guiCounter=0;\r
+ this.history=new Array();\r
+ this.histPtr=0;\r
+ this.env=new Object();\r
+ this.ns4ParentDoc=null;\r
+ this.handler=this.conf.handler;\r
+ this.ctrlHandler=this.conf.ctrlHandler;\r
+ this.initHandler=this.conf.initHandler;\r
+ this.exitHandler=this.conf.exitHandler;\r
+}\r
+\r
+function termDefaultHandler() {\r
+ this.newLine();\r
+ if (this.lineBuffer != '') {\r
+ this.type('You typed: '+this.lineBuffer);\r
+ this.newLine();\r
+ }\r
+ this.prompt();\r
+}\r
+\r
+Terminal.prototype.open=function() {\r
+ if (this.termDivReady()) {\r
+ if (!this.closed) this._makeTerm();\r
+ this.init();\r
+ return true;\r
+ }\r
+ else return false;\r
+}\r
+\r
+Terminal.prototype.close=function() {\r
+ this.lock=true;\r
+ this.cursorOff();\r
+ if (this.exitHandler) this.exitHandler();\r
+ TermGlobals.setVisible(this.termDiv,0);\r
+ this.closed=true;\r
+}\r
+\r
+Terminal.prototype.init=function() {\r
+ // wait for gui\r
+ if (this.guiReady()) {\r
+ this.guiCounter=0;\r
+ // clean up at re-entry\r
+ if (this.closed) {\r
+ this.setInitValues();\r
+ }\r
+ this.clear();\r
+ TermGlobals.setVisible(this.termDiv,1);\r
+ TermGlobals.enableKeyboard(this);\r
+ if (this.initHandler) {\r
+ this.initHandler();\r
+ }\r
+ else {\r
+ this.write(this.conf.greeting);\r
+ this.newLine();\r
+ this.prompt();\r
+ }\r
+ }\r
+ else {\r
+ this.guiCounter++;\r
+ if (this.guiCounter>18000) {\r
+ if (confirm('Terminal:\nYour browser hasn\'t responded for more than 2 minutes.\nRetry?')) this.guiCounter=0\r
+ else return;\r
+ };\r
+ TermGlobals.termToInitialze=this;\r
+ window.setTimeout('TermGlobals.termToInitialze.init()',200);\r
+ }\r
+}\r
+\r
+Terminal.prototype.getRowArray=function(l,v) {\r
+ var a=new Array();\r
+ for (var i=0; i<l; i++) a[i]=v;\r
+ return a;\r
+}\r
+\r
+Terminal.prototype.type=function(text,style) {\r
+ for (var i=0; i<text.length; i++) {\r
+ var ch=text.charCodeAt(i);\r
+ if (!this.isPrintable(ch)) ch=94;\r
+ this.charBuf[this.r][this.c]=ch;\r
+ this.styleBuf[this.r][this.c]=(style)? style:0;\r
+ var last_r=this.r;\r
+ this._incCol();\r
+ if (this.r!=last_r) this.redraw(last_r);\r
+ }\r
+ this.redraw(this.r)\r
+}\r
+\r
+Terminal.prototype.write=function(text,usemore) {\r
+ // write to scroll buffer with markup\r
+ // new line = '%n' prepare any strings or arrys first\r
+ if (typeof text != 'object') {\r
+ if (typeof text!='string') text=''+text;\r
+ if (text.indexOf('\n')>=0) {\r
+ var ta=text.split('\n');\r
+ text=ta.join('%n');\r
+ }\r
+ }\r
+ else {\r
+ if (text.join) text=text.join('%n')\r
+ else text=''+text;\r
+ }\r
+ this._sbInit(usemore);\r
+ var chunks=text.split('%');\r
+ var esc=(text.charAt(0)!='%');\r
+ var style=0;\r
+ for (var i=0; i<chunks.length; i++) {\r
+ if (esc) {\r
+ if (chunks[i].length>0) this._sbType(chunks[i],style)\r
+ else if (i>0) this._sbType('%', style);\r
+ esc=false;\r
+ }\r
+ else {\r
+ var func=chunks[i].charAt(0);\r
+ if ((chunks[i].length==0) && (i>0)) {\r
+ this._sbType("%",style);\r
+ esc=true;\r
+ }\r
+ else if (func=='n') {\r
+ this._sbNewLine();\r
+ if (chunks[i].length>1) this._sbType(chunks[i].substring(1),style);\r
+ }\r
+ else if (func=='+') {\r
+ var opt=chunks[i].charAt(1);\r
+ opt=opt.toLowerCase();\r
+ if (opt=='p') style=0\r
+ else if (opt=='r') style|=1\r
+ else if (opt=='u') style|=2\r
+ else if (opt=='i') style|=4\r
+ else if (opt=='s') style|=8;\r
+ if (chunks[i].length>2) this._sbType(chunks[i].substring(2),style);\r
+ }\r
+ else if (func=='-') {\r
+ var opt=chunks[i].charAt(1);\r
+ opt=opt.toLowerCase();\r
+ if (opt=='p') style|=0\r
+ else if (opt=='r') style&=~1\r
+ else if (opt=='u') style&=~2\r
+ else if (opt=='i') style&=~4\r
+ else if (opt=='s') style&=~8;\r
+ if (chunks[i].length>2) this._sbType(chunks[i].substring(2),style);\r
+ }\r
+ else if ((chunks[i].length>1) && (chunks[i].charAt(0)=='C') && (chunks[i].charAt(1)=='S')) {\r
+ this.clear();\r
+ this._sbInit();\r
+ if (chunks[i].length>2) this._sbType(chunks[i].substring(2),style);\r
+ }\r
+ else {\r
+ if (chunks[i].length>0) this._sbType(chunks[i],style);\r
+ }\r
+ }\r
+ }\r
+ this._sbOut();\r
+}\r
+\r
+Terminal.prototype._sbType=function(text,style) {\r
+ // type to scroll buffer\r
+ var sb=this.scrollBuf;\r
+ for (var i=0; i<text.length; i++) {\r
+ var ch=text.charCodeAt(i);\r
+ if (!this.isPrintable(ch)) ch=94;\r
+ sb.lines[sb.r][sb.c]=ch;\r
+ sb.styles[sb.r][sb.c]=(style)? style:0;\r
+ sb.c++;\r
+ if (sb.c>=this.maxCols) this._sbNewLine();\r
+ }\r
+}\r
+\r
+Terminal.prototype._sbNewLine=function() {\r
+ var sb=this.scrollBuf;\r
+ sb.r++;\r
+ sb.c=0;\r
+ sb.lines[sb.r]=this.getRowArray(this.conf.cols,0);\r
+ sb.styles[sb.r]=this.getRowArray(this.conf.cols,0);\r
+}\r
+\r
+\r
+Terminal.prototype._sbInit=function(usemore) {\r
+ var sb=this.scrollBuf=new Object();\r
+ var sbl=sb.lines=new Array();\r
+ var sbs=sb.styles=new Array();\r
+ sb.more=usemore;\r
+ sb.line=0;\r
+ sb.status=0;\r
+ sb.r=0;\r
+ sb.c=this.c;\r
+ sbl[0]=this.getRowArray(this.conf.cols,0);\r
+ sbs[0]=this.getRowArray(this.conf.cols,0);\r
+ for (var i=0; i<this.c; i++) {\r
+ sbl[0][i]=this.charBuf[this.r][i];\r
+ sbs[0][i]=this.styleBuf[this.r][i];\r
+ }\r
+}\r
+\r
+Terminal.prototype._sbOut=function() {\r
+ var sb=this.scrollBuf;\r
+ var sbl=sb.lines;\r
+ var sbs=sb.styles;\r
+ var tcb=this.charBuf;\r
+ var tsb=this.styleBuf;\r
+ var ml=this.maxLines;\r
+ var buflen=sbl.length;\r
+ if (sb.more) {\r
+ if (sb.status) {\r
+ if (this.inputChar==TermGlobals.lcMoreKeyAbort) {\r
+ this.r=ml-1;\r
+ this.c=0;\r
+ tcb[this.r]=this.getRowArray(this.maxLines,0);\r
+ tsb[this.r]=this.getRowArray(this.maxLines,0);\r
+ this.redraw(this.r);\r
+ this.handler=sb.handler;\r
+ this.charMode=false;\r
+ this.inputChar=0;\r
+ this.scrollBuf=null;\r
+ this.prompt();\r
+ return;\r
+ }\r
+ else if (this.inputChar==TermGlobals.lcMoreKeyContinue) {\r
+ this.clear();\r
+ }\r
+ else {\r
+ return;\r
+ }\r
+ }\r
+ else {\r
+ if (this.r>=ml-1) this.clear();\r
+ }\r
+ }\r
+ if (this.r+buflen-sb.line<=ml) {\r
+ for (var i=sb.line; i<buflen; i++) {\r
+ var r=this.r+i-sb.line;\r
+ tcb[r]=sbl[i];\r
+ tsb[r]=sbs[i];\r
+ this.redraw(r);\r
+ }\r
+ this.r+=sb.r-sb.line;\r
+ this.c=sb.c;\r
+ if (sb.more) {\r
+ if (sb.status) this.handler=sb.handler;\r
+ this.charMode=false;\r
+ this.inputChar=0;\r
+ this.scrollBuf=null;\r
+ this.prompt();\r
+ return;\r
+ }\r
+ }\r
+ else if (sb.more) {\r
+ ml--;\r
+ if (sb.status==0) {\r
+ sb.handler=this.handler;\r
+ this.handler=this._sbOut;\r
+ this.charMode=true;\r
+ sb.status=1;\r
+ }\r
+ if (this.r) {\r
+ var ofs=ml-this.r;\r
+ for (var i=sb.line; i<ofs; i++) {\r
+ var r=this.r+i-sb.line;\r
+ tcb[r]=sbl[i];\r
+ tsb[r]=sbs[i];\r
+ this.redraw(r);\r
+ }\r
+ }\r
+ else {\r
+ var ofs=sb.line+ml;\r
+ for (var i=sb.line; i<ofs; i++) {\r
+ var r=this.r+i-sb.line;\r
+ tcb[r]=sbl[i];\r
+ tsb[r]=sbs[i];\r
+ this.redraw(r);\r
+ }\r
+ }\r
+ sb.line=ofs;\r
+ this.r=ml;\r
+ this.c=0;\r
+ this.type(TermGlobals.lcMorePrompt1, TermGlobals.lcMorePromtp1Style);\r
+ this.type(TermGlobals.lcMorePrompt2, TermGlobals.lcMorePrompt2Style);\r
+ this.lock=false;\r
+ return;\r
+ }\r
+ else if (buflen>=ml) {\r
+ var ofs=buflen-ml;\r
+ for (var i=0; i<ml; i++) {\r
+ var r=ofs+i;\r
+ tcb[i]=sbl[r];\r
+ tsb[i]=sbs[r];\r
+ this.redraw(i);\r
+ }\r
+ this.r=ml-1;\r
+ this.c=sb.c;\r
+ }\r
+ else {\r
+ var dr=ml-buflen;\r
+ var ofs=this.r-dr;\r
+ for (var i=0; i<dr; i++) {\r
+ var r=ofs+i;\r
+ for (var c=0; c<this.maxCols; c++) {\r
+ tcb[i][c]=tcb[r][c];\r
+ tsb[i][c]=tsb[r][c];\r
+ }\r
+ this.redraw(i);\r
+ }\r
+ for (var i=0; i<buflen; i++) {\r
+ var r=dr+i;\r
+ tcb[r]=sbl[i];\r
+ tsb[r]=sbs[i];\r
+ this.redraw(r);\r
+ }\r
+ this.r=ml-1;\r
+ this.c=sb.c;\r
+ }\r
+ this.scrollBuf=null;\r
+}\r
+\r
+// basic console output\r
+\r
+Terminal.prototype.typeAt=function(r,c,text,style) {\r
+ var tr1=this.r;\r
+ var tc1=this.c;\r
+ this.cursorSet(r,c);\r
+ for (var i=0; i<text.length; i++) {\r
+ var ch=text.charCodeAt(i);\r
+ if (!this.isPrintable(ch)) ch=94;\r
+ this.charBuf[this.r][this.c]=ch;\r
+ this.styleBuf[this.r][this.c]=(style)? style:0;\r
+ var last_r=this.r;\r
+ this._incCol();\r
+ if (this.r!=last_r) this.redraw(last_r);\r
+ }\r
+ this.redraw(this.r);\r
+ this.r=tr1;\r
+ this.c=tc1;\r
+}\r
+\r
+Terminal.prototype.statusLine = function(text,style,offset) {\r
+ var ch,r;\r
+ style=((style) && (!isNaN(style)))? parseInt(style)&15:0;\r
+ if ((offset) && (offset>0)) r=this.conf.rows-offset\r
+ else r=this.conf.rows-1;\r
+ for (var i=0; i<this.conf.cols; i++) {\r
+ if (i<text.length) {\r
+ ch=text.charCodeAt(i);\r
+ if (!this.isPrintable(ch)) ch = 94;\r
+ }\r
+ else ch=0;\r
+ this.charBuf[r][i]=ch;\r
+ this.styleBuf[r][i]=style;\r
+ }\r
+ this.redraw(r);\r
+}\r
+\r
+Terminal.prototype.printRowFromString = function(r,text,style) {\r
+ var ch;\r
+ style=((style) && (!isNaN(style)))? parseInt(style)&15:0;\r
+ if ((r>=0) && (r<this.maxLines)) {\r
+ if (typeof text != 'string') text=''+text;\r
+ for (var i=0; i<this.conf.cols; i++) {\r
+ if (i<text.length) {\r
+ ch=text.charCodeAt(i);\r
+ if (!this.isPrintable(ch)) ch = 94;\r
+ }\r
+ else ch=0;\r
+ this.charBuf[r][i]=ch;\r
+ this.styleBuf[r][i]=style;\r
+ }\r
+ this.redraw(r);\r
+ }\r
+}\r
+\r
+Terminal.prototype.setChar=function(ch,r,c,style) {\r
+ this.charBuf[r][c]=ch;\r
+ this.styleBuf[this.r][this.c]=(style)? style:0;\r
+ this.redraw(r);\r
+}\r
+\r
+Terminal.prototype._charOut=function(ch, style) {\r
+ this.charBuf[this.r][this.c]=ch;\r
+ this.styleBuf[this.r][this.c]=(style)? style:0;\r
+ this.redraw(this.r);\r
+ this._incCol();\r
+}\r
+\r
+Terminal.prototype._incCol=function() {\r
+ this.c++;\r
+ if (this.c>=this.maxCols) {\r
+ this.c=0;\r
+ this._incRow();\r
+ }\r
+}\r
+\r
+Terminal.prototype._incRow=function() {\r
+ this.r++;\r
+ if (this.r>=this.maxLines) {\r
+ this._scrollLines(0,this.maxLines);\r
+ this.r=this.maxLines-1;\r
+ }\r
+}\r
+\r
+Terminal.prototype._scrollLines=function(start, end) {\r
+ window.status='Scrolling lines ...';\r
+ start++;\r
+ for (var ri=start; ri<end; ri++) {\r
+ var rt=ri-1;\r
+ this.charBuf[rt]=this.charBuf[ri];\r
+ this.styleBuf[rt]=this.styleBuf[ri];\r
+ }\r
+ // clear last line\r
+ var rt=end-1;\r
+ this.charBuf[rt]=this.getRowArray(this.conf.cols,0);\r
+ this.styleBuf[rt]=this.getRowArray(this.conf.cols,0);\r
+ this.redraw(rt);\r
+ for (var r=end-1; r>=start; r--) this.redraw(r-1);\r
+ window.status='';\r
+}\r
+\r
+Terminal.prototype.newLine=function() {\r
+ this.c=0;\r
+ this._incRow();\r
+}\r
+\r
+Terminal.prototype.clear=function() {\r
+ window.status='Clearing display ...';\r
+ this.cursorOff();\r
+ this.insert=false;\r
+ for (var ri=0; ri<this.maxLines; ri++) {\r
+ this.charBuf[ri]=this.getRowArray(this.conf.cols,0);\r
+ this.styleBuf[ri]=this.getRowArray(this.conf.cols,0);\r
+ this.redraw(ri);\r
+ }\r
+ this.r=0;\r
+ this.c=0;\r
+ window.status='';\r
+}\r
+\r
+Terminal.prototype.reset=function() {\r
+ if (this.lock) return;\r
+ this.lock=true;\r
+ this.rawMode=false;\r
+ this.charMode=false;\r
+ this.maxLines=this.conf.rows;\r
+ this.maxCols=this.conf.cols;\r
+ this.lastLine='';\r
+ this.lineBuffer='';\r
+ this.inputChar=0;\r
+ this.clear();\r
+}\r
+\r
+Terminal.prototype.cursorSet=function(r,c) {\r
+ var crsron=this.cursoractive;\r
+ if (crsron) this.cursorOff();\r
+ this.r=r%this.maxLines;\r
+ this.c=c%this.maxCols;\r
+ this._cursorReset(crsron);\r
+}\r
+\r
+Terminal.prototype._cursorReset=function(crsron) {\r
+ if (crsron) this.cursorOn()\r
+ else {\r
+ this.blinkBuffer=this.styleBuf[this.r][this.c];\r
+ }\r
+}\r
+\r
+Terminal.prototype.cursorOn=function() {\r
+ if (this.blinkTimer) clearTimeout(this.blinkTimer);\r
+ this.blinkBuffer=this.styleBuf[this.r][this.c];\r
+ this._cursorBlink();\r
+ this.cursoractive=true;\r
+}\r
+\r
+Terminal.prototype.cursorOff=function() {\r
+ if (this.blinkTimer) clearTimeout(this.blinkTimer);\r
+ if (this.cursoractive) {\r
+ this.styleBuf[this.r][this.c]=this.blinkBuffer;\r
+ this.redraw(this.r);\r
+ this.cursoractive=false;\r
+ }\r
+}\r
+\r
+Terminal.prototype._cursorBlink=function() {\r
+ if (this.blinkTimer) clearTimeout(this.blinkTimer);\r
+ if (this == TermGlobals.activeTerm) {\r
+ if (this.crsrBlockMode) {\r
+ this.styleBuf[this.r][this.c]=(this.styleBuf[this.r][this.c]&1)?\r
+ this.styleBuf[this.r][this.c]&254:this.styleBuf[this.r][this.c]|1;\r
+ }\r
+ else {\r
+ this.styleBuf[this.r][this.c]=(this.styleBuf[this.r][this.c]&2)?\r
+ this.styleBuf[this.r][this.c]&253:this.styleBuf[this.r][this.c]|2;\r
+ }\r
+ this.redraw(this.r);\r
+ }\r
+ if (this.crsrBlinkMode) this.blinkTimer=setTimeout('TermGlobals.activeTerm._cursorBlink()', this.blinkDelay);\r
+}\r
+\r
+Terminal.prototype.cursorLeft=function() {\r
+ var crsron=this.cursoractive;\r
+ if (crsron) this.cursorOff();\r
+ var r=this.r;\r
+ var c=this.c;\r
+ if (c>0) c--\r
+ else if (r>0) {\r
+ c=this.maxCols-1;\r
+ r--;\r
+ }\r
+ if (this.isPrintable(this.charBuf[r][c])) {\r
+ this.r=r;\r
+ this.c=c;\r
+ }\r
+ this.insert=true;\r
+ this._cursorReset(crsron);\r
+}\r
+\r
+Terminal.prototype.cursorRight=function() {\r
+ var crsron=this.cursoractive;\r
+ if (crsron) this.cursorOff();\r
+ var r=this.r;\r
+ var c=this.c;\r
+ if (c<this.maxCols-1) c++\r
+ else if (r<this.maxLines-1) {\r
+ c=0;\r
+ r++;\r
+ }\r
+ if (!this.isPrintable(this.charBuf[r][c])) {\r
+ this.insert=false;\r
+ }\r
+ if (this.isPrintable(this.charBuf[this.r][this.c])) {\r
+ this.r=r;\r
+ this.c=c;\r
+ }\r
+ this._cursorReset(crsron);\r
+}\r
+\r
+Terminal.prototype.backspace=function() {\r
+ var crsron=this.cursoractive;\r
+ if (crsron) this.cursorOff();\r
+ var r=this.r;\r
+ var c=this.c;\r
+ if (c>0) c--\r
+ else if (r>0) {\r
+ c=this.maxCols-1;\r
+ r--;\r
+ };\r
+ if (this.isPrintable(this.charBuf[r][c])) {\r
+ this._scrollLeft(r, c);\r
+ this.r=r;\r
+ this.c=c;\r
+ }; \r
+ this._cursorReset(crsron);\r
+}\r
+\r
+Terminal.prototype.fwdDelete=function() {\r
+ var crsron=this.cursoractive;\r
+ if (crsron) this.cursorOff();\r
+ if (this.isPrintable(this.charBuf[this.r][this.c])) {\r
+ this._scrollLeft(this.r,this.c);\r
+ if (!this.isPrintable(this.charBuf[this.r][this.c])) this.insert=false;\r
+ }\r
+ this._cursorReset(crsron);\r
+}\r
+\r
+Terminal.prototype.prompt=function() {\r
+ this.lock=true;\r
+ if (this.c>0) this.newLine();\r
+ this.type(this.ps);\r
+ this._charOut(1);\r
+ this.lock=false;\r
+ this.cursorOn();\r
+}\r
+\r
+Terminal.prototype._scrollLeft=function(r,c) {\r
+ var rows=new Array();\r
+ rows[0]=r;\r
+ while (this.isPrintable(this.charBuf[r][c])) {\r
+ var ri=r;\r
+ var ci=c+1;\r
+ if (ci==this.maxCols) {\r
+ if (ri<this.maxLines-1) {\r
+ ci=0;\r
+ ri++;\r
+ rows[rows.length]=ri;\r
+ }\r
+ else {\r
+ break;\r
+ }\r
+ }\r
+ this.charBuf[r][c]=this.charBuf[ri][ci];\r
+ this.styleBuf[r][c]=this.styleBuf[ri][ci];\r
+ c=ci;\r
+ r=ri;\r
+ }\r
+ if (this.charBuf[r][c]!=0) this.charBuf[r][c]=0;\r
+ for (var i=0; i<rows.length; i++) this.redraw(rows[i]);\r
+}\r
+\r
+Terminal.prototype._scrollRight=function(r,c) {\r
+ var rows=new Array();\r
+ var end=this._getLineEnd(r,c);\r
+ var ri=end[0];\r
+ var ci=end[1];\r
+ if ((ci==this.maxCols-1) && (ri==this.maxLines-1)) {\r
+ if (r==0) return;\r
+ this._scrollLines(0,this.maxLines);\r
+ this.r--;\r
+ r--;\r
+ ri--;\r
+ }\r
+ rows[r]=1;\r
+ while (this.isPrintable(this.charBuf[ri][ci])) {\r
+ var rt=ri;\r
+ var ct=ci+1;\r
+ if (ct==this.maxCols) {\r
+ ct=0;\r
+ rt++;\r
+ rows[rt]=1;\r
+ }\r
+ this.charBuf[rt][ct]=this.charBuf[ri][ci];\r
+ this.styleBuf[rt][ct]=this.styleBuf[ri][ci];\r
+ if ((ri==r) && (ci==c)) break;\r
+ ci--;\r
+ if (ci<0) {\r
+ ci=this.maxCols-1;\r
+ ri--;\r
+ rows[ri]=1;\r
+ }\r
+ }\r
+ for (var i=r; i<this.maxLines; i++) {\r
+ if (rows[i]) this.redraw(i);\r
+ }\r
+}\r
+\r
+Terminal.prototype._getLineEnd=function(r,c) {\r
+ if (!this.isPrintable(this.charBuf[r][c])) {\r
+ c--;\r
+ if (c<0) {\r
+ if (r>0) {\r
+ r--;\r
+ c=this.maxCols-1;\r
+ }\r
+ else {\r
+ c=0;\r
+ }\r
+ }\r
+ }\r
+ if (this.isPrintable(this.charBuf[r][c])) {\r
+ while (true) {\r
+ var ri=r;\r
+ var ci=c+1;\r
+ if (ci==this.maxCols) {\r
+ if (ri<this.maxLines-1) {\r
+ ri++;\r
+ ci=0;\r
+ }\r
+ else {\r
+ break;\r
+ }\r
+ }\r
+ if (!this.isPrintable(this.charBuf[ri][ci])) break;\r
+ c=ci;\r
+ r=ri;\r
+ }\r
+ }\r
+ return [r,c];\r
+}\r
+\r
+Terminal.prototype._getLineStart=function(r,c) {\r
+ // not used by now, just in case anyone needs this ...\r
+ var ci, ri;\r
+ if (!this.isPrintable(this.charBuf[r][c])) {\r
+ ci=c-1;\r
+ ri=r;\r
+ if (ci<0) {\r
+ if (ri==0) return [0,0];\r
+ ci=this.maxCols-1;\r
+ ri--;\r
+ }\r
+ if (!this.isPrintable(this.charBuf[ri][ci])) return [r,c]\r
+ else {\r
+ r=ri;\r
+ c=ci;\r
+ }\r
+ }\r
+ while (true) {\r
+ var ri=r;\r
+ var ci=c-1;\r
+ if (ci<0) {\r
+ if (ri==0) break;\r
+ ci=this.maxCols-1;\r
+ ri--;\r
+ }\r
+ if (!this.isPrintable(this.charBuf[ri][ci])) break;;\r
+ r=ri;\r
+ c=ci;\r
+ }\r
+ return [r,c];\r
+}\r
+\r
+Terminal.prototype._getLine=function() {\r
+ var end=this._getLineEnd(this.r,this.c);\r
+ var r=end[0];\r
+ var c=end[1];\r
+ var line=new Array();\r
+ while (this.isPrintable(this.charBuf[r][c])) {\r
+ line[line.length]=String.fromCharCode(this.charBuf[r][c]);\r
+ if (c>0) c--\r
+ else if (r>0) {\r
+ c=this.maxCols-1;\r
+ r--;\r
+ }\r
+ else break;\r
+ }\r
+ line.reverse();\r
+ return line.join('');\r
+}\r
+\r
+Terminal.prototype._clearLine=function() {\r
+ var end=this._getLineEnd(this.r,this.c);\r
+ var r=end[0];\r
+ var c=end[1];\r
+ var line='';\r
+ while (this.isPrintable(this.charBuf[r][c])) {\r
+ this.charBuf[r][c]=0;\r
+ if (c>0) {\r
+ c--;\r
+ }\r
+ else if (r>0) {\r
+ this.redraw(r);\r
+ c=this.maxCols-1;\r
+ r--;\r
+ }\r
+ else break;\r
+ }\r
+ if (r!=end[0]) this.redraw(r);\r
+ c++;\r
+ this.cursorSet(r,c);\r
+ this.insert=false;\r
+}\r
+\r
+Terminal.prototype.isPrintable=function(ch, unicodePage1only) {\r
+ if ((unicodePage1only) && (ch>255)) {\r
+ return ((ch==termKey.EURO) && (this.printEuro))? true:false;\r
+ }\r
+ return (\r
+ ((ch>=32) && (ch!=termKey.DEL)) ||\r
+ ((this.printTab) && (ch==termKey.TAB))\r
+ );\r
+}\r
+\r
+// keyboard focus\r
+\r
+Terminal.prototype.focus=function() {\r
+ TermGlobals.activeTerm=this;\r
+}\r
+\r
+// global store and functions\r
+\r
+var TermGlobals={\r
+ termToInitialze:null,\r
+ activeTerm:null,\r
+ kbdEnabled:false,\r
+ keylock:false,\r
+ lcMorePrompt1: ' -- MORE -- ',\r
+ lcMorePromtp1Style: 1,\r
+ lcMorePrompt2: ' (Type: space to continue, \'q\' to quit)',\r
+ lcMorePrompt2Style: 0,\r
+ lcMoreKeyAbort: 113,\r
+ lcMoreKeyContinue: 32\r
+};\r
+\r
+// keybard focus\r
+\r
+TermGlobals.setFocus=function(termref) {\r
+ TermGlobals.activeTerm=termref;\r
+}\r
+\r
+// text related\r
+\r
+TermGlobals.normalize=function(n,m) {\r
+ var s=''+n;\r
+ while (s.length<m) s='0'+s;\r
+ return s;\r
+}\r
+\r
+TermGlobals.fillLeft=function(t,n) {\r
+ if (typeof t != 'string') t=''+t;\r
+ while (t.length<n) t=' '+t;\r
+ return t;\r
+}\r
+\r
+TermGlobals.center=function(t,l) {\r
+ var s='';\r
+ for (var i=t.length; i<l; i+=2) s+=' ';\r
+ return s+t;\r
+}\r
+\r
+TermGlobals.stringReplace=function(s1,s2,t) {\r
+ var l1=s1.length;\r
+ var l2=s2.length;\r
+ var ofs=t.indexOf(s1);\r
+ while (ofs>=0) {\r
+ t=t.substring(0,ofs)+s2+t.substring(ofs+l1);\r
+ ofs=t.indexOf(s1,ofs+l2);\r
+ }\r
+ return t;\r
+}\r
+\r
+// keyboard\r
+\r
+var termKey= {\r
+ // special key codes\r
+ 'NUL': 0x00,\r
+ 'SOH': 0x01,\r
+ 'STX': 0x02,\r
+ 'ETX': 0x03,\r
+ 'EOT': 0x04,\r
+ 'ENQ': 0x05,\r
+ 'ACK': 0x06,\r
+ 'BEL': 0x07,\r
+ 'BS': 0x08,\r
+ 'HT': 0x09,\r
+ 'TAB': 0x09,\r
+ 'LF': 0x0A,\r
+ 'VT': 0x0B,\r
+ 'FF': 0x0C,\r
+ 'CR': 0x0D,\r
+ 'SO': 0x0E,\r
+ 'SI': 0x0F,\r
+ 'DLE': 0x10,\r
+ 'DC1': 0x11,\r
+ 'DC2': 0x12,\r
+ 'DC3': 0x13,\r
+ 'DC4': 0x14,\r
+ 'NAK': 0x15,\r
+ 'SYN': 0x16,\r
+ 'ETB': 0x17,\r
+ 'CAN': 0x18,\r
+ 'EM': 0x19,\r
+ 'SUB': 0x1A,\r
+ 'ESC': 0x1B,\r
+ 'IS4': 0x1C,\r
+ 'IS3': 0x1D,\r
+ 'IS2': 0x1E,\r
+ 'IS1': 0x1F,\r
+ 'DEL': 0x7F,\r
+ // other specials\r
+ 'EURO': 0x20AC,\r
+ // cursor mapping\r
+ 'LEFT': 0x1C,\r
+ 'RIGHT': 0x1D,\r
+ 'UP': 0x1E,\r
+ 'DOWN': 0x1F\r
+};\r
+\r
+var termDomKeyRef = {\r
+ DOM_VK_LEFT: termKey.LEFT,\r
+ DOM_VK_RIGHT: termKey.RIGHT,\r
+ DOM_VK_UP: termKey.UP,\r
+ DOM_VK_DOWN: termKey.DOWN,\r
+ DOM_VK_BACK_SPACE: termKey.BS,\r
+ DOM_VK_RETURN: termKey.CR,\r
+ DOM_VK_ENTER: termKey.CR,\r
+ DOM_VK_ESCAPE: termKey.ESC,\r
+ DOM_VK_DELETE: termKey.DEL,\r
+ DOM_VK_TAB: termKey.TAB\r
+};\r
+\r
+TermGlobals.enableKeyboard=function(term) {\r
+ if (!this.kbdEnabled) {\r
+ if (document.addEventListener) document.addEventListener("keypress", this.keyHandler, true)\r
+ else {\r
+ if ((self.Event) && (self.Event.KEYPRESS)) document.captureEvents(Event.KEYPRESS);\r
+ document.onkeypress = this.keyHandler;\r
+ }\r
+ window.document.onkeydown=this.keyFix;\r
+ this.kbdEnabled=true;\r
+ }\r
+ this.activeTerm=term;\r
+}\r
+\r
+TermGlobals.keyFix=function(e) {\r
+ var term=TermGlobals.activeTerm;\r
+ if ((TermGlobals.keylock) || (term.lock)) return true;\r
+ if (window.event) {\r
+ var ch=window.event.keyCode;\r
+ if (!e) e=window.event;\r
+ if (e.DOM_VK_UP) {\r
+ for (var i in termDomKeyRef) {\r
+ if ((e[i]) && (ch == e[i])) {\r
+ this.keyHandler({which:termDomKeyRef[i],_remapped:true});\r
+ if (e.preventDefault) e.preventDefault();\r
+ if (e.stopPropagation) e.stopPropagation();\r
+ e.cancleBubble=true;\r
+ return false;\r
+ }\r
+ }\r
+ e.cancleBubble=false;\r
+ return true;\r
+ }\r
+ else {\r
+ // no DOM support\r
+ if ((ch==8) && (!term.isSafari)) TermGlobals.keyHandler({which:termKey.BS,_remapped:true})\r
+ else if (ch==9) TermGlobals.keyHandler({which:termKey.TAB,_remapped:true})\r
+ else if (ch==37) TermGlobals.keyHandler({which:termKey.LEFT,_remapped:true})\r
+ else if (ch==39) TermGlobals.keyHandler({which:termKey.RIGHT,_remapped:true})\r
+ else if (ch==38) TermGlobals.keyHandler({which:termKey.UP,_remapped:true})\r
+ else if (ch==40) TermGlobals.keyHandler({which:termKey.DOWN,_remapped:true})\r
+ else if (ch==127) TermGlobals.keyHandler({which:termKey.DEL,_remapped:true})\r
+ else if ((ch>=57373) && (ch<=57376)) {\r
+ if (ch==57373) TermGlobals.keyHandler({which:termKey.UP,_remapped:true})\r
+ else if (ch==57374) TermGlobals.keyHandler({which:termKey.DOWN,_remapped:true})\r
+ else if (ch==57375) TermGlobals.keyHandler({which:termKey.LEFT,_remapped:true})\r
+ else if (ch==57376) TermGlobals.keyHandler({which:termKey.RIGHT,_remapped:true});\r
+ }\r
+ else {\r
+ e.cancleBubble=false;\r
+ return true;\r
+ }\r
+ if (e.preventDefault) e.preventDefault();\r
+ if (e.stopPropagation) e.stopPropagation();\r
+ e.cancleBubble=true;\r
+ return false;\r
+ }\r
+ }\r
+}\r
+\r
+TermGlobals.keyHandler=function(e) {\r
+ var term=TermGlobals.activeTerm;\r
+ if ((TermGlobals.keylock) || (term.lock)) return true;\r
+ if ((window.event) && (window.event.preventDefault)) window.event.preventDefault()\r
+ else if ((e) && (e.preventDefault)) e.preventDefault();\r
+ if ((window.event) && (window.event.stopPropagation)) window.event.stopPropagation()\r
+ else if ((e) && (e.stopPropagation)) e.stopPropagation();\r
+ var ch;\r
+ var ctrl=false;\r
+ var shft=false;\r
+ var remapped=false;\r
+ if (e) {\r
+ ch=e.which;\r
+ ctrl=(((e.ctrlKey) && (e.altKey)) || (e.modifiers==2));\r
+ shft=((e.shiftKey) || (e.modifiers==4));\r
+ if (e._remapped) {\r
+ remapped=true;\r
+ if (window.event) {\r
+ //ctrl=((ctrl) || (window.event.ctrlKey));\r
+ ctrl=((ctrl) || ((window.event.ctrlKey) && (!window.event.altKey)));\r
+ shft=((shft) || (window.event.shiftKey));\r
+ }\r
+ }\r
+ }\r
+ else if (window.event) {\r
+ ch=window.event.keyCode;\r
+ //ctrl=(window.event.ctrlKey);\r
+ ctrl=((window.event.ctrlKey) && (!window.event.altKey)); // allow alt gr == ctrl alts\r
+ shft=(window.event.shiftKey);\r
+ }\r
+ else {\r
+ return true;\r
+ }\r
+ if ((ch=='') && (remapped==false)) {\r
+ // map specials\r
+ if (e==null) e=window.event;\r
+ if ((e.charCode==0) && (e.keyCode)) {\r
+ if (e.DOM_VK_UP) {\r
+ for (var i in termDomKeyRef) {\r
+ if ((e[i]) && (e.keyCode == e[i])) {\r
+ ch=termDomKeyRef[i];\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ else {\r
+ // NS4\r
+ if (e.keyCode==28) ch=termKey.LEFT\r
+ else if (e.keyCode==29) ch=termKey.RIGHT\r
+ else if (e.keyCode==30) ch=termKey.UP\r
+ else if (e.keyCode==31) ch=termKey.DOWN\r
+ // Mozilla alike but no DOM support\r
+ else if (e.keyCode==37) ch=termKey.LEFT\r
+ else if (e.keyCode==39) ch=termKey.RIGHT\r
+ else if (e.keyCode==38) ch=termKey.UP\r
+ else if (e.keyCode==40) ch=termKey.DOWN\r
+ // just to have the TAB mapping here too\r
+ else if (e.keyCode==9) ch=termKey.TAB;\r
+ }\r
+ }\r
+ }\r
+ // key actions\r
+ if (term.charMode) {\r
+ term.insert=false;\r
+ term.inputChar=ch;\r
+ term.lineBuffer='';\r
+ term.handler();\r
+ if ((ch<=32) && (window.event)) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ if (!ctrl) {\r
+ // special keys\r
+ if (ch==termKey.CR) {\r
+ term.lock=true;\r
+ term.cursorOff();\r
+ term.insert=false;\r
+ if (term.rawMode) {\r
+ term.lineBuffer=term.lastLine;\r
+ }\r
+ else {\r
+ term.lineBuffer=term._getLine();\r
+ if (\r
+ (term.lineBuffer!='') && ((!term.historyUnique) ||\r
+ (term.history.length==0) ||\r
+ (term.lineBuffer!=term.history[term.history.length-1]))\r
+ ) {\r
+ term.history[term.history.length]=term.lineBuffer;\r
+ }\r
+ term.histPtr=term.history.length;\r
+ }\r
+ term.lastLine='';\r
+ term.inputChar=0;\r
+ term.handler();\r
+ if (window.event) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ else if (ch==termKey.ESC) {\r
+ if (term.conf.closeOnESC) term.close();\r
+ if (window.event) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ if ((ch<32) && (term.rawMode)) {\r
+ if (window.event) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ else {\r
+ if (ch==termKey.LEFT) {\r
+ term.cursorLeft();\r
+ if (window.event) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ else if (ch==termKey.RIGHT) {\r
+ term.cursorRight();\r
+ if (window.event) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ else if (ch==termKey.UP) {\r
+ term.cursorOff();\r
+ if (term.histPtr==term.history.length) term.lastLine=term._getLine();\r
+ term._clearLine();\r
+ if ((term.history.length) && (term.histPtr>=0)) {\r
+ if (term.histPtr>0) term.histPtr--;\r
+ term.type(term.history[term.histPtr]);\r
+ }\r
+ else if (term.lastLine) term.type(term.lastLine);\r
+ term.cursorOn();\r
+ if (window.event) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ else if (ch==termKey.DOWN) {\r
+ term.cursorOff();\r
+ if (term.histPtr==term.history.length) term.lastLine=term._getLine();\r
+ term._clearLine();\r
+ if ((term.history.length) && (term.histPtr<=term.history.length)) {\r
+ if (term.histPtr<term.history.length) term.histPtr++;\r
+ if (term.histPtr<term.history.length) term.type(term.history[term.histPtr])\r
+ else if (term.lastLine) term.type(term.lastLine);\r
+ }\r
+ else if (term.lastLine) term.type(term.lastLine);\r
+ term.cursorOn();\r
+ if (window.event) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ else if (ch==termKey.BS) {\r
+ term.backspace();\r
+ if (window.event) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ else if (ch==termKey.DEL) {\r
+ if (term.DELisBS) term.backspace()\r
+ else term.fwdDelete();\r
+ if (window.event) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ }\r
+ }\r
+ if (term.rawMode) {\r
+ if (term.isPrintable(ch)) {\r
+ term.lastLine+=String.fromCharCode(ch);\r
+ }\r
+ if ((ch==32) && (window.event)) window.event.cancleBubble=true\r
+ else if ((window.opera) && (window.event)) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ else {\r
+ if ((term.conf.catchCtrlH) && ((ch==termKey.BS) || ((ctrl) && (ch==72)))) {\r
+ // catch ^H\r
+ term.backspace();\r
+ if (window.event) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ else if ((term.ctrlHandler) && ((ch<32) || ((ctrl) && (term.isPrintable(ch,true))))) {\r
+ if (((ch>=65) && (ch<=96)) || (ch==63)) {\r
+ // remap canonical\r
+ if (ch==63) ch=31\r
+ else ch-=64;\r
+ }\r
+ term.inputChar=ch;\r
+ term.ctrlHandler();\r
+ if (window.event) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ else if ((ctrl) || (!term.isPrintable(ch,true))) {\r
+ if (window.event) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ else if (term.isPrintable(ch,true)) {\r
+ if (term.blinkTimer) clearTimeout(term.blinkTimer);\r
+ if (term.insert) {\r
+ term.cursorOff();\r
+ term._scrollRight(term.r,term.c);\r
+ }\r
+ term._charOut(ch);\r
+ term.cursorOn();\r
+ if ((ch==32) && (window.event)) window.event.cancleBubble=true\r
+ else if ((window.opera) && (window.event)) window.event.cancleBubble=true;\r
+ return false;\r
+ }\r
+ }\r
+ return true;\r
+}\r
+\r
+// term gui\r
+\r
+TermGlobals.hasSubDivs=false;\r
+TermGlobals.hasLayers=false;\r
+TermGlobals.termStringStart='';\r
+TermGlobals.termStringEnd='';\r
+\r
+TermGlobals.termSpecials=new Array();\r
+TermGlobals.termSpecials[0]=' ';\r
+TermGlobals.termSpecials[1]=' ';\r
+TermGlobals.termSpecials[9]=' ';\r
+TermGlobals.termSpecials[32]=' ';\r
+TermGlobals.termSpecials[34]='"';\r
+TermGlobals.termSpecials[38]='&';\r
+TermGlobals.termSpecials[60]='<';\r
+TermGlobals.termSpecials[62]='>';\r
+TermGlobals.termSpecials[127]='◊';\r
+TermGlobals.termSpecials[0x20AC]='€';\r
+\r
+TermGlobals.termStyles=new Array(1,2,4,8);\r
+TermGlobals.termStyleOpen=new Array();\r
+TermGlobals.termStyleClose=new Array();\r
+TermGlobals.termStyleOpen[1]='<span class="termReverse">';\r
+TermGlobals.termStyleClose[1]='<\/span>';\r
+TermGlobals.termStyleOpen[2]='<u>';\r
+TermGlobals.termStyleClose[2]='<\/u>';\r
+TermGlobals.termStyleOpen[4]='<i>';\r
+TermGlobals.termStyleClose[4]='<\/i>';\r
+TermGlobals.termStyleOpen[8]='<strike>';\r
+TermGlobals.termStyleClose[8]='<\/strike>';\r
+\r
+Terminal.prototype._makeTerm=function(rebuild) {\r
+ window.status='Building terminal ...';\r
+ TermGlobals.hasLayers=(document.layers)? true:false;\r
+ TermGlobals.hasSubDivs=(navigator.userAgent.indexOf('Gecko')<0);\r
+ var divPrefix=this.termDiv+'_r';\r
+ var s='';\r
+ s+='<table border="0" cellspacing="0" cellpadding="'+this.conf.frameWidth+'">\n';\r
+ s+='<tr><td bgcolor="'+this.conf.frameColor+'"><table border="0" cellspacing="0" cellpadding="2"><tr><td bgcolor="'+this.conf.bgColor+'"><table border="0" cellspacing="0" cellpadding="0">\n';\r
+ var rstr='';\r
+ for (var c=0; c<this.conf.cols; c++) rstr+=' ';\r
+ for (var r=0; r<this.conf.rows; r++) {\r
+ var termid=((TermGlobals.hasLayers) || (TermGlobals.hasSubDivs))? '' : ' id="'+divPrefix+r+'"';\r
+ s+='<tr><td nowrap height="'+this.conf.rowHeight+'"'+termid+' class="'+this.conf.fontClass+'">'+rstr+'<\/td><\/tr>\n';\r
+ }\r
+ s+='<\/table><\/td><\/tr>\n';\r
+ s+='<\/table><\/td><\/tr>\n';\r
+ s+='<\/table>\n';\r
+ var termOffset=2+this.conf.frameWidth;\r
+ if (TermGlobals.hasLayers) {\r
+ for (var r=0; r<this.conf.rows; r++) {\r
+ s+='<layer name="'+divPrefix+r+'" top="'+(termOffset+r*this.conf.rowHeight)+'" left="'+termOffset+'" class="'+this.conf.fontClass+'"><\/layer>\n';\r
+ }\r
+ this.ns4ParentDoc=document.layers[this.termDiv].document;\r
+ TermGlobals.termStringStart='<table border="0" cellspacing="0" cellpadding="0"><tr><td nowrap height="'+this.conf.rowHeight+'" class="'+this.conf.fontClass+'">';\r
+ TermGlobals.termStringEnd='<\/td><\/tr><\/table>';\r
+ }\r
+ else if (TermGlobals.hasSubDivs) {\r
+ for (var r=0; r<this.conf.rows; r++) {\r
+ s+='<div id="'+divPrefix+r+'" style="position:absolute; top:'+(termOffset+r*this.conf.rowHeight)+'px; left: '+termOffset+'px;" class="'+this.conf.fontClass+'"><\/div>\n';\r
+ }\r
+ TermGlobals.termStringStart='<table border="0" cellspacing="0" cellpadding="0"><tr><td nowrap height="'+this.conf.rowHeight+'" class="'+this.conf.fontClass+'">';\r
+ TermGlobals.termStringEnd='<\/td><\/tr><\/table>';\r
+ }\r
+ TermGlobals.writeElement(this.termDiv,s);\r
+ if (!rebuild) {\r
+ TermGlobals.setElementXY(this.termDiv,this.conf.x,this.conf.y);\r
+ TermGlobals.setVisible(this.termDiv,1);\r
+ }\r
+ window.status='';\r
+}\r
+\r
+Terminal.prototype.rebuild=function() {\r
+ // check for bounds and array lengths\r
+ var rl=this.conf.rows;\r
+ var cl=this.conf.cols;\r
+ for (var r=0; r<rl; r++) {\r
+ var cbr=this.charBuf[r];\r
+ if (!cbr) {\r
+ this.charBuf[r]=this.getRowArray(cl,0);\r
+ this.styleBuf[r]=this.getRowArray(cl,0);\r
+ }\r
+ else if (cbr.length<cl) {\r
+ for (var c=cbr.length; c<cl; c++) {\r
+ this.charBuf[r][c]=0;\r
+ this.styleBuf[r][c]=0;\r
+ }\r
+ }\r
+ }\r
+ var resetcrsr=false;\r
+ if (this.r>=rl) {\r
+ r=rl-1;\r
+ resetcrsr=true;\r
+ }\r
+ if (this.c>=cl) {\r
+ c=cl-1;\r
+ resetcrsr=true;\r
+ }\r
+ if ((resetcrsr) && (this.cursoractive)) this.cursorOn();\r
+ // and actually rebuild\r
+ this._makeTerm(true);\r
+ for (var r=0; r<rl; r++) {\r
+ this.redraw(r);\r
+ }\r
+}\r
+\r
+Terminal.prototype.moveTo=function(x,y) {\r
+ TermGlobals.setElementXY(this.termDiv,x,y);\r
+}\r
+\r
+Terminal.prototype.resizeTo=function(x,y) {\r
+ if (this.termDivReady()) {\r
+ x=parseInt(x,10);\r
+ y=parseInt(y,10);\r
+ if ((isNaN(x)) || (isNaN(y)) || (x<4) || (y<2)) return false;\r
+ this.maxCols=this.conf.cols=x;\r
+ this.maxLines=this.conf.rows=y;\r
+ this._makeTerm();\r
+ this.clear();\r
+ return true;\r
+ }\r
+ else return false;\r
+}\r
+\r
+Terminal.prototype.redraw=function(r) {\r
+ var s=TermGlobals.termStringStart;\r
+ var curStyle=0;\r
+ var tstls=TermGlobals.termStyles;\r
+ var tscls=TermGlobals.termStyleClose;\r
+ var tsopn=TermGlobals.termStyleOpen;\r
+ var tspcl=TermGlobals.termSpecials;\r
+ var t_cb=this.charBuf;\r
+ var t_sb=this.styleBuf;\r
+ for (var i=0; i<this.conf.cols; i++) {\r
+ var c=t_cb[r][i];\r
+ var cs=t_sb[r][i];\r
+ if (cs!=curStyle) {\r
+ if (curStyle) {\r
+ for (var k=tstls.length-1; k>=0; k--) {\r
+ var st=tstls[k];\r
+ if (curStyle&st) s+=tscls[st];\r
+ }\r
+ }\r
+ curStyle=cs;\r
+ for (var k=0; k<tstls.length; k++) {\r
+ var st=tstls[k];\r
+ if (curStyle&st) s+=tsopn[st];\r
+ }\r
+ }\r
+ s+= (tspcl[c])? tspcl[c] : String.fromCharCode(c);\r
+ }\r
+ if (curStyle>0) {\r
+ for (var k=tstls.length-1; k>=0; k--) {\r
+ var st=tstls[k];\r
+ if (curStyle&st) s+=tscls[st];\r
+ }\r
+ }\r
+ s+=TermGlobals.termStringEnd;\r
+ TermGlobals.writeElement(this.termDiv+'_r'+r,s,this.ns4ParentDoc);\r
+}\r
+\r
+Terminal.prototype.guiReady=function() {\r
+ ready=true;\r
+ if (TermGlobals.guiElementsReady(this.termDiv, self.document)) {\r
+ for (var r=0; r<this.conf.rows; r++) {\r
+ if (TermGlobals.guiElementsReady(this.termDiv+'_r'+r,this.ns4ParentDoc)==false) {\r
+ ready=false;\r
+ break;\r
+ }\r
+ }\r
+ }\r
+ else ready=false;\r
+ return ready;\r
+}\r
+\r
+Terminal.prototype.termDivReady=function() {\r
+ if (document.layers) {\r
+ return (document.layers[this.termDiv])? true:false;\r
+ }\r
+ else if (document.getElementById) {\r
+ return (document.getElementById(this.termDiv))? true:false;\r
+ }\r
+ else if (document.all) {\r
+ return (document.all[this.termDiv])? true:false;\r
+ }\r
+ else {\r
+ return false;\r
+ }\r
+}\r
+\r
+Terminal.prototype.getDimensions=function() {\r
+ var w=0;\r
+ var h=0;\r
+ var d=this.termDiv;\r
+ if (document.layers) {\r
+ if (document.layers[d]) {\r
+ w=document.layers[d].clip.right;\r
+ h=document.layers[d].clip.bottom;\r
+ }\r
+ }\r
+ else if (document.getElementById) {\r
+ var obj=document.getElementById(d);\r
+ if ((obj) && (obj.firstChild)) {\r
+ w=parseInt(obj.firstChild.offsetWidth,10);\r
+ h=parseInt(obj.firstChild.offsetHeight,10);\r
+ }\r
+ else if ((obj) && (obj.children) && (obj.children[0])) {\r
+ w=parseInt(obj.children[0].offsetWidth,10);\r
+ h=parseInt(obj.children[0].offsetHeight,10);\r
+ }\r
+ }\r
+ else if (document.all) {\r
+ var obj=document.all[d];\r
+ if ((obj) && (obj.children) && (obj.children[0])) {\r
+ w=parseInt(obj.children[0].offsetWidth,10);\r
+ h=parseInt(obj.children[0].offsetHeight,10);\r
+ }\r
+ }\r
+ return { width: w, height: h };\r
+}\r
+\r
+// basic dynamics\r
+\r
+TermGlobals.writeElement=function(e,t,d) {\r
+ if (document.layers) {\r
+ var doc=(d)? d : self.document;\r
+ doc.layers[e].document.open();\r
+ doc.layers[e].document.write(t);\r
+ doc.layers[e].document.close();\r
+ }\r
+ else if (document.getElementById) {\r
+ var obj=document.getElementById(e);\r
+ obj.innerHTML=t;\r
+ }\r
+ else if (document.all) {\r
+ document.all[e].innerHTML=t;\r
+ }\r
+}\r
+\r
+TermGlobals.setElementXY=function(d,x,y) {\r
+ if (document.layers) {\r
+ document.layers[d].moveTo(x,y);\r
+ }\r
+ else if (document.getElementById) {\r
+ var obj=document.getElementById(d);\r
+ obj.style.left=x+'px';\r
+ obj.style.top=y+'px';\r
+ }\r
+ else if (document.all) {\r
+ document.all[d].style.left=x+'px';\r
+ document.all[d].style.top=y+'px';\r
+ }\r
+}\r
+\r
+TermGlobals.setVisible=function(d,v) {\r
+ if (document.layers) {\r
+ document.layers[d].visibility= (v)? 'show':'hide';\r
+ }\r
+ else if (document.getElementById) {\r
+ var obj=document.getElementById(d);\r
+ obj.style.visibility= (v)? 'visible':'hidden';\r
+ }\r
+ else if (document.all) {\r
+ document.all[d].style.visibility= (v)? 'visible':'hidden';\r
+ }\r
+}\r
+\r
+TermGlobals.setDisplay=function(d,v) {\r
+ if (document.getElementById) {\r
+ var obj=document.getElementById(d);\r
+ obj.style.display=v;\r
+ }\r
+ else if (document.all) {\r
+ document.all[d].style.display=v;\r
+ }\r
+}\r
+\r
+TermGlobals.guiElementsReady=function(e,d) {\r
+ if (document.layers) {\r
+ var doc=(d)? d : self.document;\r
+ return ((doc) && (doc.layers[e]))? true:false;\r
+ }\r
+ else if (document.getElementById) {\r
+ return (document.getElementById(e))? true:false;\r
+ }\r
+ else if (document.all) {\r
+ return (document.all[e])? true:false;\r
+ }\r
+ else return false;\r
+}\r
+\r
+\r
+// constructor mods (ie4 fix)\r
+\r
+var termString_keyref;\r
+var termString_keycoderef;\r
+\r
+function termString_makeKeyref() {\r
+ termString_keyref= new Array();\r
+ termString_keycoderef= new Array();\r
+ var hex= new Array('A','B','C','D','E','F');\r
+ for (var i=0; i<=15; i++) {\r
+ var high=(i<10)? i:hex[i-10];\r
+ for (var k=0; k<=15; k++) {\r
+ var low=(k<10)? k:hex[k-10];\r
+ var cc=i*16+k;\r
+ if (cc>=32) {\r
+ var cs=unescape("%"+high+low);\r
+ termString_keyref[cc]=cs;\r
+ termString_keycoderef[cs]=cc;\r
+ }\r
+ }\r
+ }\r
+}\r
+\r
+if (!String.fromCharCode) {\r
+ termString_makeKeyref();\r
+ String.fromCharCode=function(cc) {\r
+ return (cc!=null)? termString_keyref[cc] : '';\r
+ };\r
+}\r
+if (!String.prototype.charCodeAt) {\r
+ if (!termString_keycoderef) termString_makeKeyref();\r
+ String.prototype.charCodeAt=function(n) {\r
+ cs=this.charAt(n);\r
+ return (termString_keycoderef[cs])? termString_keycoderef[cs] : 0;\r
+ };\r
+}\r
+\r
+// eof
\ No newline at end of file
--- /dev/null
+/*\r
+ termlib_parser.js v.1.0\r
+ command line parser for termlib.js\r
+ (c) Norbert Landsteiner 2005\r
+ mass:werk - media environments\r
+ <http://www.masswerk.at>\r
+\r
+ you are free to use this parser under the "termlib.js" license.\r
+\r
+ usage: call "parseLine(this)" from your Terminal handler\r
+ parsed args in this.argv\r
+ quoting levels per arg in this.argQL (value: quote char)\r
+ this.argc: pointer to this.argv and this.argQL (used by parserGetopt)\r
+ call parseretopt(this, "<options>") from your handler to get opts\r
+ (returns an object with properties for every option flag. any float\r
+ values are stored in Object.<flag>.value; illegal opts in array\r
+ Object.illegals)\r
+\r
+ configuration: you may want to overide the follow objects (or add properties):\r
+ parserWhiteSpace: chars to be parsed as whitespace\r
+ parserQuoteChars: chars to be parsed as quotes\r
+ parserSingleEscapes: chars to escape a quote or escape expression\r
+ parserOptionChars: chars that start an option\r
+ parserEscapeExpressions: chars that start escape expressions\r
+*/\r
+\r
+// chars to be parsed as white space\r
+var parserWhiteSpace = {\r
+ ' ': true,\r
+ '\t': true\r
+}\r
+\r
+// chars to be parsed as quotes\r
+var parserQuoteChars = {\r
+ '"': true,\r
+ "'": true,\r
+ '`': true\r
+};\r
+\r
+// chars to be parsed as escape char\r
+var parserSingleEscapes = {\r
+ '\\': true\r
+};\r
+\r
+// chars that mark the start of an option-expression\r
+// for use with parserGetopt\r
+var parserOptionChars = {\r
+ '-': true\r
+}\r
+\r
+// chars that start escape expressions (value = handler)\r
+// plugin handlers for ascii escapes or variable substitution\r
+var parserEscapeExpressions = {\r
+ '%': parserHexExpression\r
+}\r
+\r
+function parserHexExpression(termref, pointer, echar, quotelevel) {\r
+ /* example for parserEscapeExpressions\r
+ params:\r
+ termref: ref to Terminal instance\r
+ pointer: position in termref.lineBuffer (echar)\r
+ echar: escape character found\r
+ quotelevel: current quoting level (quote char or empty)\r
+ char under pointer will be ignored\r
+ the return value is added to the current argument\r
+ */\r
+ // convert hex values to chars (e.g. %20 => <SPACE>)\r
+ if (termref.lineBuffer.length > pointer+2) {\r
+ // get next 2 chars\r
+ var hi = termref.lineBuffer.charAt(pointer+1);\r
+ var lo = termref.lineBuffer.charAt(pointer+2);\r
+ lo = lo.toUpperCase();\r
+ hi = hi.toUpperCase();\r
+ // check for valid hex digits\r
+ if ((((hi>='0') && (hi<='9')) || ((hi>='A') && ((hi<='F')))) &&\r
+ (((lo>='0') && (lo<='9')) || ((lo>='A') && ((lo<='F'))))) {\r
+ // next 2 chars are valid hex, so strip them from lineBuffer\r
+ parserEscExprStrip(termref, pointer+1, pointer+3);\r
+ // and return the char\r
+ return String.fromCharCode(parseInt(hi+lo, 16));\r
+ }\r
+ }\r
+ // if not handled return the escape character (=> no conversion)\r
+ return echar;\r
+}\r
+\r
+function parserEscExprStrip(termref, from, to) {\r
+ // strip characters from termref.lineBuffer (for use with escape expressions)\r
+ termref.lineBuffer =\r
+ termref.lineBuffer.substring(0, from) +\r
+ termref.lineBuffer.substring(to);\r
+}\r
+\r
+function parserGetopt(termref, optsstring) {\r
+ // scans argv form current position of argc for opts\r
+ // arguments in argv must not be quoted\r
+ // returns an object with a property for every option flag found\r
+ // option values (absolute floats) are stored in Object.<opt>.value (default -1)\r
+ // the property "illegals" contains an array of all flags found but not in optstring\r
+ // argc is set to first argument that is not an option\r
+ var opts = { 'illegals':[] };\r
+ while ((termref.argc < termref.argv.length) && (termref.argQL[termref.argc]=='')) {\r
+ var a = termref.argv[termref.argc];\r
+ if ((a.length>0) && (parserOptionChars[a.charAt(0)])) {\r
+ var i = 1;\r
+ while (i<a.length) {\r
+ var c=a.charAt(i);\r
+ var v = '';\r
+ while (i<a.length-1) {\r
+ var nc=a.charAt(i+1);\r
+ if ((nc=='.') || ((nc>='0') && (nc<='9'))) {\r
+ v += nc;\r
+ i++;\r
+ }\r
+ else break;\r
+ }\r
+ if (optsstring.indexOf(c)>=0) {\r
+ opts[c] = (v == '')? {value:-1} : (isNaN(v))? {value:0} : {value:parseFloat(v)};\r
+ }\r
+ else {\r
+ opts.illegals[opts.illegals.length]=c;\r
+ }\r
+ i++;\r
+ }\r
+ termref.argc++;\r
+ }\r
+ else break;\r
+ }\r
+ return opts;\r
+}\r
+\r
+function parseLine(termref) {\r
+ // stand-alone parser, takes a Terminal instance as argument\r
+ // parses the command line and stores results as instance properties\r
+ // argv: list of parsed arguments\r
+ // argQL: argument's quoting level (<empty> or quote character)\r
+ // argc: cursur for argv, set initinally to zero (0)\r
+ // open quote strings are not an error but automatically closed.\r
+ var argv = ['']; // arguments vector\r
+ var argQL = ['']; // quoting level\r
+ var argc = 0; // arguments cursor\r
+ var escape = false ; // escape flag\r
+ for (var i=0; i<termref.lineBuffer.length; i++) {\r
+ var ch= termref.lineBuffer.charAt(i);\r
+ if (escape) {\r
+ argv[argc] += ch;\r
+ escape = false;\r
+ }\r
+ else if (parserEscapeExpressions[ch]) {\r
+ var v = parserEscapeExpressions[ch](termref, i, ch, argQL[argc]);\r
+ if (typeof v != 'undefined') argv[argc] += v;\r
+ }\r
+ else if (parserQuoteChars[ch]) {\r
+ if (argQL[argc]) {\r
+ if (argQL[argc] == ch) {\r
+ argc ++;\r
+ argv[argc] = argQL[argc] = '';\r
+ }\r
+ else {\r
+ argv[argc] += ch;\r
+ }\r
+ }\r
+ else {\r
+ if (argv[argc] != '') {\r
+ argc ++;\r
+ argv[argc] = '';\r
+ argQL[argc] = ch;\r
+ }\r
+ else {\r
+ argQL[argc] = ch;\r
+ }\r
+ }\r
+ }\r
+ else if (parserWhiteSpace[ch]) {\r
+ if (argQL[argc]) {\r
+ argv[argc] += ch;\r
+ }\r
+ else if (argv[argc] != '') {\r
+ argc++;\r
+ argv[argc] = argQL[argc] = '';\r
+ }\r
+ }\r
+ else if (parserSingleEscapes[ch]) {\r
+ escape = true;\r
+ }\r
+ else {\r
+ argv[argc] += ch;\r
+ }\r
+ }\r
+ if ((argv[argc] == '') && (!argQL[argc])) {\r
+ argv.length--;\r
+ argQL.length--;\r
+ }\r
+ termref.argv = argv;\r
+ termref.argQL = argQL;\r
+ termref.argc = 0;\r
+}\r
+\r
+// eof
\ No newline at end of file
--- /dev/null
+Web interface for Factor to Javascript compiler
--- /dev/null
+Slava Pestov
--- /dev/null
+! Copyright (C) 2005, 2007 Slava Pestov.
+! See http://factorcode.org/license.txt for BSD license.
+USING: kernel furnace furnace.validator http.server.responders
+ help help.topics html splitting sequences words strings
+ quotations macros vocabs tools.browser combinators
+ arrays io.files ;
+IN: webapps.help
+
+! : string>topic ( string -- topic )
+ ! " " split dup length 1 = [ first ] when ;
+
+: show-help ( topic -- )
+ serving-html
+ dup article-title [
+ [ help ] with-html-stream
+ ] simple-html-document ;
+
+\ show-help {
+ { "topic" }
+} define-action
+\ show-help { { "topic" "handbook" } } default-values
+
+M: link browser-link-href
+ link-name
+ dup word? over f eq? or [
+ browser-link-href
+ ] [
+ dup array? [ " " join ] when
+ [ show-help ] curry quot-link
+ ] if ;
+
+: show-word ( word vocab -- )
+ lookup show-help ;
+
+\ show-word {
+ { "word" }
+ { "vocab" }
+} define-action
+\ show-word { { "word" "call" } { "vocab" "kernel" } } default-values
+
+M: f browser-link-href
+ drop \ f browser-link-href ;
+
+M: word browser-link-href
+ dup word-name swap word-vocabulary
+ [ show-word ] 2curry quot-link ;
+
+: show-vocab ( vocab -- )
+ f >vocab-link show-help ;
+
+\ show-vocab {
+ { "vocab" }
+} define-action
+
+\ show-vocab { { "vocab" "kernel" } } default-values
+
+M: vocab-spec browser-link-href
+ vocab-name [ show-vocab ] curry quot-link ;
+
+: show-vocabs-tagged ( tag -- )
+ <vocab-tag> show-help ;
+
+\ show-vocabs-tagged {
+ { "tag" }
+} define-action
+
+M: vocab-tag browser-link-href
+ vocab-tag-name [ show-vocabs-tagged ] curry quot-link ;
+
+: show-vocabs-by ( author -- )
+ <vocab-author> show-help ;
+
+\ show-vocabs-by {
+ { "author" }
+} define-action
+
+M: vocab-author browser-link-href
+ vocab-author-name [ show-vocabs-by ] curry quot-link ;
+
+"help" "show-help" "extra/webapps/help" web-app
+
+! Hard-coding for factorcode.org
+PREDICATE: pathname resource-pathname
+ pathname-string "resource:" head? ;
+
+M: resource-pathname browser-link-href
+ pathname-string
+ "resource:" ?head drop
+ "/responder/source/" swap append ;
--- /dev/null
+Chris Double
--- /dev/null
+! cont-number-guess
+!
+! Copyright (C) 2004 Chris Double.
+!
+! Redistribution and use in source and binary forms, with or without
+! modification, are permitted provided that the following conditions are met:
+!
+! 1. Redistributions of source code must retain the above copyright notice,
+! this list of conditions and the following disclaimer.
+!
+! 2. Redistributions in binary form must reproduce the above copyright notice,
+! this list of conditions and the following disclaimer in the documentation
+! and/or other materials provided with the distribution.
+!
+! THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+! INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
+! FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+! DEVELOPERS AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+! SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+! PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+! OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+! WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+! OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+! ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+!
+! This example modifies the console based 'numbers-game' example
+! in a very minimal way to demonstrate conversion of a console
+! program to a web based application.
+!
+! All that was required was changing the input and output functions
+! to use HTML. The remaining code was untouched.
+!
+! The result is not that pretty but it shows the basic idea.
+USING: kernel math parser html html.elements io namespaces
+math.parser random webapps.continuation ;
+
+IN: webapps.numbers
+
+: web-print ( str -- )
+ #! Display the string in a web page.
+ [
+ swap dup
+ <html>
+ <head> <title> write </title> </head>
+ <body>
+ <p> write </p>
+ <p> <a =href a> "Press to continue" write </a> </p>
+ </body>
+ </html>
+ ] show 2drop ;
+
+: read-number ( -- )
+ [
+ <html>
+ <head> <title> "Enter a number" write </title> </head>
+ <body>
+ <form =action "post" =method form>
+ <p>
+ "Enter a number:" write
+ <input "text" =type "num" =name "20" =size input/>
+ <input "submit" =type "Press to continue" =value input/>
+ </p>
+ </form>
+ </body>
+ </html>
+ ] show [ "num" get ] bind string>number ;
+
+: guess-banner
+ "I'm thinking of a number between 0 and 100." web-print ;
+: guess-prompt ;
+: too-high "Too high" web-print ;
+: too-low "Too low" web-print ;
+: correct "Correct - you win!" web-print ;
+: inexact-guess ( actual guess -- )
+ < [ too-high ] [ too-low ] if ;
+
+: judge-guess ( actual guess -- ? )
+ 2dup = [
+ 2drop correct f
+ ] [
+ inexact-guess t
+ ] if ;
+
+: number-to-guess ( -- n ) 100 random ;
+
+: numbers-game-loop ( actual -- )
+ dup guess-prompt read-number judge-guess [
+ numbers-game-loop
+ ] [
+ drop
+ ] if ;
+
+: numbers-game number-to-guess numbers-game-loop ;
+
+"numbers-game" [ numbers-game ] install-cont-responder
--- /dev/null
+<% USING: io math math.parser namespaces furnace ; %>
+
+<h1>Annotate</h1>
+
+<form method="POST" action="/responder/pastebin/annotate-paste">
+
+<table>
+
+<tr>
+<th align="right">Summary:</th>
+<td><input type="TEXT" name="summary" value="<% "summary" render %>" /></td>
+<td align="left" class="error"><% "summary" "*Required" render-error %></td>
+</tr>
+
+<tr>
+<th align="right">Your name:</th>
+<td><input type="TEXT" name="author" value="<% "author" render %>" /></td>
+<td class="error"><% "author" "*Required" render-error %></td>
+</tr>
+
+<tr>
+<th align="right">File type:</th>
+<td><% "modes" render-template %></td>
+</tr>
+
+<!--
+<tr>
+<th align="right">Channel:</th>
+<td><input type="TEXT" name="channel" value="#concatenative" /></td>
+</tr>
+-->
+
+<tr>
+<td></td>
+<td colspan="2" class="error" align="left"><% "contents" "*Required" render-error %></td>
+</tr>
+
+<tr>
+<th align="right" valign="top">Content:</th>
+<td colspan="2"><textarea rows="24" cols="60" name="contents"><% "contents" render %></textarea></td>
+</tr>
+</table>
+
+<input type="hidden" name="n" value="<% "n" get number>string write %>" />
+<input type="hidden" name="furnace-form-submitted" value="annotate-paste"/>
+<input type="SUBMIT" value="Annotate" />
+</form>
--- /dev/null
+<% USING: namespaces io furnace calendar ; %>
+
+<h2>Annotation: <% "summary" get write %></h2>
+
+<table>
+<tr><th align="right">Annotation by:</th><td><% "author" get write %></td></tr>
+<tr><th align="right">File type:</th><td><% "mode" get write %></td></tr>
+<tr><th align="right">Created:</th><td><% "date" get timestamp>string write %></td></tr>
+</table>
+
+<% "syntax" render-template %>
--- /dev/null
+Slava Pestov
--- /dev/null
+</body>
+
+</html>
--- /dev/null
+<% USING: namespaces io furnace sequences xmode.code2html webapps.pastebin ; %>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+
+ <title><% "title" get write %></title>
+ <link rel="stylesheet" href="style.css" type="text/css" media="screen" title="no title" charset="utf-8" />
+ <% default-stylesheet %>
+ <link rel="alternate" type="application/atom+xml" title="Pastebin - Atom" href="feed.xml" />
+</head>
+
+<body id="index">
+
+ <div class="navbar">
+ <% [ paste-list ] "Paste list" render-link %> |
+ <% [ new-paste ] "New paste" render-link %> |
+ <% [ feed.xml ] "Syndicate" render-link %>
+ </div>
+ <h1 class="pastebin-title"><% "title" get write %></h1>
--- /dev/null
+<% USING: furnace xmode.catalog sequences kernel html.elements assocs io sorting continuations ; %>
+
+<select name="mode">
+ <% modes keys natural-sort [
+ <option dup "mode" session-var = [ "true" =selected ] when option> write </option>
+ ] each %>
+</select>
--- /dev/null
+<% USING: continuations furnace namespaces ; %>
+
+<%
+ "New paste" "title" set
+ "header" render-template
+%>
+
+<form method="POST" action="/responder/pastebin/submit-paste">
+
+<table>
+
+<tr>
+<th align="right">Summary:</th>
+<td><input type="TEXT" name="summary" value="<% "summary" render %>" /></td>
+<td align="left" class="error"><% "summary" "*Required" render-error %></td>
+</tr>
+
+<tr>
+<th align="right">Your name:</th>
+<td><input type="TEXT" name="author" value="<% "author" render %>" /></td>
+<td class="error"><% "author" "*Required" render-error %></td>
+</tr>
+
+<tr>
+<th align="right">File type:</th>
+<td><% "modes" render-template %></td>
+</tr>
+
+<!--
+<tr>
+<th align="right">Channel:</th>
+<td><input type="TEXT" name="channel" value="#concatenative" /></td>
+</tr>
+-->
+
+<tr>
+<td></td>
+<td colspan="2" class="error" align="left"><% "contents" "*Required" render-error %></td>
+</tr>
+
+<tr>
+<th align="right" valign="top">Content:</th>
+<td colspan="2"><textarea rows="24" cols="60" name="contents"><% "contents" render %></textarea></td>
+</tr>
+</table>
+
+<input type="hidden" name="furnace-form-submitted" value="new-paste"/>
+<input type="SUBMIT" value="Submit paste" />
+</form>
+
+<% "footer" render-template %>
--- /dev/null
+<% USING: namespaces furnace sequences ; %>
+
+<%
+ "Pastebin" "title" set
+ "header" render-template
+%>
+
+<table width="100%" cellspacing="10">
+ <tr>
+ <td valign="top">
+ <table width="100%">
+ <tr align="left" class="pastebin-headings">
+ <th width="50%">Summary:</th>
+ <th width="100">Paste by:</th>
+ <th width="200">Date:</th>
+ </tr>
+ <% "pastes" get <reversed> [ "paste-summary" render-component ] each %>
+ </table>
+ </td>
+ <td valign="top" width="25%">
+ <div class="infobox">
+ <p>This pastebin is written in <a href="http://factorcode.org/">Factor</a>. It is inspired by <a href="http://paste.lisp.org">lisppaste</a>.
+ </p>
+ <p>It can be used for collaborative development over IRC. You can post code for review, and annotate other people's code. Syntax highlighting for over a hundred file types is supported.
+ </p>
+ <p>
+ <% "webapps.pastebin" browse-webapp-source %></p>
+ </div>
+ </td>
+ </tr>
+</table>
+
+<% "footer" render-template %>
--- /dev/null
+<% USING: continuations namespaces io kernel math math.parser
+furnace webapps.pastebin calendar sequences ; %>
+
+<tr>
+ <td>
+ <a href="<% model get paste-link write %>">
+ <% "summary" get write %>
+ </a>
+ </td>
+ <td><% "author" get write %></td>
+ <td><% "date" get timestamp>string write %></td>
+</tr>
--- /dev/null
+USING: calendar furnace furnace.validator io.files kernel
+namespaces sequences http.server.responders html math.parser rss
+xml.writer xmode.code2html math calendar.format ;
+IN: webapps.pastebin
+
+TUPLE: pastebin pastes ;
+
+: <pastebin> ( -- pastebin )
+ V{ } clone pastebin construct-boa ;
+
+<pastebin> pastebin set-global
+
+TUPLE: paste
+summary author channel mode contents date
+annotations n ;
+
+: <paste> ( summary author channel mode contents -- paste )
+ f V{ } clone f paste construct-boa ;
+
+TUPLE: annotation summary author mode contents ;
+
+C: <annotation> annotation
+
+: get-paste ( n -- paste )
+ pastebin get pastebin-pastes nth ;
+
+: show-paste ( n -- )
+ serving-html
+ get-paste
+ [ "show-paste" render-component ] with-html-stream ;
+
+\ show-paste { { "n" v-number } } define-action
+
+: new-paste ( -- )
+ serving-html
+ [ "new-paste" render-template ] with-html-stream ;
+
+\ new-paste { } define-action
+
+: paste-list ( -- )
+ serving-html
+ [
+ [ show-paste ] "show-paste-quot" set
+ [ new-paste ] "new-paste-quot" set
+ pastebin get "paste-list" render-component
+ ] with-html-stream ;
+
+\ paste-list { } define-action
+
+: paste-link ( paste -- link )
+ paste-n number>string [ show-paste ] curry quot-link ;
+
+: safe-head ( seq n -- seq' )
+ over length min head ;
+
+: paste-feed ( -- entries )
+ pastebin get pastebin-pastes <reversed> 20 safe-head [
+ {
+ paste-summary
+ paste-link
+ paste-date
+ } get-slots timestamp>rfc3339 f swap <entry>
+ ] map ;
+
+: feed.xml ( -- )
+ "text/xml" serving-content
+ "pastebin"
+ "http://pastebin.factorcode.org"
+ paste-feed <feed> feed>xml write-xml ;
+
+\ feed.xml { } define-action
+
+: add-paste ( paste pastebin -- )
+ >r now over set-paste-date r>
+ pastebin-pastes 2dup length swap set-paste-n push ;
+
+: submit-paste ( summary author channel mode contents -- )
+ <paste> [ pastebin get add-paste ] keep
+ paste-link permanent-redirect ;
+
+\ new-paste
+\ submit-paste {
+ { "summary" v-required }
+ { "author" v-required }
+ { "channel" }
+ { "mode" v-required }
+ { "contents" v-required }
+} define-form
+
+\ new-paste {
+ { "channel" "#concatenative" }
+ { "mode" "factor" }
+} default-values
+
+: annotate-paste ( n summary author mode contents -- )
+ <annotation> swap get-paste
+ [ paste-annotations push ] keep
+ paste-link permanent-redirect ;
+
+[ "n" show-paste ]
+\ annotate-paste {
+ { "n" v-required v-number }
+ { "summary" v-required }
+ { "author" v-required }
+ { "mode" v-required }
+ { "contents" v-required }
+} define-form
+
+\ show-paste {
+ { "mode" "factor" }
+} default-values
+
+: style.css ( -- )
+ "text/css" serving-content
+ "style.css" send-resource ;
+
+\ style.css { } define-action
+
+"pastebin" "paste-list" "extra/webapps/pastebin" web-app
--- /dev/null
+<% USING: namespaces io furnace sequences xmode.code2html calendar ; %>
+
+<%
+ "Paste: " "summary" get append "title" set
+ "header" render-template
+%>
+
+<table>
+<tr><th>Paste by:</th><td><% "author" get write %></td></tr>
+<!-- <tr><th>Channel:</th><td><% "channel" get write %></td></tr> -->
+<tr><th>Created:</th><td><% "date" get timestamp>string write %></td></tr>
+<tr><th>File type:</th><td><% "mode" get write %></td></tr>
+</table>
+
+<% "syntax" render-template %>
+
+<% "annotations" get [ "annotation" render-component ] each %>
+
+<% model get "annotate-paste" render-component %>
+
+<% "footer" render-template %>
--- /dev/null
+body {
+ font:75%/1.6em "Lucida Grande", "Lucida Sans Unicode", verdana, geneva, sans-serif;
+ color:#888;
+}
+
+h1.pastebin-title {
+ font-size:300%;
+}
+
+a {
+ color:#222;
+ border-bottom:1px dotted #ccc;
+ text-decoration:none;
+}
+
+a:hover {
+ border-bottom:1px solid #ccc;
+}
+
+pre.code {
+ border:1px dashed #ccc;
+ background-color:#f5f5f5;
+ padding:5px;
+ font-size:150%;
+ color:#000000;
+}
+
+.navbar {
+ background-color:#eeeeee;
+ padding:5px;
+ border:1px solid #ccc;
+}
+
+.infobox {
+ border: 1px solid #C1DAD7;
+ padding: 10px;
+}
+
+.error {
+ color: red;
+}
--- /dev/null
+<% USING: xmode.code2html splitting namespaces ; %>
+
+<pre class="code"><% "contents" get string-lines "mode" get htmlize-lines %></pre>
--- /dev/null
+Slava Pestov
--- /dev/null
+USING: sequences rss arrays concurrency.combinators kernel
+sorting html.elements io assocs namespaces math threads vocabs
+html furnace http.server.templating calendar math.parser
+splitting continuations debugger system http.server.responders
+xml.writer prettyprint logging calendar.format ;
+IN: webapps.planet
+
+: print-posting-summary ( posting -- )
+ <p "news" =class p>
+ <b> dup entry-title write </b> <br/>
+ <a entry-link =href "more" =class a>
+ "Read More..." write
+ </a>
+ </p> ;
+
+: print-posting-summaries ( postings -- )
+ [ print-posting-summary ] each ;
+
+: print-blogroll ( blogroll -- )
+ <ul "description" =class ul>
+ [
+ <li> <a dup third =href a> first write </a> </li>
+ ] each
+ </ul> ;
+
+: format-date ( date -- string )
+ rfc3339>timestamp timestamp>string ;
+
+: print-posting ( posting -- )
+ <h2 "posting-title" =class h2>
+ <a dup entry-link =href a>
+ dup entry-title write-html
+ </a>
+ </h2>
+ <p "posting-body" =class p>
+ dup entry-description write-html
+ </p>
+ <p "posting-date" =class p>
+ entry-pub-date format-date write
+ </p> ;
+
+: print-postings ( postings -- )
+ [ print-posting ] each ;
+
+SYMBOL: default-blogroll
+SYMBOL: cached-postings
+
+: safe-head ( seq n -- seq' )
+ over length min head ;
+
+: mini-planet-factor ( -- )
+ cached-postings get 4 safe-head print-posting-summaries ;
+
+: planet-factor ( -- )
+ serving-html [ "planet" render-template ] with-html-stream ;
+
+\ planet-factor { } define-action
+
+: planet-feed ( -- feed )
+ "[ planet-factor ]"
+ "http://planet.factorcode.org"
+ cached-postings get 30 safe-head <feed> ;
+
+: feed.xml ( -- )
+ "text/xml" serving-content
+ planet-feed feed>xml write-xml ;
+
+\ feed.xml { } define-action
+
+: style.css ( -- )
+ "text/css" serving-content
+ "style.css" send-resource ;
+
+\ style.css { } define-action
+
+SYMBOL: last-update
+
+: <posting> ( author entry -- entry' )
+ clone
+ [ ": " swap entry-title 3append ] keep
+ [ set-entry-title ] keep ;
+
+: fetch-feed ( url -- feed )
+ download-feed feed-entries ;
+
+\ fetch-feed DEBUG add-error-logging
+
+: fetch-blogroll ( blogroll -- entries )
+ dup 0 <column> swap 1 <column>
+ [ fetch-feed ] parallel-map
+ [ [ <posting> ] with map ] 2map concat ;
+
+: sort-entries ( entries -- entries' )
+ [ [ entry-pub-date ] compare ] sort <reversed> ;
+
+: update-cached-postings ( -- )
+ default-blogroll get
+ fetch-blogroll sort-entries
+ cached-postings set-global ;
+
+: update-thread ( -- )
+ millis last-update set-global
+ [ update-cached-postings ] "RSS feed update slave" spawn drop
+ 10 60 * 1000 * sleep
+ update-thread ;
+
+: start-update-thread ( -- )
+ [
+ "webapps.planet" [
+ update-thread
+ ] with-logging
+ ] "RSS feed update master" spawn drop ;
+
+"planet" "planet-factor" "extra/webapps/planet" web-app
+
+{
+ { "Berlin Brown" "http://factorlang-fornovices.blogspot.com/feeds/posts/default" "http://factorlang-fornovices.blogspot.com" }
+ { "Chris Double" "http://www.blogger.com/feeds/18561009/posts/full/-/factor" "http://www.bluishcoder.co.nz/" }
+ { "Elie Chaftari" "http://fun-factor.blogspot.com/feeds/posts/default" "http://fun-factor.blogspot.com/" }
+ { "Doug Coleman" "http://code-factor.blogspot.com/feeds/posts/default" "http://code-factor.blogspot.com/" }
+ { "Daniel Ehrenberg" "http://useless-factor.blogspot.com/feeds/posts/default" "http://useless-factor.blogspot.com/" }
+ { "Gavin Harrison" "http://gmh33.blogspot.com/feeds/posts/default" "http://gmh33.blogspot.com/" }
+ { "Kio M. Smallwood"
+ "http://sekenre.wordpress.com/feed/atom/"
+ "http://sekenre.wordpress.com/" }
+ { "Phil Dawes" "http://www.phildawes.net/blog/category/factor/feed/atom" "http://www.phildawes.net/blog/" }
+ { "Samuel Tardieu" "http://www.rfc1149.net/blog/tag/factor/feed/atom/" "http://www.rfc1149.net/blog/tag/factor/" }
+ { "Slava Pestov" "http://factor-language.blogspot.com/atom.xml" "http://factor-language.blogspot.com/" }
+} default-blogroll set-global
--- /dev/null
+<% USING: namespaces html.elements webapps.planet sequences
+furnace ; %>
+
+<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+ <meta http-equiv="Content-type" content="text/html; charset=utf-8" />
+
+ <title>planet-factor</title>
+ <link rel="stylesheet" href="style.css" type="text/css" media="screen" title="no title" charset="utf-8" />
+ <link rel="alternate" type="application/atom+xml" title="Planet Factor - Atom" href="feed.xml" />
+</head>
+
+<body id="index">
+ <h1 class="planet-title">[ planet-factor ]</h1>
+ <table width="100%" cellpadding="10">
+ <tr>
+ <td> <% cached-postings get 20 safe-head print-postings %> </td>
+ <td valign="top" width="25%" class="infobox">
+ <p>
+ <b>planet-factor</b> is an Atom/RSS aggregator that collects the
+ contents of <a href="http://factorcode.org/">Factor</a>-related blogs. It is inspired by
+ <a href="http://planet.lisp.org">Planet Lisp</a>.
+ </p>
+ <p>
+ <img src="http://planet.lisp.org/feed-icon-14x14.png" />
+ <a href="feed.xml"> Syndicate </a>
+ </p>
+ <p>
+ This webapp is written in <a href="http://factorcode.org/">Factor</a>.<br/>
+ <% "webapps.planet" browse-webapp-source %>
+ </p>
+ <h2 class="blogroll-title">Blogroll</h2>
+ <% default-blogroll get print-blogroll %>
+ <p>
+ If you want your weblog added to the blogroll, <a href="http://factorcode.org/gethelp.fhtml">just ask</a>.
+ </p>
+ </td>
+ </tr>
+ </table>
+</body>
+
+</html>
--- /dev/null
+body {
+ font:75%/1.6em "Lucida Grande", "Lucida Sans Unicode", verdana, geneva, sans-serif;
+ color:#888;
+}
+
+h1.planet-title {
+ font-size:300%;
+}
+
+a {
+ color:#222;
+ border-bottom:1px dotted #ccc;
+ text-decoration:none;
+}
+
+a:hover {
+ border-bottom:1px solid #ccc;
+}
+
+.posting-title {
+ background-color:#f5f5f5;
+}
+
+pre, code {
+ color:#000000;
+ font-size:120%;
+}
+
+.infobox {
+ border-left: 1px solid #C1DAD7;
+}
+
+.posting-date {
+ text-align: right;
+ font-size:90%;
+}
+
+a.more {
+ display:block;
+ padding:0 0 5px 0;
+ color:#333;
+ text-decoration:none;
+ text-align:right;
+ border:none;
+}
}
else
{
- dpush(tag_object(memory_to_char_string(
- (char *)(buf + 1),c)));
+ if(c != size)
+ {
+ REGISTER_UNTAGGED(buf);
+ F_BYTE_ARRAY *new_buf = allot_byte_array(c);
+ UNREGISTER_UNTAGGED(buf);
+ memcpy(new_buf + 1, buf + 1,c);
+ buf = new_buf;
+ }
+ dpush(tag_object(buf));
break;
}
}
}
+DEFINE_PRIMITIVE(fputc)
+{
+ FILE *file = unbox_alien();
+ F_FIXNUM ch = to_fixnum(dpop());
+
+ for(;;)
+ {
+ if(fputc(ch,file) == EOF)
+ {
+ io_error();
+
+ /* Still here? EINTR */
+ }
+ else
+ break;
+ }
+}
+
DEFINE_PRIMITIVE(fwrite)
{
- FILE* file = unbox_alien();
- F_STRING* text = untag_string(dpop());
- F_FIXNUM length = untag_fixnum_fast(text->length);
- char* string = to_char_string(text,false);
+ FILE *file = unbox_alien();
+ F_BYTE_ARRAY *text = untag_byte_array(dpop());
+ F_FIXNUM length = array_capacity(text);
+ char *string = (char *)(text + 1);
- if(string_capacity(text) == 0)
+ if(length == 0)
return;
for(;;)
int err_no(void);
DECLARE_PRIMITIVE(fopen);
+DECLARE_PRIMITIVE(fgetc);
+DECLARE_PRIMITIVE(fread);
+DECLARE_PRIMITIVE(fputc);
DECLARE_PRIMITIVE(fwrite);
DECLARE_PRIMITIVE(fflush);
DECLARE_PRIMITIVE(fclose);
-DECLARE_PRIMITIVE(fgetc);
-DECLARE_PRIMITIVE(fread);
/* Platform specific primitives */
DECLARE_PRIMITIVE(open_file);
dpush(result);
}
+DEFINE_PRIMITIVE(set_os_envs)
+{
+ F_ARRAY *array = untag_array(dpop());
+ CELL size = array_capacity(array);
+
+ /* Memory leak */
+ char **env = calloc(size + 1,sizeof(CELL));
+
+ CELL i;
+ for(i = 0; i < size; i++)
+ {
+ F_STRING *string = untag_string(array_nth(array,i));
+ CELL length = to_fixnum(string->length);
+
+ char *chars = malloc(length + 1);
+ char_string_to_memory(string,chars);
+ chars[length] = '\0';
+ env[i] = chars;
+ }
+
+ environ = env;
+}
+
F_SEGMENT *alloc_segment(CELL size)
{
int pagesize = getpagesize();
{
Sleep(msec);
}
+
+DECLARE_PRIMITIVE(set_os_envs)
+{
+ not_implemented_error();
+}
primitive_fopen,
primitive_fgetc,
primitive_fread,
+ primitive_fputc,
primitive_fwrite,
primitive_fflush,
primitive_fclose,
primitive_set_innermost_stack_frame_quot,
primitive_call_clear,
primitive_os_envs,
+ primitive_set_os_envs,
primitive_resize_byte_array,
primitive_resize_bit_array,
primitive_resize_float_array,
DECLARE_PRIMITIVE(exit);
DECLARE_PRIMITIVE(os_env);
DECLARE_PRIMITIVE(os_envs);
+DECLARE_PRIMITIVE(set_os_envs);
DECLARE_PRIMITIVE(eq);
DECLARE_PRIMITIVE(millis);
DECLARE_PRIMITIVE(sleep);