Skip to content
This repository
Browse code

Merge remote-tracking branch 'hwhsiao/master'

  • Loading branch information...
commit d70fcc9c4be7abef695e23ca434cfd1cbdc0e118 2 parents e072417 + 7b56f01
Chia-liang Kao authored July 21, 2012
10  Makefile.PL
@@ -7,6 +7,10 @@ author   q{Chia-liang Kao <clkao@clkao.org>};
7 7
 license  'perl';
8 8
 
9 9
 build_requires 'Test::More';
  10
+build_requires 'Class::Load';
  11
+build_requires 'Redis::hiredis';
  12
+build_requires 'Graph';
  13
+build_requires 'Number::Extreme';
10 14
 
11 15
 requires 'Try::Tiny';
12 16
 requires 'Plack';
@@ -36,14 +40,16 @@ auto_set_repository;
36 40
 install_share;
37 41
 
38 42
 clean_files "share/static/gen/*.js";
  43
+clean_files "share/static/gen/*.sass";
39 44
 
40 45
 WriteAll;
41 46
 
42 47
 sub MY::postamble {
43 48
     return <<'MAKE_FRAG';
44  
-all:: buildjs
  49
+all:: build_css_and_jss
45 50
 
46  
-buildjs:
  51
+build_css_and_jss:
47 52
 	$(PERL) utils/build.pl
  53
+
48 54
 MAKE_FRAG
49 55
 }
8  coffee/tschart-ui.coffee
@@ -51,8 +51,8 @@ class TradeSpring.ChartUI
51 51
           view.init_width()
52 52
           view.on_view_change()
53 53
 
54  
-        h.prev = d.start_price;
55  
-        $('.prev', h).text(d.start_price);
  54
+        h.prev = d.start_price
  55
+        $('.prev', h).text(d.start_price)
56 56
 
57 57
 
58 58
   @init_connection_status = (hpipe) ->
@@ -86,12 +86,12 @@ class TradeSpring.ChartUI
86 86
   @set_day_low = (h, day_low, view) ->
87 87
         if !h.day_low  || day_low  < h.day_low
88 88
           h.day_low = day_low
89  
-          $('.low', h).text(day_low);
  89
+          $('.low', h).text(day_low)
90 90
           view.price_label_low.text(day_low).css('top', view.candle_zone.val_to_y(day_low))
91 91
 
92 92
   @init_live_events = (view, hpipe, h) ->
93 93
         $(hpipe).bind("message.tick", (e, d) =>
94  
-                view.on_new_event(d);
  94
+                view.on_new_event(d)
95 95
                 if h
96 96
                   change = d.prices[CLOSE] - h.prev
97 97
                   h.last = d.prices[CLOSE]
176  coffee/tschart-widget.coffee
@@ -2,7 +2,7 @@
2 2
 exports = @TradeSpring
3 3
 
4 4
 class TradeSpring.Widget
5  
-  constructor: (@zone) ->
  5
+  constructor: (@zone, @name) ->
6 6
   init: (d) ->
7 7
     @render_item v, d.start + parseInt(i) for i, v of d.values
8 8
 
@@ -19,9 +19,10 @@ class TradeSpring.Widget.Curve extends TradeSpring.Widget
19 19
       @curve.lineTo i * 10 + off_.translation[0], (@zone.ymax - val + off_.translation[1]) * off_.scale[1]
20 20
     else
21 21
       @curve = @zone.render_curve([ val ], i, @color, @name, @fast)
  22
+      @curve.node.setAttribute "class", @name
22 23
   init: (d) ->
23 24
     @curve = @zone.render_curve(d.values, d.start, @color, @name, @fast)
24  
-    @curve.node.setAttribute "class", "curve"
  25
+    @curve.node.setAttribute "class", @name
25 26
 
26 27
 class TradeSpring.Widget.Bar extends TradeSpring.Widget
27 28
   constructor: (@zone, @color = 'red', @name, @fast) ->
@@ -36,13 +37,15 @@ class TradeSpring.Widget.Bar extends TradeSpring.Widget
36 37
       @zone._callbacks[idx][i - @zone.view.loaded_offset] = cb
37 38
     else
38 39
       @bar = 1
  40
+      @bar.node.setAttribute "class", @name
39 41
       @zone.render_bar [ val ], i, null, @name, 0, 0, @fast
40 42
   init: (d) ->
41 43
     @bar = 1
  44
+    @bar.node.setAttribute "class", @name
42 45
     @zone.render_bar d.values, d.start, @color, @name, 0, 0, @fast
43 46
 
44 47
 class TradeSpring.Widget.CandleBody extends TradeSpring.Widget
45  
-  constructor: (@zone, @color = 'red') ->
  48
+  constructor: (@zone, @color = 'red', @name) ->
46 49
   render_item: (val, i) ->
47 50
     data = @zone.data_set[i - @zone.view.loaded_offset]
48 51
     val = parseInt(val)
@@ -54,20 +57,40 @@ class TradeSpring.Widget.CandleBody extends TradeSpring.Widget
54 57
       "stroke-width": width
55 58
       stroke: c
56 59
     ).attr(@zone.offset_attr)
  60
+    bar.node.setAttribute "class", @name
57 61
     @zone.blanket.push bar
58 62
   get_color: (val) ->
59 63
     (if val > 0 then "red" else (if val < 0 then "green" else "yellow"))
60 64
 
61  
-
62  
-
63 65
 class TradeSpring.Widget.CandleBackgroundBase extends TradeSpring.Widget
64  
-  constructor: (@zone) ->
  66
+  constructor: (@zone, @name) ->
65 67
     @data = {}
66 68
   render_item: (val, i) ->
67 69
     @data[i - @zone.view.loaded_offset] = val
68 70
   get: (i) ->
69 71
     @data[i - @zone.view.loaded_offset]
70 72
 
  73
+class TradeSpring.Widget.CandleBackground extends TradeSpring.Widget
  74
+  constructor: (@zone, @color = 'orange', @name, @base) ->
  75
+    @base = zone.view.indicators[@base].self
  76
+  render_item: (val, i) ->
  77
+    val = parseFloat(val)
  78
+    return if (isNaN(val))
  79
+    c = @get_color(val)
  80
+    x = i * 10
  81
+    height = @base.get(i) - val
  82
+    bar = @zone.r.path().beginMulti().moveTo(x, @zone.ymax - val).relatively().lineTo(0, -height).andUpdate().attr(
  83
+      opacity: 0.6
  84
+      "stroke-width": 10
  85
+      stroke: (if height > 0 then "green" else "red")
  86
+    ).attr(@zone.offset_attr).toBack()
  87
+    bar.node.setAttribute "class", @name
  88
+    @zone.blanket.push bar
  89
+  get_color: (val) ->
  90
+    (if val > 0 then "red" else (if val < 0 then "green" else "yellow"))
  91
+  val: (d) ->
  92
+    @render_item d.value, d.i
  93
+
71 94
 wrapper = (klass, args) ->
72 95
   c = Object.create(klass.prototype)
73 96
   klass.apply(c, args)
@@ -78,7 +101,7 @@ wrapper = (klass, args) ->
78 101
   self: c
79 102
 
80 103
 class TradeSpring.Widget.SignalArrow extends TradeSpring.Widget
81  
-  constructor: (@zone, @color = 'black') ->
  104
+  constructor: (@zone, @color = 'black', @name) ->
82 105
   render_item: (val, i) ->
83 106
     price = undefined
84 107
     val = parseInt(val)
@@ -90,7 +113,8 @@ class TradeSpring.Widget.SignalArrow extends TradeSpring.Widget
90 113
       x: i
91 114
       y: price
92 115
       direction: val,
93  
-      c: @color);
  116
+      c: @color)
  117
+    arrow.node.setAttribute "class", @name
94 118
     @zone.blanket.push arrow
95 119
 
96 120
 class TradeSpring.Widget.Band extends TradeSpring.Widget
@@ -120,7 +144,7 @@ class TradeSpring.Widget.Band extends TradeSpring.Widget
120 144
 
121 145
     if @pointer
122 146
       @pointer.attr('path', p.attr('path')).toBack()
123  
-      p.remove();
  147
+      p.remove()
124 148
     else
125 149
       @pointer = p.attr(
126 150
         "stroke-width": "0.0"
@@ -128,6 +152,7 @@ class TradeSpring.Widget.Band extends TradeSpring.Widget
128 152
         stroke: "none"
129 153
         "fill-opacity": 0.5
130 154
       ).toBack()
  155
+      @pointer.node.setAttribute "class", @name
131 156
 
132 157
     xstart = i - if @last_up and @last_down then 1 else 0.5
133 158
     a = @zone.path([ [ "M", xstart * 10, ymax - @last_up ], [ "L", (i) * 10, ymax - up ], [ "L", (i) * 10, ymax - down ], [ "L", xstart * 10, ymax - @last_down ], [ "z" ] ]).attr(
@@ -136,6 +161,7 @@ class TradeSpring.Widget.Band extends TradeSpring.Widget
136 161
       stroke: "none"
137 162
       "fill-opacity": 0.5
138 163
     ).toBack()
  164
+    a.node.setAttribute "class", @name
139 165
     @zone.blanket.push a
140 166
     if @annotate
141 167
       text = Math.round(down) + " - " + Math.round(up)
@@ -147,6 +173,7 @@ class TradeSpring.Widget.Band extends TradeSpring.Widget
147 173
           "text-anchor": "left"
148 174
         ).toBack()
149 175
         @label.translate -$(@label.node).width() / 2
  176
+        @label.node.setAttribute "class", @name
150 177
         @zone.blanket.push @label
151 178
         @annotate_cb()  if @annotate_cb
152 179
     @last_up = up
@@ -184,12 +211,16 @@ class TradeSpring.Widget.Band extends TradeSpring.Widget
184 211
     @last_up = d.values[d.values.length - 1][0]
185 212
     @last_down = d.values[d.values.length - 1][1]
186 213
 
  214
+    _area.node.setAttribute "class", @name
187 215
 
188 216
 
  217
+window.mk_rect = (args...) -> wrapper(TradeSpring.Widget.Rect, args)
  218
+window.mk_ellipse = (args...) -> wrapper(TradeSpring.Widget.Ellipse, args)
189 219
 window.mk_curve = (args...) -> wrapper(TradeSpring.Widget.Curve, args)
190 220
 window.mk_bar   = (args...) -> wrapper(TradeSpring.Widget.Bar, args)
191 221
 window.mk_candlebody = (args...) -> wrapper(TradeSpring.Widget.CandleBody, args)
192 222
 window.mk_candlebackgroundbase = (args...) -> wrapper(TradeSpring.Widget.CandleBackgroundBase, args)
  223
+window.mk_candlebackground = (args...) -> wrapper(TradeSpring.Widget.CandleBackground, args)
193 224
 window.mk_band = (args...) -> wrapper(TradeSpring.Widget.Band, args)
194 225
 window.mk_signalarrow = (args...) -> wrapper(TradeSpring.Widget.SignalArrow, args)
195 226
 window.mk_srline = (args...) -> wrapper(TradeSpring.Widget.SRLine, args)
@@ -202,70 +233,60 @@ window.mk_debug = (zone) ->
202 233
   val: (d) ->
203 234
     console.log "val", d.values
204 235
 
205  
-window.mk_rect = (zone, color, name, attr) ->
206  
-  last_start = undefined
207  
-  rx = undefined
208  
-  lx = undefined
209  
-  us = zone.r.set()
210  
-  ob = zone.blanket
211  
-  ob.push us
212  
-  render_item = (rect, i) ->
213  
-    if rx and last_start == i - rect[2]
214  
-      us.pop()
215  
-      rx.remove()
216  
-      lx.remove()
217  
-    rx = zone.rect(10 * (i - rect[2]) - 5, rect[0], rect[2] * 10 + 10, rect[0] - rect[1]).attr(
218  
-      "stroke-width": 2
219  
-      stroke: color
220  
-    )
221  
-    lx = zone.r.path().moveTo(10 * (i - rect[2]) - 10, zone.ymax - (parseInt(rect[0]) + parseInt(rect[1])) / 2).relatively().lineTo(rect[2] * 10 + 20, 0).attr(
222  
-      "stroke-width": 1
223  
-      stroke: "gray"
224  
-    ).attr(zone.offset_attr)
225  
-    if attr
226  
-      rx.attr attr
227  
-      lx.attr attr
228  
-    us.push rx
229  
-    us.push lx
230  
-    last_start = i - rect[2]
231  
-  init: (d) ->
232  
-    jQuery(d.values).each (idx) ->
233  
-      render_item this, d.start + idx  if this? and this[0]?
234  
-
235  
-  val: (d) ->
236  
-    render_item d.value, d.i  if d.value? and d.value[0]?
237  
-
238  
-window.mk_ellipse = (zone, color, name) ->
239  
-  last_start = undefined
240  
-  rx = undefined
241  
-  lx = undefined
242  
-  us = zone.r.set()
243  
-  zone.blanket.push us
244  
-  render_item = (spec, i) ->
245  
-    if rx and last_start == i - spec[2]
246  
-      us.pop()
247  
-      rx.remove()
248  
-      lx.remove()
  236
+class TradeSpring.Widget.Rect extends TradeSpring.Widget
  237
+  constructor: (@zone, @color, @name, @attr) ->
  238
+      @last_start = undefined
  239
+      @rx = undefined
  240
+      @lx = undefined
  241
+      @us = @zone.r.set()
  242
+      ob = @zone.blanket
  243
+      ob.push @us
  244
+  render_item: (rect, i) ->
  245
+      if @rx and @last_start == i - rect[2]
  246
+          @us.pop()
  247
+          @rx.remove()
  248
+          @lx.remove()
  249
+          @rx = @zone.rect(10 * (i - rect[2]) - 5, rect[0], rect[2] * 10 + 10, rect[0] - rect[1]).attr(
  250
+              "stroke-width": 2
  251
+              stroke: @color
  252
+          )
  253
+          @lx = @zone.r.path().moveTo(10 * (i - rect[2]) - 10, @zone.ymax - (parseInt(rect[0]) + parseInt(rect[1])) / 2).relatively().lineTo(rect[2] * 10 + 20, 0).attr(
  254
+              "stroke-width": 1
  255
+              stroke: "gray"
  256
+          ).attr(@zone.offset_attr)
  257
+          if attr
  258
+              @rx.attr attr
  259
+              @lx.attr attr
  260
+              @us.push @rx
  261
+              @us.push @lx
  262
+              @last_start = i - rect[2]
  263
+
  264
+class TradeSpring.Widget.Ellipse extends TradeSpring.Widget
  265
+  constructor: (@zone, @color, @name) ->
  266
+    @last_start = undefined
  267
+    @rx = undefined
  268
+    @lx = undefined
  269
+    @us = @zone.r.set()
  270
+    @zone.blanket.push @us
  271
+  render_item: (spec, i) ->
  272
+    if @rx and @last_start == i - spec[2]
  273
+      @us.pop()
  274
+      @rx.remove()
  275
+      @lx.remove()
249 276
     h = parseFloat(spec[0])
250 277
     l = parseFloat(spec[1])
251  
-    rx = zone.ellipse(10 * (i - spec[2] / 2), (h + l) / 2, (spec[2] / 2) * 10, (h - l) / 2).attr(
  278
+    @rx = @zone.ellipse(10 * (i - spec[2] / 2), (h + l) / 2, (spec[2] / 2) * 10, (h - l) / 2).attr(
252 279
       "stroke-width": 2
253  
-      stroke: color
  280
+      stroke: @color
254 281
     )
255  
-    lx = zone.rect(10 * (i - spec[2]) - 10, (parseInt(spec[0]) + parseInt(spec[1])) / 2, spec[2] * 10 + 20, 0.5).attr(
  282
+    @lx = @zone.rect(10 * (i - spec[2]) - 10, (parseInt(spec[0]) + parseInt(spec[1])) / 2, spec[2] * 10 + 20, 0.5).attr(
256 283
       "stroke-width": 1
257 284
       stroke: "gray"
258 285
       fill: "gray"
259 286
     )
260  
-    us.push rx
261  
-    us.push lx
262  
-    last_start = i - spec[2]
263  
-  init: (d) ->
264  
-    jQuery(d.values).each (idx) ->
265  
-      render_item this, d.start + idx  if this? and this[0]?
266  
-
267  
-  val: (d) ->
268  
-    render_item d.value, d.i  if d.value? and d.value[0]?
  287
+    @us.push @rx
  288
+    @us.push @lx
  289
+    @last_start = i - spec[2]
269 290
 
270 291
 window.mk_colorheat = (zone, mul) ->
271 292
   get_color = (val) ->
@@ -320,7 +341,7 @@ window.mk_annarrow = (zone, mul) ->
320 341
     zone.blanket.push item[0]
321 342
 
322 343
 class TradeSpring.Widget.SRLine extends TradeSpring.Widget
323  
-  constructor: (@zone, @colors = ['black', 'blue']) ->
  344
+  constructor: (@zone, @colors = ['black', 'blue'], @name) ->
324 345
     @pset = @zone.r.set()
325 346
     @eset = @zone.r.set()
326 347
     @zone.blanket.push @pset
@@ -388,26 +409,3 @@ class TradeSpring.Widget.SRLine extends TradeSpring.Widget
388 409
     @pset.push @px
389 410
     @last_price = price
390 411
     @last_dir = dir
391  
-
392  
-window.mk_candlebackground = (zone, color, name, base) ->
393  
-  base = zone.view.indicators[base].self
394  
-  render_item = (val, i) ->
395  
-    val = parseFloat(val)
396  
-    c = get_color(val)
397  
-    x = i * 10
398  
-    height = base.get(i) - val
399  
-    bar = zone.r.path().beginMulti().moveTo(x, zone.ymax - val).relatively().lineTo(0, -height).andUpdate().attr(
400  
-      opacity: 0.6
401  
-      "stroke-width": 10
402  
-      stroke: (if height > 0 then "green" else "red")
403  
-    ).attr(zone.offset_attr).toBack()
404  
-    zone.blanket.push bar
405  
-  get_color = (val) ->
406  
-    (if val > 0 then "red" else (if val < 0 then "green" else "yellow"))
407  
-
408  
-  init: (d) ->
409  
-    $(d.values).each (idx) ->
410  
-      render_item this, d.start + idx  if this?
411  
-
412  
-  val: (d) ->
413  
-    render_item d.value, d.i
68  coffee/tschart.coffee
@@ -14,6 +14,7 @@ class TradeSpring.Chart
14 14
         @tz ||= 'Asia/Taipei'
15 15
         @zones = []
16 16
         @indicators = {}
  17
+        @indicator_groups ?= {}
17 18
         @current_zoom = 10
18 19
         @canvas_width = 10 * @items_to_load
19 20
         @chart_view = $("<div>").css(
@@ -58,6 +59,11 @@ class TradeSpring.Chart
58 59
         @loaded_nb_items = @items_to_load
59 60
         @loaded_offset = @cnt - @items_to_load
60 61
 
  62
+        @date_label = $("<span/>").addClass("xlabel").addClass("cursor").css(
  63
+          position: "absolute"
  64
+          top: 5 + @x + @height
  65
+        ).appendTo(@holder)
  66
+
61 67
         @price_label = $("<span/>").addClass("ylabel").addClass("cursor").css(
62 68
           position: "absolute"
63 69
           left: 5 + @x + @width
@@ -82,6 +88,7 @@ class TradeSpring.Chart
82 88
         @chart_view.css "width", @width
83 89
         @canvas.css "left", @nb_items() * @current_zoom - @canvas_width * @current_zoom / 10
84 90
         @offset = @loaded_offset + @loaded_nb_items - @nb_items()
  91
+        @date_label.css "top", 5 + @x + @height
85 92
         @price_label.css "left", 5 + @x + @width
86 93
         @price_label_high.css "left", 5 + @x + @width
87 94
         @price_label_low.css "left", 5 + @x + @width
@@ -196,9 +203,16 @@ class TradeSpring.Chart
196 203
               e = e.originalEvent.touches[0]  if e.originalEvent.touches
197 204
               return true if !e
198 205
               scaled_y = e.pageY - zone.canvas_holder.offset().top
  206
+              scaled_x = e.pageX - zone.canvas_holder.offset().left
199 207
               offY = scaled_y / zone.nr_yscale - zone.nr_offset
  208
+              offX = e.pageX - @canvas.offset().left
  209
+              x = Math.floor(offX / @current_zoom + 0.5)
200 210
               hcursor.css "top", scaled_y
201 211
               y = @candle_zone.offset_to_val(offY)
  212
+
  213
+              if @candle_zone.data_set? && @candle_zone.data_set[x]?
  214
+                  datetime = @candle_zone.data_set[x][0]
  215
+                  @date_label.text(datetime).css left: e.pageX
202 216
               @price_label.text(y).css top: scaled_y
203 217
               true
204 218
 
@@ -357,22 +371,66 @@ class TradeSpring.Chart
357 371
         @on_view_change()
358 372
 
359 373
       indicator_bind: (name, zone, type, args...) ->
360  
-        cb = "mk_" + type.toLowerCase();
  374
+        cb = "mk_" + type.toLowerCase()
361 375
         arg0 = args.shift()
362 376
         doit = =>
363 377
           @indicators[name] = window[cb].apply(this, [zone, arg0, name].concat(args))
364  
-        doit();
365  
-        $(zone).bind('zone-reset', => doit());
  378
+        doit()
  379
+
  380
+        labelbox = $("<label/>").addClass("checkbox").attr("id", name).css(
  381
+            background: arg0
  382
+        ).appendTo($('#indicator_config'))
  383
+        $(labelbox).text(name)
  384
+        @indicators[name].label = $("<input type='checkbox' />").val(name).attr("id", name).attr('checked', true).appendTo($(labelbox))
  385
+
  386
+        indicator_spec = 'path.' + name.replace(/([\(\)])/g, "\\$1")
  387
+        @indicators[name].label.change ->
  388
+            if $(@).attr('checked')
  389
+                $(indicator_spec).show()
  390
+            else
  391
+                $(indicator_spec).hide()
  392
+
  393
+        $(zone).bind('zone-reset', => doit())
366 394
 
  395
+      indicator_bind_with_group: (name, zone, type, group, args...) ->
  396
+        @indicator_bind(name, zone, type, args...)
  397
+
  398
+        if @indicator_groups[group]?
  399
+            @indicator_groups[group].namelist.push name
  400
+        else
  401
+            labelbox = $("<label/>").addClass("checkbox").attr("id", group).css(
  402
+                background: args.shift()
  403
+            ).appendTo($('#indicator_group_config'))
  404
+            $(labelbox).text(group)
  405
+            @indicator_groups[group] = {
  406
+                label: $("<input type='checkbox' />").val(group).attr("id", group).attr('checked', true).appendTo($(labelbox))
  407
+
  408
+                namelist: [name]
  409
+            }
  410
+
  411
+        checkbox = 'input#' + group.replace(/([\(\)])/g, "\\$1")
  412
+        label = @indicator_groups[group].label
  413
+        label.change =>
  414
+            is_show_indicator = label.attr('checked')
  415
+            for tname in @indicator_groups[group].namelist
  416
+                indicator_spec = 'path.' + tname.replace(/([\(\)])/g, "\\$1")
  417
+                if is_show_indicator
  418
+                    $(indicator_spec).show()
  419
+                else
  420
+                    $(indicator_spec).hide()
367 421
 
368 422
       indicator_names: ->
369 423
         name for name of @indicators
370 424
 
371 425
       indicator_init: (name, d) ->
372 426
         @indicators[name].init(d)
  427
+        widget = @indicators[name].self
  428
+        label = @indicators[name].label
373 429
 
374 430
       indicator_pub: (name, d) ->
375 431
         @indicators[name].val(d)
  432
+        widget = @indicators[name].self
  433
+        label = @indicators[name].label
376 434
 
377 435
 class TradeSpring.Chart.Zone
378 436
       constructor: (opt)->
@@ -419,7 +477,7 @@ class TradeSpring.Chart.Zone
419 477
           $("span.ylabel.yaxis").remove()
420 478
           @ylabels = null
421 479
         =>
422  
-          $(@).trigger('zone-reset');
  480
+          $(@).trigger('zone-reset')
423 481
           oldblanket.hide()
424 482
           oldblanket.remove()
425 483
 
@@ -695,7 +753,7 @@ class TradeSpring.Chart.Zone
695 753
             'margin-left': -@view.width
696 754
           ).addClass("yaxis-line").appendTo(label)
697 755
 
698  
-          @ylabels[val] = label;
  756
+          @ylabels[val] = label
699 757
 
700 758
         that = this
701 759
         $("span.ylabel").each ->
4  lib/TradeSpring/Chart.pm
@@ -370,6 +370,10 @@ method allowed_indicators($session, $tf) {
370 370
     return {
371 371
         'SMA(9)' => { class => 'SMA',
372 372
                       args => { n => 9 } },
  373
+        'SMA(15)' => { class => 'SMA',
  374
+                      args => { n => 15 } },
  375
+        'SMA(30)' => { class => 'SMA',
  376
+                      args => { n => 30 } },
373 377
     }
374 378
 }
375 379
 
1  lib/TradeSpring/Chart/JSFiles.pm
@@ -17,6 +17,7 @@ sub files {
17 17
        jquery.cookie.js jquery.hotkeys.js
18 18
        jquery.dataTables.js jquery.ui.js
19 19
        jquery.contextMenu.js
  20
+       bootstrap-modal.js
20 21
        gen/tschart.js gen/tsdraw.js
21 22
        gen/tsorder.js gen/tschart-ui.js gen/tschart-widget.js );
22 23
 }
246  sass/tschart.sass
... ...
@@ -0,0 +1,246 @@
  1
+body
  2
+  background: -webkit-gradient(linear, left top, left bottom, from(rgba(0, 0, 0, 0.1)), to(rgba(0, 0, 0, 0.3)))
  3
+  position: relative
  4
+
  5
+div
  6
+  &#header
  7
+    position: absolute
  8
+    height: 10px
  9
+    left: 10px
  10
+    top: 0px
  11
+  &#trade-header
  12
+    height: 10px
  13
+    position: absolute
  14
+    right: 10px
  15
+    top: 0px
  16
+
  17
+#header .price.up, #trade-header .up
  18
+  color: red
  19
+
  20
+#header .price.down, #trade-header .down
  21
+  color: green
  22
+
  23
+#header .span
  24
+  margin: 0
  25
+
  26
+span
  27
+  &.xlabel.cursor
  28
+    -webkit-transform: rotateZ(-10deg)
  29
+    -webkit-transition-property: top
  30
+    -webkit-transition-duration: 0.1s
  31
+    -webkit-transition-timing-function: easeout
  32
+    -moz-transform: rotate(-10deg)
  33
+    -moz-transition-property: top
  34
+    -moz-transition-duration: 0.1s
  35
+    -moz-transition-timing-function: easeout
  36
+    background-color: orange
  37
+    color: white
  38
+    z-index: 9
  39
+  &.ylabel
  40
+    &.cursor
  41
+      -webkit-transform: rotateZ(-10deg)
  42
+      -webkit-transition-property: top
  43
+      -webkit-transition-duration: 0.1s
  44
+      -webkit-transition-timing-function: easeout
  45
+      -moz-transform: rotate(-10deg)
  46
+      -moz-transition-property: top
  47
+      -moz-transition-duration: 0.1s
  48
+      -moz-transition-timing-function: easeout
  49
+      background-color: orange
  50
+      color: white
  51
+      z-index: 9
  52
+    &.order
  53
+      color: white
  54
+      z-index: 2
  55
+      &.filled
  56
+        -webkit-transition-property: opacity
  57
+        -webkit-transition-duration: 5s
  58
+        -moz-transition-property: opacity
  59
+        -moz-transition-duration: 5s
  60
+        opacity: 0.1
  61
+    &.low
  62
+      -webkit-transform: rotateZ(-10deg)
  63
+      -webkit-box-reflect: below -1px -webkit-gradient(linear, 0% 0%, 0% 100%, from(transparent), to(rgba(255, 255, 255, 0.393))) 0 0 0 0 stretch stretch
  64
+      -moz-transform: rotate(-10deg)
  65
+      -moz-box-reflect: below -1px -moz-gradient(linear, 0% 0%, 0% 100%, from(transparent), to(rgba(255, 255, 255, 0.393))) 0 0 0 0 stretch stretch
  66
+      background-color: green
  67
+      color: white
  68
+      z-index: 1
  69
+    &.high
  70
+      -webkit-transform: rotateZ(-10deg)
  71
+      -webkit-box-reflect: above -1px -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(255, 255, 255, 0.393)), to(transparent)) 0 0 0 0 stretch stretch
  72
+      -moz-transform: rotate(-10deg)
  73
+      -moz-box-reflect: above -1px -moz-gradient(linear, 0% 0%, 0% 100%, from(rgba(255, 255, 255, 0.393)), to(transparent)) 0 0 0 0 stretch stretch
  74
+      background-color: red
  75
+      color: white
  76
+      z-index: 1
  77
+
  78
+div
  79
+  &.hcursor
  80
+    border-top-style: solid
  81
+    border-top-width: 1px
  82
+    height: 0px
  83
+    left: 0px
  84
+    position: absolute
  85
+    width: 100%
  86
+    cursor: crosshair
  87
+  &.vcursor
  88
+    border-left-style: solid
  89
+    border-left-width: 1px
  90
+    position: absolute
  91
+    width: 0px
  92
+    top: 0px
  93
+    cursor: crosshair
  94
+  &.order-line
  95
+    border-top-style: solid
  96
+    border-top-width: 1px
  97
+    height: 0px
  98
+    margin-top: -50%
  99
+    margin-left: -100%
  100
+    position: absolute
  101
+    width: 100%
  102
+  &.yaxis-line
  103
+    border-top-style: solid
  104
+    border-top-color: balack
  105
+    border-top-width: 1px
  106
+    height: 0px
  107
+    margin-top: -50%
  108
+    position: absolute
  109
+    opacity: 0.2
  110
+
  111
+span.order
  112
+  &.submitted div.order-line
  113
+    border-top-style: dotted
  114
+  &.pending div.order-line
  115
+    border-top-style: dashed
  116
+
  117
+div.infobox
  118
+  background: rgba(255, 255, 255, 0.59)
  119
+  text-shadow: white 3px 3px 3px
  120
+  color: black
  121
+  border: 1px solid black
  122
+  font-size: smaller
  123
+  width: 120px
  124
+  overflow: hidden
  125
+  padding: 5px
  126
+  position: absolute
  127
+  white-space: pre
  128
+  z-index: 2
  129
+
  130
+#holder
  131
+  border: black 1px solid
  132
+  margin: 0
  133
+  position: relative
  134
+  height: 600px
  135
+  left: 10px
  136
+  width: 800px
  137
+  -moz-user-select: none
  138
+  -khtml-user-select: none
  139
+  -webkit-user-select: none
  140
+  -o-user-select: none
  141
+
  142
+#connection-status
  143
+  position: absolute
  144
+  top: 0px
  145
+  right: 0px
  146
+  background-color: red
  147
+  &.connected
  148
+    background-color: #00ff00
  149
+    display: none
  150
+
  151
+#drawing-control
  152
+  display: none
  153
+
  154
+span.draw-prop
  155
+  padding: 1em
  156
+
  157
+div
  158
+  &.chat
  159
+    position: absolute
  160
+    right: 0px
  161
+    top: 0px
  162
+    width: 250px
  163
+  &.chat-messages
  164
+    width: 100%
  165
+    overflow-y: auto
  166
+    overflow-x: hidden
  167
+
  168
+input#chat
  169
+  width: 100%
  170
+  height: 100px
  171
+  font-size: 1.2em
  172
+
  173
+table#messages
  174
+  margin-top: 1em
  175
+  margin-right: 3em
  176
+  width: 100%
  177
+
  178
+tr.message
  179
+  height: 1em
  180
+
  181
+.chat-message
  182
+  width: 70%
  183
+  word-break: break-all
  184
+  .name
  185
+    font-weight: bold
  186
+
  187
+.user-list span
  188
+  margin-left: 0.5em
  189
+  margin-right: 0.5em
  190
+
  191
+span.cancel-order
  192
+  margin-left: 0.5em
  193
+  text-decoration: underline
  194
+  background: #ee0000
  195
+  border-color: #AAA black black #AAA
  196
+  border-style: solid
  197
+  border-width: 1px
  198
+  color: white
  199
+  cursor: pointer
  200
+  &:hover
  201
+    background: #ff0000
  202
+    border-color: black #aaa #aaa black
  203
+
  204
+.ui-layout-toggler-east span.content-closed
  205
+  -webkit-transform: rotate(90deg)
  206
+  -moz-transform: rotate(90deg)
  207
+  background: green
  208
+  color: orange
  209
+  margin-left: -20px
  210
+  width: 50px
  211
+
  212
+.contextMenu
  213
+  position: absolute
  214
+  z-index: 99999
  215
+  border: solid 1px #CCC
  216
+  background: #EEE
  217
+  padding: 0px
  218
+  margin: 0px
  219
+  display: none
  220
+  LI
  221
+    list-style: none
  222
+    padding: 0px
  223
+    margin: 0px
  224
+    overflow: hidden
  225
+  A
  226
+    color: #333
  227
+    text-decoration: none
  228
+    display: block
  229
+    line-height: 2em
  230
+    height: 2em
  231
+    background-position: 6px center
  232
+    background-repeat: no-repeat
  233
+    outline: none
  234
+    padding: 1px 5px
  235
+    padding-left: 28px
  236
+  LI
  237
+    &.hover A
  238
+      color: #FFF
  239
+      background-color: #3399FF
  240
+    &.disabled A
  241
+      color: #AAA
  242
+      cursor: default
  243
+    &.hover.disabled A
  244
+      background-color: transparent
  245
+    &.separator
  246
+      border-top: solid 1px #CCC
218  share/static/bootstrap-modal.js
... ...
@@ -0,0 +1,218 @@
  1
+/* =========================================================
  2
+ * bootstrap-modal.js v2.0.3
  3
+ * http://twitter.github.com/bootstrap/javascript.html#modals
  4
+ * =========================================================
  5
+ * Copyright 2012 Twitter, Inc.
  6
+ *
  7
+ * Licensed under the Apache License, Version 2.0 (the "License");
  8
+ * you may not use this file except in compliance with the License.
  9
+ * You may obtain a copy of the License at
  10
+ *
  11
+ * http://www.apache.org/licenses/LICENSE-2.0
  12
+ *
  13
+ * Unless required by applicable law or agreed to in writing, software
  14
+ * distributed under the License is distributed on an "AS IS" BASIS,
  15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16
+ * See the License for the specific language governing permissions and
  17
+ * limitations under the License.
  18
+ * ========================================================= */
  19
+
  20
+
  21
+!function ($) {
  22
+
  23
+  "use strict"; // jshint ;_;
  24
+
  25
+
  26
+ /* MODAL CLASS DEFINITION
  27
+  * ====================== */
  28
+
  29
+  var Modal = function (content, options) {
  30
+    this.options = options
  31
+    this.$element = $(content)
  32
+      .delegate('[data-dismiss="modal"]', 'click.dismiss.modal', $.proxy(this.hide, this))
  33
+  }
  34
+
  35
+  Modal.prototype = {
  36
+
  37
+      constructor: Modal
  38
+
  39
+    , toggle: function () {
  40
+        return this[!this.isShown ? 'show' : 'hide']()
  41
+      }
  42
+
  43
+    , show: function () {
  44
+        var that = this
  45
+          , e = $.Event('show')
  46
+
  47
+        this.$element.trigger(e)
  48
+
  49
+        if (this.isShown || e.isDefaultPrevented()) return
  50
+
  51
+        $('body').addClass('modal-open')
  52
+
  53
+        this.isShown = true
  54
+
  55
+        escape.call(this)
  56
+        backdrop.call(this, function () {
  57
+          var transition = $.support.transition && that.$element.hasClass('fade')
  58
+
  59
+          if (!that.$element.parent().length) {
  60
+            that.$element.appendTo(document.body) //don't move modals dom position
  61
+          }
  62
+
  63
+          that.$element
  64
+            .show()
  65
+
  66
+          if (transition) {
  67
+            that.$element[0].offsetWidth // force reflow
  68
+          }
  69
+
  70
+          that.$element.addClass('in')
  71
+
  72
+          transition ?
  73
+            that.$element.one($.support.transition.end, function () { that.$element.trigger('shown') }) :
  74
+            that.$element.trigger('shown')
  75
+
  76
+        })
  77
+      }
  78
+
  79
+    , hide: function (e) {
  80
+        e && e.preventDefault()
  81
+
  82
+        var that = this
  83
+
  84
+        e = $.Event('hide')
  85
+
  86
+        this.$element.trigger(e)
  87
+
  88
+        if (!this.isShown || e.isDefaultPrevented()) return
  89
+
  90
+        this.isShown = false
  91
+
  92
+        $('body').removeClass('modal-open')
  93
+
  94
+        escape.call(this)
  95
+
  96
+        this.$element.removeClass('in')
  97
+
  98
+        $.support.transition && this.$element.hasClass('fade') ?
  99
+          hideWithTransition.call(this) :
  100
+          hideModal.call(this)
  101
+      }
  102
+
  103
+  }
  104
+
  105
+
  106
+ /* MODAL PRIVATE METHODS
  107
+  * ===================== */
  108
+
  109
+  function hideWithTransition() {
  110
+    var that = this
  111
+      , timeout = setTimeout(function () {
  112
+          that.$element.off($.support.transition.end)
  113
+          hideModal.call(that)
  114
+        }, 500)
  115
+
  116
+    this.$element.one($.support.transition.end, function () {
  117
+      clearTimeout(timeout)
  118
+      hideModal.call(that)
  119
+    })
  120
+  }
  121
+
  122
+  function hideModal(that) {
  123
+    this.$element
  124
+      .hide()
  125
+      .trigger('hidden')
  126
+
  127
+    backdrop.call(this)
  128
+  }
  129
+
  130
+  function backdrop(callback) {
  131
+    var that = this
  132
+      , animate = this.$element.hasClass('fade') ? 'fade' : ''
  133
+
  134
+    if (this.isShown && this.options.backdrop) {
  135
+      var doAnimate = $.support.transition && animate
  136
+
  137
+      this.$backdrop = $('<div class="modal-backdrop ' + animate + '" />')
  138
+        .appendTo(document.body)
  139
+
  140
+      if (this.options.backdrop != 'static') {
  141
+        this.$backdrop.click($.proxy(this.hide, this))
  142
+      }
  143
+
  144
+      if (doAnimate) this.$backdrop[0].offsetWidth // force reflow
  145
+
  146
+      this.$backdrop.addClass('in')
  147
+
  148
+      doAnimate ?
  149
+        this.$backdrop.one($.support.transition.end, callback) :
  150
+        callback()
  151
+
  152
+    } else if (!this.isShown && this.$backdrop) {
  153
+      this.$backdrop.removeClass('in')
  154
+
  155
+      $.support.transition && this.$element.hasClass('fade')?
  156
+        this.$backdrop.one($.support.transition.end, $.proxy(removeBackdrop, this)) :
  157
+        removeBackdrop.call(this)
  158
+
  159
+    } else if (callback) {
  160
+      callback()
  161
+    }
  162
+  }
  163
+
  164
+  function removeBackdrop() {
  165
+    this.$backdrop.remove()
  166
+    this.$backdrop = null
  167
+  }
  168
+
  169
+  function escape() {
  170
+    var that = this
  171
+    if (this.isShown && this.options.keyboard) {
  172
+      $(document).on('keyup.dismiss.modal', function ( e ) {
  173
+        e.which == 27 && that.hide()
  174
+      })
  175
+    } else if (!this.isShown) {
  176
+      $(document).off('keyup.dismiss.modal')
  177
+    }
  178
+  }
  179
+
  180
+
  181
+ /* MODAL PLUGIN DEFINITION
  182
+  * ======================= */
  183
+
  184
+  $.fn.modal = function (option) {
  185
+    return this.each(function () {
  186
+      var $this = $(this)
  187
+        , data = $this.data('modal')
  188
+        , options = $.extend({}, $.fn.modal.defaults, $this.data(), typeof option == 'object' && option)
  189
+      if (!data) $this.data('modal', (data = new Modal(this, options)))
  190
+      if (typeof option == 'string') data[option]()
  191
+      else if (options.show) data.show()
  192
+    })
  193
+  }
  194
+
  195
+  $.fn.modal.defaults = {
  196
+      backdrop: true
  197
+    , keyboard: true
  198
+    , show: true
  199
+  }
  200
+
  201
+  $.fn.modal.Constructor = Modal
  202
+
  203
+
  204
+ /* MODAL DATA-API
  205
+  * ============== */
  206
+
  207
+  $(function () {
  208
+    $('body').on('click.modal.data-api', '[data-toggle="modal"]', function ( e ) {
  209
+      var $this = $(this), href
  210
+        , $target = $($this.attr('data-target') || (href = $this.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, '')) //strip for ie7
  211
+        , option = $target.data('modal') ? 'toggle' : $.extend({}, $target.data(), $this.data())
  212
+
  213
+      e.preventDefault()
  214
+      $target.modal(option)
  215
+    })
  216
+  })
  217
+
  218
+}(window.jQuery);
4,960  share/static/bootstrap.css
4960 additions, 0 deletions not shown
287  share/static/tschart.css
... ...
@@ -1,287 +0,0 @@
1  
-body {
2  
-    background: -webkit-gradient(linear, left top, left bottom, from(rgba(0,0,0,.1)), to(rgba(0,0,0,.3)));
3  
-    position: relative;
4  
-}
5  
-
6  
-div#header {
7  
-  position: absolute;
8  
-  height: 10px;
9  
-  left: 10px;  
10  
-  top: 0px;
11  
-}
12  
-
13  
-div#trade-header {
14  
-  height: 10px;
15  
-  position: absolute;
16  
-  right: 10px;
17  
-  top: 0px;
18  
-}
19  
-
20  
-#header .price.up, #trade-header .up {
21  
-  color: red;
22  
-}
23  
-
24  
-#header .price.down, #trade-header .down {
25  
-  color: green;
26  
-}
27  
-
28  
-#header .span {
29  
-    margin: 0;
30  
-
31  
-}
32  
-
33  
-span.ylabel.cursor {
34  
-    -webkit-transform: rotateZ(-10deg);
35  
-    -webkit-transition-property: top;
36  
-    -webkit-transition-duration: 0.1s;
37  
-    -webkit-transition-timing-function: easeout;
38  
-    -moz-transform: rotate(-10deg);
39  
-    -moz-transition-property: top;
40  
-    -moz-transition-duration: 0.1s;
41  
-    -moz-transition-timing-function: easeout;
42  
-    background-color: orange;
43  
-    color: white;
44  
-    z-index: 9;
45  
-}
46  
-
47  
-span.ylabel.order {
48  
-    color: white;
49  
-    z-index: 2;
50  
-}
51  
-
52  
-span.ylabel.order.filled {
53  
-    -webkit-transition-property: opacity;
54  
-    -webkit-transition-duration: 5s;
55  
-    -moz-transition-property: opacity;
56  
-    -moz-transition-duration: 5s;
57  
-    opacity: 0.1;
58  
-}
59  
-
60  
-span.ylabel.low {
61  
-    -webkit-transform: rotateZ(-10deg);
62  
-    -webkit-box-reflect: below -1px -webkit-gradient(linear, 0% 0%, 0% 100%, from(transparent), to(rgba(255, 255, 255, 0.392969))) 0 0 0 0 stretch stretch;
63  
-    -moz-transform: rotate(-10deg);
64  
-    -moz-box-reflect: below -1px -moz-gradient(linear, 0% 0%, 0% 100%, from(transparent), to(rgba(255, 255, 255, 0.392969))) 0 0 0 0 stretch stretch;
65  
-    background-color: green;
66  
-    color: white;
67  
-    z-index: 1;
68  
-}
69  
-
70  
-span.ylabel.high {
71  
-    -webkit-transform: rotateZ(-10deg);
72  
-    -webkit-box-reflect: above -1px -webkit-gradient(linear, 0% 0%, 0% 100%, from(rgba(255, 255, 255, 0.392969)), to(transparent)) 0 0 0 0 stretch stretch;
73  
-    -moz-transform: rotate(-10deg);
74  
-    -moz-box-reflect: above -1px -moz-gradient(linear, 0% 0%, 0% 100%, from(rgba(255, 255, 255, 0.392969)), to(transparent)) 0 0 0 0 stretch stretch;
75  
-    background-color: red;
76  
-    color: white;
77  
-    z-index: 1;
78  
-}
79  
-
80  
-div.hcursor {
81  
-    border-top-style: solid;
82  
-    border-top-width: 1px;
83  
-    height: 0px;
84  
-    left: 0px;
85  
-    position: absolute;
86  
-    width: 100%;
87  
-    cursor: crosshair;
88  
-}
89  
-
90  
-div.vcursor {
91  
-    border-left-style: solid;
92  
-    border-left-width: 1px;
93  
-    position: absolute;
94  
-    width: 0px;
95  
-    top: 0px;
96  
-    cursor: crosshair;
97  
-}
98  
-
99  
-div.order-line {
100  
-    border-top-style: solid;
101  
-    border-top-width: 1px;
102  
-    height: 0px;
103  
-    margin-top: -50%;
104  
-    margin-left: -100%;
105  
-    position: absolute;
106  
-    width: 100%;
107  
-}
108  
-
109  
-div.yaxis-line {
110  
-    border-top-style: solid;
111  
-    border-top-color: balack;
112  
-    border-top-width: 1px;
113  
-    height: 0px;
114  
-    margin-top: -50%;
115  
-    position: absolute;
116  
-    opacity: 0.2
117  
-}
118  
-
119  
-span.order.submitted div.order-line {
120  
-    border-top-style: dotted;
121  
-}
122  
-
123  
-span.order.pending div.order-line {
124  
-    border-top-style: dashed;
125  
-}
126  
-
127  
-div.infobox {
128  
-    background: rgba(255, 255, 255, 0.589844);
129  
-    text-shadow: white 3px 3px 3px;
130  
-    color: black;
131  
-    border: 1px solid black;
132  
-    font-size: smaller;
133  
-    width: 120px;
134  
-    overflow: hidden;
135  
-    padding: 5px;
136  
-    position: absolute;
137  
-    white-space: pre;
138  
-    z-index: 2;
139  
-}
140  
-
141  
-
142  
-#holder {
143  
-  border: black 1px solid;
144  
-  margin: 0;
145  
-  position: relative;
146  
-  height: 600px;
147  
-  left: 10px;
148  
-  width: 800px;
149  
-  -moz-user-select: none;
150  
-  -khtml-user-select: none;
151  
-  -webkit-user-select: none;
152  
-  -o-user-select: none;
153  
-}  
154  
-
155  
-#connection-status {
156  
-  position: absolute;
157  
-  top: 0px;
158  
-  right:0px;
159  
-  background-color: red;