if window.angular_poc_loaded then return
window.angular_poc_loaded = true
do ->
doc = document; warn = window.warn ?= (a...) -> console.log a...
loadTemplate = (sel) -> doc.importNode doc.querySelector(sel).content,true
loadFirst = null
fns = []
run = -> if fn = fns.shift() then fn run
runScript = (code,scope) ->
Function(CoffeeScript.compile(code,bare:true)).apply(scope)
runScriptLater = (code,scope) ->
fns.push (cb) -> runScript code, scope; cb()
loadScript = (src,scope) ->
fns.push (cb) ->
r = new window.XMLHttpRequest
r.open('GET',src, true)
r.overrideMimeType('text/plain')
r.onreadystatechange = -> if 4 == r.readyState
if r.status in [0,200] then runScript r.responseText, scope
else throw new Error "Script not loaded" + src
cb()
r.send()
applyTemplate = (tpl,trg,content) ->
trg = $(trg).append loadTemplate tpl
for t in trg.find("content[select]")
select = $(t).attr "select"
match = $(content).find(select)
$(t).replaceWith match.contents() if match.length > 0
match.remove()
trg.find("content").replaceWith content if content?
scope = document:trg
for t in trg.find("script[type='text/coffeescript']")
src = $(t).attr "src"
warn src:src
if src == "" or not src? then runScriptLater $(t).text(), scope
else loadScript src, scope
run()
trg
do ->
tabs = $("tabs")
apps = $("app")
tpl = "linedemo>template"
apps.each ->
name = $(@).attr("name"); autostart = $(@).attr "autostart"
console.log name, tabs
id = @id; warn {name,id,autostart}
tab = applyTemplate tpl, $("<tab>"), app = @
tab.find(".tab-button").click fn = ->
warn "loading app",name,app_tpl = "##{id}>template"
applyTemplate app_tpl,app
$(tabs).append tab
setTimeout fn,1 if autostart?
warn "app tabs and first app loaded"
window.SVGCOLORS =
aliceblue: "rgb(240, 248, 255)"
antiquewhite: "rgb(250, 235, 215)"
aqua: "rgb( 0, 255, 255)"
aquamarine: "rgb(127, 255, 212)"
azure: "rgb(240, 255, 255)"
beige: "rgb(245, 245, 220)"
bisque: "rgb(255, 228, 196)"
black: "rgb( 0, 0, 0)"
blanchedalmond: "rgb(255, 235, 205)"
blue: "rgb( 0, 0, 255)"
blueviolet: "rgb(138, 43, 226)"
brown: "rgb(165, 42, 42)"
burlywood: "rgb(222, 184, 135)"
cadetblue: "rgb( 95, 158, 160)"
chartreuse: "rgb(127, 255, 0)"
chocolate: "rgb(210, 105, 30)"
coral: "rgb(255, 127, 80)"
cornflowerblue: "rgb(100, 149, 237)"
cornsilk: "rgb(255, 248, 220)"
crimson: "rgb(220, 20, 60)"
cyan: "rgb( 0, 255, 255)"
darkblue: "rgb( 0, 0, 139)"
darkcyan: "rgb( 0, 139, 139)"
darkgoldenrod: "rgb(184, 134, 11)"
darkgray: "rgb(169, 169, 169)"
darkgreen: "rgb( 0, 100, 0)"
darkgrey: "rgb(169, 169, 169)"
darkkhaki: "rgb(189, 183, 107)"
darkmagenta: "rgb(139, 0, 139)"
darkolivegreen: "rgb( 85, 107, 47)"
darkorange: "rgb(255, 140, 0)"
darkorchid: "rgb(153, 50, 204)"
darkred: "rgb(139, 0, 0)"
darksalmon: "rgb(233, 150, 122)"
darkseagreen: "rgb(143, 188, 143)"
darkslateblue: "rgb( 72, 61, 139)"
darkslategray: "rgb( 47, 79, 79)"
darkslategrey: "rgb( 47, 79, 79)"
darkturquoise: "rgb( 0, 206, 209)"
darkviolet: "rgb(148, 0, 211)"
deeppink: "rgb(255, 20, 147)"
deepskyblue: "rgb( 0, 191, 255)"
dimgray: "rgb(105, 105, 105)"
dimgrey: "rgb(105, 105, 105)"
dodgerblue: "rgb( 30, 144, 255)"
firebrick: "rgb(178, 34, 34)"
floralwhite: "rgb(255, 250, 240)"
forestgreen: "rgb( 34, 139, 34)"
fuchsia: "rgb(255, 0, 255)"
gainsboro: "rgb(220, 220, 220)"
ghostwhite: "rgb(248, 248, 255)"
gold: "rgb(255, 215, 0)"
goldenrod: "rgb(218, 165, 32)"
gray: "rgb(128, 128, 128)"
grey: "rgb(128, 128, 128)"
green: "rgb( 0, 128, 0)"
greenyellow: "rgb(173, 255, 47)"
honeydew: "rgb(240, 255, 240)"
hotpink: "rgb(255, 105, 180)"
indianred: "rgb(205, 92, 92)"
indigo: "rgb( 75, 0, 130)"
ivory: "rgb(255, 255, 240)"
khaki: "rgb(240, 230, 140)"
lavender: "rgb(230, 230, 250)"
lavenderblush: "rgb(255, 240, 245)"
lawngreen: "rgb(124, 252, 0)"
lemonchiffon: "rgb(255, 250, 205)"
lightblue: "rgb(173, 216, 230)"
lightcoral: "rgb(240, 128, 128)"
lightcyan: "rgb(224, 255, 255)"
lightgoldenrodyellow: "rgb(250, 250, 210)"
lightgray: "rgb(211, 211, 211)"
lightgreen: "rgb(144, 238, 144)"
lightgrey: "rgb(211, 211, 211)"
lightpink: "rgb(255, 182, 193)"
lightsalmon: "rgb(255, 160, 122)"
lightseagreen: "rgb( 32, 178, 170)"
lightskyblue: "rgb(135, 206, 250)"
lightslategray: "rgb(119, 136, 153)"
lightslategrey: "rgb(119, 136, 153)"
lightsteelblue: "rgb(176, 196, 222)"
lightyellow: "rgb(255, 255, 224)"
lime: "rgb( 0, 255, 0)"
limegreen: "rgb( 50, 205, 50)"
linen: "rgb(250, 240, 230)"
magenta: "rgb(255, 0, 255)"
maroon: "rgb(128, 0, 0)"
mediumaquamarine: "rgb(102, 205, 170)"
mediumblue: "rgb( 0, 0, 205)"
mediumorchid: "rgb(186, 85, 211)"
mediumpurple: "rgb(147, 112, 219)"
mediumseagreen: "rgb( 60, 179, 113)"
mediumslateblue: "rgb(123, 104, 238)"
mediumspringgreen: "rgb( 0, 250, 154)"
mediumturquoise: "rgb( 72, 209, 204)"
mediumvioletred: "rgb(199, 21, 133)"
midnightblue: "rgb( 25, 25, 112)"
mintcream: "rgb(245, 255, 250)"
mistyrose: "rgb(255, 228, 225)"
moccasin: "rgb(255, 228, 181)"
navajowhite: "rgb(255, 222, 173)"
navy: "rgb( 0, 0, 128)"
oldlace: "rgb(253, 245, 230)"
olive: "rgb(128, 128, 0)"
olivedrab: "rgb(107, 142, 35)"
orange: "rgb(255, 165, 0)"
orangered: "rgb(255, 69, 0)"
orchid: "rgb(218, 112, 214)"
palegoldenrod: "rgb(238, 232, 170)"
palegreen: "rgb(152, 251, 152)"
paleturquoise: "rgb(175, 238, 238)"
palevioletred: "rgb(219, 112, 147)"
papayawhip: "rgb(255, 239, 213)"
peachpuff: "rgb(255, 218, 185)"
peru: "rgb(205, 133, 63)"
pink: "rgb(255, 192, 203)"
plum: "rgb(221, 160, 221)"
powderblue: "rgb(176, 224, 230)"
purple: "rgb(128, 0, 128)"
red: "rgb(255, 0, 0)"
rosybrown: "rgb(188, 143, 143)"
royalblue: "rgb( 65, 105, 225)"
saddlebrown: "rgb(139, 69, 19)"
salmon: "rgb(250, 128, 114)"
sandybrown: "rgb(244, 164, 96)"
seagreen: "rgb( 46, 139, 87)"
seashell: "rgb(255, 245, 238)"
sienna: "rgb(160, 82, 45)"
silver: "rgb(192, 192, 192)"
skyblue: "rgb(135, 206, 235)"
slateblue: "rgb(106, 90, 205)"
slategray: "rgb(112, 128, 144)"
slategrey: "rgb(112, 128, 144)"
snow: "rgb(255, 250, 250)"
springgreen: "rgb( 0, 255, 127)"
steelblue: "rgb( 70, 130, 180)"
tan: "rgb(210, 180, 140)"
teal: "rgb( 0, 128, 128)"
thistle: "rgb(216, 191, 216)"
tomato: "rgb(255, 99, 71)"
turquoise: "rgb( 64, 224, 208)"
violet: "rgb(238, 130, 238)"
wheat: "rgb(245, 222, 179)"
white: "rgb(255, 255, 255)"
whitesmoke: "rgb(245, 245, 245)"
yellow: "rgb(255, 255, 0)"
yellowgreen: "rgb(154, 205, 50)"
<linedemo unresolved>
<template>
<!--<button class="tab-button"><content select="name"></content></button>-->
<content></content>
</template>
<tabs>
</tabs>
<app name="Lines" id="line-render-app" autostart>
<!--<name>Lines!</name>-->
<template>
<canvas width="400" height=200></canvas>
<controls>
<inputs ng-enter="render">
<label>lines:</label><input type="text" ng-model="lineCount"></input>
<label>t<sub>max</sub>:</label><input type="text" ng-model="t_max"></input>
<label></label><button ng-click="render">render</button>
<label></label><button ng-click="render(,true)">render (sync)</button>
</inputs>
<stats>
<label>t<sub>create</sub></label><span> = </span><span ng-text="t_create"></span><span> ms</span>
<label>t<sub>pack</sub></label><span> = </span><span ng-text="t_pack"></span><span> ms</span>
<label>t<sub>unpack</sub></label><span> = </span><span ng-text="t_unpack"></span><span> ms</span>
<label>t<sub>render</sub></label><span> = </span><span ng-text="t_render"></span><span> ms</span>
</stats>
</controls>
<script type="text/coffeescript">
#imports
{SVGCOLORS} = window
#setup global scope
G = this
#setup scope for DOM interaction
G.O = {}
#basic utils
G.warn ?= (a...) -> console.debug a...
G.$ ?= (a...) -> document.querySelectorAll a...
G.isString ?= (s) -> typeof s is "string"
G.isEmpty ?= (s) -> (not s?) or s == ""
#setup document root
G.document ?= document
if G.document == document then G.warn "You are running as root. Be careful!", G.document
#wrap global jQuery locally
G.$ = do -> jQuery = window.$; (o,a...) ->
if isString o then jQuery(G.document).find o,a...
else jQuery o,a...
#benchmarking fns
G.bench = do ->
bench_count = 0
bench_fn = (name,fn=name,log=true) ->
bench_count++
name = bench_count unless G.isString name
t1 = Date.now(); fn(); t = Date.now() - t1
if log == true then G.warn "bench",name,":",t,"ms"
t
G.abench = do ->
abench_count = 0
abench_fn = (name,fn=name,setTime,cb,log=true) ->
abench_count++
name = bench_count unless G.isString name
t1 = Date.now()
fn (a...) ->
setTime t = Date.now() - t1
G.warn "bench",name,":",t,"ms"
cb a... if cb?
#expression parser
G.parseValue = (v) -> #maps string to basic atoms
switch v
when "" then undefined
when "null" then null
when "true" then true
when "false" then false
else v
G.parseExpr = (exp) -> #converts "fn(a.b)" string to name + params
tokens = exp.split /\(|\)/
name = tokens[0]
if tokens.length == 1 then params = []
else if tokens.length == 3 then params = tokens[1].split(",").map G.parseValue
else throw new Error "Bad expression: #{exp}"
result =
name: name
params: params
#warn "parseExpr", result
result
#scope utils
G.read = (scope,name) -> #reads properties or calls getter in scope
if typeof scope[name] is "function" then scope[name]()
else scope[name]
G.write = (scope,name,val) -> #writes properties or calls setter in scope
if typeof scope[name] is "function" then scope[name] val
else scope[name] = val
G.call = (scope,name,params=[]) -> #calls a fn in scope
if typeof scope[name] is "function" then scope[name] params...
else throw new Error "#{name} is not a function in the scope"
#async utils
G.postpone = (t,fn=t) -> t = 0 if isNaN t; setTimeout fn,t
G.async = do -> #queues and auto invokes async fns
list = []; timo = null
runLater = -> clearTimeout timo; timo = postpone 1, run
run = -> if list.length > 0 then list.shift()(); runLater()
async_fn = (fns...) -> list.push fns...; runLater()
#import utils from global scope
{O,warn,$,bench,abench,isString,isEmpty,
parseExpr,read,write,call,postpone,async} = G
class window.SvgColors
_colors = (v for k,v of SVGCOLORS)
constructor: ->
i = 0
fn = (num=i) -> _colors[i]
fn.next = => i = (i + 1)%_colors.length; @color_fn()
@color_fn = fn
D =
START_LEN: 10000
MAX_LEN: 10e6
T_MAX: "10000"
{MAX_LEN} = D
rand = (n=1) -> Math.random()*n
fr = (n) -> Math.round n
frand = (n) -> fr rand n
O.line_count = D.START_LEN
O.t_max = D.T_MAX
O.points = []
O.lineCount = (n) ->
if n?
n = 1 * n
O.line_count = if isNaN(n) then 0 else n
warn "lineCount:", O.line_count, n:n
O.line_count
O.resetStats = -> O.t_create = O.t_pack = O.t_unpack = O.t_render = ""
O.render = (cb,sync=false) ->
O.packed = "[]"
b = (name,fn) -> bench name,fn,false
if sync then dsync = (fn) -> fn()
else dsync = (fn) -> async -> fn(); O.digest()
dsync -> O.resetStats()
dsync -> O.t_create = b "create", -> O.createPoints()
dsync -> O.t_pack = b "pack", -> O.packed = JSON.stringify(data: O.points, size: O.points.length)
dsync -> O.t_unpack = b "unpack", -> O.points = JSON.parse(O.packed).data
draw_fn = (cb) -> O.drawLines cb,sync
setTime = (v) -> O.t_render = v
done = -> O.digest(); warn "render done", sync:sync; cb?()
if sync then setTime b "render", draw_fn; done()
else async -> abench "render", draw_fn, setTime, done
O.createPoints = ->
{w,h} = O
v0 = rand(h)
len = O.line_count
nextVal = (v0) -> Math.max 0, Math.min h, v0 + h * (rand() - rand())/100
toPoint = (n) -> [w*i/len + rand(), v0 = nextVal(v0)]
warn "creatng", len, "points"
len = MAX_LEN if len > MAX_LEN
if len > 0
O.points = list = []
list.push toPoint i for i in [0...len]
#warn i, "points created"
O.line_count = O.points.length
return
O.drawLines = (cb,sync=false) ->
{w,h,canvas,t_max} = O
ctx = canvas.getContext "2d"
ctx.save()
ctx.clearRect 0,0,w,h
#ctx.translate -0.5,-0.5 #not for rects!
ctx.fillStyle = "black"
len = O.line_count
t1 = Date.now()
t_max = 1 * t_max
p0 = O.points[0]
color = new SvgColors().color_fn
ctx.strokeStyle = color()
fns = []
addRenderFn = (i0,num) ->
i1 = Math.min len-1, i0 + num
#warn "addRenderFn", i0,i1,len
fns.push fn = -> renderLines i0,i1
addRenderFn i,step-1 for i in [0...len] by step=100000
renderLines = (i0,i1) -> #bench "renderLines", ->
if Date.now() - t1 < t_max
#warn "renderLines", i0,i1,len
for i in [i0..i1]
p = O.points[i]
ctx.beginPath()
ctx.moveTo p0[0],p0[1]
ctx.lineTo p[0], p[1]
ctx.stroke()
#ctx.fillRect frand(w), frand(h), 1,1
p0 = p
if i%1e3 == 0
ctx.strokeStyle = color.next()
if Date.now() - t1 > t_max
warn "stopped rendering after",t_max,"ms"; break
done = ->
warn i,"of",len,"lines rendered",sync:sync
ctx.restore()
cb?()
if sync then fn() for fn in fns; done()
else async fns...; async done
O.setupModel = ->
O.canvas = $("canvas")[0]
for e in $("input[ng-model]")
name = $(e).attr "ng-model"
val = read O,name if isEmpty val = $(e).val()
write O,name,val
$(e).change ->
warn "change",name=$(@).attr("ng-model"), val = $(@).val()
write O,name,val
for e in $("[ng-enter]")
$(e).keypress (event) -> if event.which == 13
$(@).find("input").change()
expr = parseExpr $(@).attr("ng-enter")
call O, expr.name, expr.params
for e in $("[ng-click]")
$(e).click ->
expr = parseExpr $(@).attr("ng-click")
call O, expr.name, expr.params
#populate derived and bound values
O.digest true
return
O.digest = (init=false) ->
O.w = O.canvas.width
O.h = O.canvas.height
for e in $("[ng-text]")
name = $(e).attr "ng-text"
$(e).text val = read O,name
#warn "update text", name, val
for e in $("input[ng-model]")
name = $(e).attr "ng-model"
$(e).val val = read O,name
#warn "update val", name, val
return
O.setupModel()
O.render()
warn "vdda paper test module loaded"
</script>
<script type="text/coffeescript">warn "Lines app loaded"</script>
</template>
</app>
</linedemo>
linedemo canvas {
border: solid 1px silver;
}
linedemo > * {
float: left;
clear: left;
}
linedemo controls > * {
float: left;
}
linedemo span,linedemo input,linedemo button,linedemo p,linedemo label {
font-family: BentonSans, Benton Sans, News Gothic MT, News Gothic, Verdana, sans;
font-size: 10pt;
}
linedemo inputs {
padding-left: 3px;
}
linedemo inputs > input {
float: left;
}
linedemo inputs > label {
margin-top: 8px;
float: left;
clear: left;
min-width: 35px;
}
linedemo inputs > button {
float: left;
}
linedemo stats {
padding-left: 5px;
border: solid 1px silver;
}
linedemo stats > label {
clear: left;
}
stats > * {
float: left;
}
linedemo stats > span {
white-space: pre;
}
linedemo label {
display: block;
color: gray;
float: left;
min-width: 50px;
}
linedemo input,linedemo button,linedemo ul {
padding: 1px;
margin: 1px;
}
linedemo input,linedemo button {
height: 30px;
}
External resources loaded into this fiddle: