~vthompson/stock-ticker-mobile-app/autopilot-packaging

« back to all changes in this revision

Viewing changes to javascript/graph.js

  • Committer: Victor Thompson
  • Date: 2013-09-27 03:09:33 UTC
  • mfrom: (53.2.10 stock-ticker-mobile-app)
  • Revision ID: victor.thompson@gmail.com-20130927030933-kouro8bw1lth2wed
Merge with trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
 
16
16
.pragma library
17
17
 
 
18
 
 
19
// We will be overiding String and Number to included a parseFix Getter method. This will keep our crazy JavaScript math working
 
20
// This will take a String or Number and output a float with a maxium number of decimal places but remove trailing 0's since js will have problems with that.
 
21
Number.prototype.__defineGetter__("parseFix", function() { return function(val) { return parseFloat( parseFloat(this).toFixed(val)) } })
 
22
String.prototype.__defineGetter__("parseFix", function() { return function(val) { return parseFloat(parseFloat(this).toFixed(val)) } })
 
23
 
 
24
Number.prototype.__defineGetter__("pointTwoOrSevenFive", function() { return this-parseInt(this) <= .50 ? parseInt(this)+.25 : parseInt(this)+.75  })
 
25
 
 
26
 
18
27
var Graph = function (args) {
19
28
 
20
29
    args = args || {}
84
93
    this.past_len = 0
85
94
    this.space_average = 0
86
95
    this.volume_adjust = 0
 
96
    this.chartWidth = 0 // Will be the graph width minus the padding
87
97
    this.min_value = 0
88
98
    this.volume_size = 0
89
99
    this.value_adjust = 0
91
101
    this.volume_bar_size = 0.13 // Percent of the graph height
92
102
    this.chartLineTop = 0
93
103
    this.chartLineBottom = 0
94
 
    this.register();
 
104
    this.register()
95
105
}
96
106
 
97
107
Chart.prototype = {
119
129
        // 1 is x position
120
130
        dayInfo.push((curr * this.space_average)+this.graph.options.leftPadding)
121
131
        // 2 is y position
122
 
        dayInfo.push(Math.round((this.graph.options.height-this.volume_size-graphBottom)-(parseFloat(this.listModel.get(this.past_len-curr).close-this.min_value)*this.value_adjust)))
 
132
        dayInfo.push(Math.round((this.graph.options.height-this.volume_size-graphBottom)-((this.listModel.get(this.past_len-curr).close-this.min_value)*this.value_adjust).parseFix(12)))
123
133
        return dayInfo
124
134
    },
125
135
    draw: function () {
126
136
 
127
137
        if ( ! this.graph.ctx ) return false
128
138
 
129
 
        this.char_pixel_size = this.graph.ctx.measureText('M').width+5 // S very close calculation of the text chartacter height
130
 
        //this.char_pixel_height = this.graph.ctx.measureText('M').width
131
 
        //this.char_pixel_width = this.graph.ctx.measureText('8').width
132
 
 
 
139
        this.digit_pixel_size = this.graph.ctx.measureText('M').width.parseFix(2)+4 // 5 very close calculation of the digit text chartacter width and height
 
140
        this.chartWidth = this.graph.options.width-this.graph.options.leftPadding-this.graph.options.rightPadding
133
141
        this.past_len = this.listModel.count-1
134
 
        this.space_average = parseFloat(((this.graph.options.width-(this.graph.options.leftPadding+this.graph.options.rightPadding))/this.past_len).toFixed(3))
 
142
        this.space_average = (this.chartWidth/this.past_len).parseFix(3)
135
143
 
136
144
        if ( this.past_len <= 0 ) {
137
145
             this.graph.ctx.clearRect(0, 0, this.graph.options.width, this.graph.options.height)
145
153
        this.min_value = 10000000 // This is a "this" so it can be used in the getDay member function
146
154
 
147
155
        for ( var x = this.past_len; x >= 0;  x-- ) {
148
 
            sn.day_volume = parseFloat(this.listModel.get(x).volume)
149
 
            sn.day_close = parseFloat(this.listModel.get(x).close)
 
156
            sn.day_volume = (this.listModel.get(x).volume).parseFix(2)
 
157
            sn.day_close = (this.listModel.get(x).close).parseFix(2)
150
158
            if ( sn.day_volume > sn.max_volume ) sn.max_volume = sn.day_volume
151
159
            if ( sn.day_close > sn.max_close_high ) sn.max_close_high = sn.day_close // Get the largest trade value
152
160
            if ( sn.day_close < sn.max_close_low ) sn.max_close_low = sn.day_close   // Get the smallest trade value
153
161
 
154
162
            if ( this.graph.options.showDayRange === "yes" ) {
155
 
                sn.day_high = parseFloat(this.listModel.get(x).high)
156
 
                sn.day_low = parseFloat(this.listModel.get(x).low)
 
163
                sn.day_high = (this.listModel.get(x).high).parseFix(2)
 
164
                sn.day_low = (this.listModel.get(x).low).parseFix(2)
157
165
                // These are use when the range bars are active to fit the chart into the demensions
158
166
                if ( sn.day_low < this.min_value ) this.min_value = sn.day_low  // Get the smallest trade range value
159
167
                if ( sn.day_high > sn.max_value ) sn.max_value = sn.day_high  // Get the largest trade range value
160
168
 
161
 
                sn.price_length = (parseFloat(sn.max_close_low).toFixed(2).length-1) * this.char_pixel_size  // This caclulates the length in pixels of the left trade amount text
162
 
                if ( ((this.past_len-x)*this.space_average)+this.graph.options.leftPadding < sn.price_length ) {  // This sets the text down from the day range if nessesary
 
169
                if ( ((this.past_len-x)*this.space_average)+this.graph.options.leftPadding < this.graph.ctx.measureText(parseFloat(sn.max_close_low).toFixed(2)).width.toString() ) {  // This sets the text down from the day range if nessesary
163
170
                    if ( this.min_value < sn.max_close_low  ) sn.max_close_low_beg = this.min_value // We do not want the range bar to overlap the text
164
171
                    if ( sn.max_value > sn.max_close_high_beg ) sn.max_close_high_beg = sn.max_value
165
172
                }
179
186
            else sn.max_close_low_beg = 0
180
187
        }
181
188
 
182
 
        this.min_value = parseFloat(this.min_value).toFixed(2)
 
189
        this.min_value = (this.min_value).parseFix(2)
183
190
 
184
 
        for ( var o in sn ) // We wnat all 2 decimal rounded numbers
185
 
            sn[o] = parseFloat(sn[o]).toFixed(2)
 
191
        for ( var o in sn ) // We want all numbers to be ready for math! Yaae
 
192
            sn[o] = (sn[o]).parseFix(2)
186
193
 
187
194
        this.volume_size = Math.round(this.graph.options.height * this.volume_bar_size)
188
195
        this.volume_adjust = (this.volume_size/sn.max_volume).toFixed(12)
189
196
 
190
 
        var graph_bottom = this.graph.options.bottomPadding+this.char_pixel_size*2
191
 
        this.value_adjust = (this.graph.options.height-this.graph.options.topPadding-this.volume_size-graph_bottom-this.char_pixel_size) / (sn.max_value-this.min_value)
 
197
        var graph_bottom = this.graph.options.bottomPadding+this.digit_pixel_size*2
 
198
        this.value_adjust = (this.graph.options.height-this.graph.options.topPadding-this.volume_size-graph_bottom-this.digit_pixel_size) / (sn.max_value-this.min_value)
192
199
 
193
200
        this.graph.ctx.clearRect(0,0,this.graph.options.width, this.graph.options.height)
194
201
 
196
203
        sn.day_high = 0 //Re-using this varible here
197
204
        sn.day_low = 0  //Re-using this varible here
198
205
        sn.day_close = 0//Re-using this varible here
199
 
        var day_open = 0, text_pos = 0, date_text = ""
200
 
 
201
 
        var oneShot = false, dateSpacing, dateTextLength, dateFormat = ''
202
 
 
203
 
        var dateArray = this.listModel.get(this.past_len).date.split('-')
204
 
        if ( parseInt(dateArray[1]) < parseInt(new Date().getMonth()) )  // This will find if the chart goes back from this month and year for text display
 
206
 
 
207
        var dateSpacing = 10, // Approximate spacing (in pixels) between the dates
 
208
                dateFormat = '',
 
209
                dateSpace = 0, // Do not change this
 
210
                day_open = 0,
 
211
                text_pos = 0,
 
212
                date_text = "",
 
213
                dateStart = 0
 
214
 
 
215
        var keepSpace = 0, oldVolume = []
 
216
        var dateArray = this.listModel.get(this.past_len).date.split('-'),
 
217
            slashLen = this.graph.ctx.measureText('/').width,
 
218
            digitLen = this.graph.ctx.measureText('2').width
 
219
 
 
220
        var dateTextLength = digitLen*2
 
221
        if ( parseInt(dateArray[1]) < parseInt(new Date().getMonth()) )  { // This will find if the chart goes back from this month and year for text display
205
222
            dateFormat = 'month'
206
 
        if ( parseInt(dateArray[0]) < parseInt(new Date().getFullYear()) )
 
223
            dateTextLength = digitLen*4+slashLen
 
224
            // The text looks better when the month format is centered
 
225
            dateStart = Math.floor((this.chartWidth+dateSpacing) / (dateTextLength+dateSpacing))
 
226
            dateStart = Math.round((this.chartWidth+dateSpacing - dateStart*(dateTextLength+dateSpacing)) / 2)
 
227
 
 
228
        }
 
229
        if ( parseInt(dateArray[0]) < parseInt(new Date().getFullYear()) ) {
207
230
            dateFormat = 'year'
 
231
            dateTextLength = digitLen*6+slashLen*2
 
232
            // The text looks better when the year format is centered
 
233
            dateStart = Math.floor((this.chartWidth+dateSpacing) / (dateTextLength+dateSpacing))
 
234
            dateStart = Math.round((this.chartWidth+dateSpacing - dateStart*(dateTextLength+dateSpacing)) / 2)
 
235
        }
208
236
 
209
237
        var xv = this.graph.options.leftPadding, ff = false
210
238
 
 
239
        // Graph trade line
211
240
        this.graph.ctx.beginPath()
212
 
        xv = this.graph.options.leftPadding
 
241
        this.graph.ctx.strokeStyle = this.chartLineColor
213
242
        this.graph.ctx.moveTo(xv, this.graph.options.height-this.volume_size-graph_bottom )
214
243
 
215
244
        for ( var i = this.past_len; i >= 0;  i-- ) {
216
 
 
217
 
            // Graph trade line
218
 
            if ( !ff ) ff = (this.graph.options.height-this.volume_size-graph_bottom)-(parseFloat(this.listModel.get(i).close-this.min_value)*this.value_adjust).toFixed(2)
219
 
            ff = (this.graph.options.height-this.volume_size-graph_bottom)-(parseFloat(this.listModel.get(i).close-this.min_value)*this.value_adjust).toFixed(2)
 
245
            if ( !ff ) ff = (this.graph.options.height-this.volume_size-graph_bottom)-((this.listModel.get(i).close-this.min_value)*this.value_adjust).parseFix(2)
 
246
            ff = (this.graph.options.height-this.volume_size-graph_bottom)-((this.listModel.get(i).close-this.min_value)*this.value_adjust).parseFix(2)
220
247
 
221
248
            this.graph.ctx.lineTo(xv, ff )
222
 
            this.graph.ctx.strokeStyle = this.chartLineColor
223
 
            this.graph.ctx.stroke();
224
 
            // ----------------------------
225
249
            xv += this.space_average
226
250
        }
227
251
 
228
252
        this.graph.ctx.lineTo(xv-this.space_average, this.graph.options.height-this.volume_size-graph_bottom )
229
 
 
 
253
        this.graph.ctx.closePath()
230
254
        this.graph.ctx.stroke()
 
255
        // ----------------------------
 
256
 
231
257
 
232
258
        if ( this.graph.options.showDayRange !== "yes" ) {
233
 
            this.graph.ctx.closePath()
234
259
            this.graph.ctx.fillStyle = this.graph.ctx.createLinearGradient(this.graph.options.width-this.graph.options.rightPadding, 0,
235
260
            this.graph.options.width-this.graph.options.rightPadding, this.graph.options.height-this.volume_size-graph_bottom).addColorStop(0,"#A1A1A1").addColorStop(1,"#ededed")
236
261
            this.graph.ctx.fill()
239
264
        xv = this.graph.options.leftPadding
240
265
 
241
266
        for ( var i = this.past_len; i >= 0;  i-- ) {
242
 
            day_open = parseFloat(this.listModel.get(i).open)
243
 
            sn.day_close = parseFloat(this.listModel.get(i).close)
 
267
            day_open = (this.listModel.get(i).open).parseFix(2)
 
268
            sn.day_close = (this.listModel.get(i).close).parseFix(2)
244
269
 
245
270
            // Graph day range
246
271
            if ( this.graph.options.showDayRange === "yes" ) {
247
 
                sn.day_low = (this.graph.options.height-this.volume_size-graph_bottom)-(parseFloat(this.listModel.get(i).low-this.min_value)*this.value_adjust).toFixed(2)
248
 
                sn.day_high = (this.graph.options.height-this.volume_size-graph_bottom)-(parseFloat(this.listModel.get(i).high-this.min_value)*this.value_adjust).toFixed(2)
249
 
                this.graph.ctx.beginPath()
 
272
                sn.day_low = (this.graph.options.height-this.volume_size-graph_bottom)-((this.listModel.get(i).low-this.min_value)*this.value_adjust).parseFix(2)
 
273
                sn.day_high = (this.graph.options.height-this.volume_size-graph_bottom)-((this.listModel.get(i).high-this.min_value)*this.value_adjust).parseFix(2)
250
274
                if ( day_open < sn.day_close ) {  // Going up (green)
251
275
                    this.graph.ctx.strokeStyle = "#33512A"
252
276
                    this.graph.ctx.moveTo(xv, sn.day_low)
261
285
            //-----------------------------
262
286
 
263
287
            // Graph volume bar
264
 
            this.graph.ctx.beginPath();
265
 
            this.graph.ctx.strokeStyle = "#0C276D" // this.graph.options.volumeLineColor"
266
 
            this.graph.ctx.moveTo(xv, this.graph.options.height-graph_bottom+this.char_pixel_size)
267
 
            this.graph.ctx.lineTo(xv, this.graph.options.height-graph_bottom+this.char_pixel_size- (Math.round(parseFloat(this.listModel.get(i).volume)*this.volume_adjust)))
268
 
            this.graph.ctx.fillStyle = this.graph.options.volumeLineColor
269
 
            this.graph.ctx.stroke();
 
288
 
 
289
            keepSpace += this.space_average
 
290
            oldVolume.push(this.graph.options.height-graph_bottom+this.digit_pixel_size- (Math.round(parseFloat(this.listModel.get(i).volume)*this.volume_adjust)))
 
291
 
 
292
            if ( keepSpace  > 2.5 ) {
 
293
                keepSpace = 0
 
294
                this.graph.ctx.beginPath()
 
295
                this.graph.ctx.strokeStyle = this.graph.options.volumeLineColor
 
296
                this.graph.ctx.moveTo((xv).pointTwoOrSevenFive, this.graph.options.height-graph_bottom+this.digit_pixel_size)
 
297
                this.graph.ctx.lineTo((xv).pointTwoOrSevenFive, (oldVolume.map(function(val, ind, ele){this.aveVolume+=val;return this.aveVolume;},{aveVolume: 0}).pop()/oldVolume.length).parseFix(1))
 
298
                this.graph.ctx.closePath()
 
299
                this.graph.ctx.stroke()
 
300
                oldVolume = []
 
301
 
 
302
            }
 
303
 
270
304
            // ----------------------------
271
305
 
272
306
            // Graph bottom date text
273
 
            this.graph.ctx.fillStyle = this.graph.options.dateTextColor
274
 
 
275
 
            dateArray = this.listModel.get(i).date.split('-')
276
 
            if ( !dateFormat ) {
277
 
                date_text = dateArray[2].toString()
278
 
                dateSpacing = 10
279
 
            }
280
 
            else if ( dateFormat === 'month' ) {
281
 
                date_text = dateArray[1]+"/"+dateArray[2]
282
 
                dateSpacing = 10
283
 
            }
284
 
            else if ( dateFormat === 'year' ) {
285
 
                //date_text = this.listModel.get(i).date.split('-')  // I wonder how thorough the gargage collection is in qt?
286
 
                //date_text = date_text[1]+"/"+date_text[2]+"/"+date_text[0].substr(2)
287
 
                date_text = dateArray[1]+"/"+dateArray[2]+"/"+dateArray[0].substr(2)
288
 
                dateSpacing = 10
289
 
            }
290
 
 
291
 
            dateTextLength = date_text.length*this.char_pixel_size
292
 
            if ( ( text_pos >= dateTextLength/4 && !oneShot ) || ( text_pos >= dateTextLength/2+dateSpacing && i*this.space_average > dateTextLength/4 ) ) {
 
307
                                                                  // Make sure there is enough space left to put text
 
308
            if ( text_pos >= dateTextLength/2+dateSpace+dateStart && i*this.space_average+6 > dateTextLength/2 ) {
 
309
                dateSpace = dateSpacing
 
310
                dateStart = dateTextLength/2
293
311
                text_pos = 0
294
 
                oneShot = true
 
312
 
 
313
                dateArray = this.listModel.get(i).date.split('-')
 
314
                if ( !dateFormat )
 
315
                    date_text = dateArray[2].toString()
 
316
                else if ( dateFormat === 'month' )
 
317
                    date_text = dateArray[1]+"/"+dateArray[2]
 
318
                else if ( dateFormat === 'year' )
 
319
                    date_text = dateArray[1]+"/"+dateArray[2]+"/"+dateArray[0].substr(2)
 
320
 
 
321
                this.graph.ctx.fillStyle = this.graph.options.dateTextColor
295
322
                this.graph.ctx.textBaseline = 'bottom'
296
 
                this.graph.ctx.fillText(date_text, xv-dateTextLength/4, this.graph.options.height)
 
323
                this.graph.ctx.fillText(date_text, xv-dateTextLength/2, this.graph.options.height)
297
324
            }
298
325
            // ----------------------------
299
326
 
300
327
            text_pos += this.space_average
301
328
            xv += this.space_average
302
329
        }
303
 
 
304
 
 
305
 
        this.graph.ctx.beginPath();
 
330
        this.graph.ctx.beginPath()
306
331
        this.graph.ctx.strokeStyle = this.graph.options.tradePointColor
307
332
        this.graph.ctx.fillStyle = this.graph.options.tradePointColor
308
333
        // The top chart line and trade text (high)
320
345
        this.graph.ctx.lineTo(this.graph.options.width-this.graph.options.rightPadding, this.chartLineBottom);
321
346
        this.graph.ctx.textBaseline = 'top'
322
347
        this.graph.ctx.fillText(sn.max_close_low, this.graph.options.leftPadding, this.chartLineBottom+Math.round(sn.max_close_low_beg*this.value_adjust)+1 )
323
 
        this.graph.ctx.stroke();
 
348
        this.graph.ctx.closePath()
 
349
        this.graph.ctx.stroke()
 
350
 
 
351
        this.graph.ctx.beginPath()
 
352
 
 
353
        this.graph.ctx.strokeStyle = this.graph.ctx.createLinearGradient(this.graph.options.width-this.graph.options.rightPadding, 0,
 
354
                                                                         this.graph.options.width-this.graph.options.rightPadding, this.graph.options.height-this.volume_size-graph_bottom).addColorStop(0,"#2e2e2e").addColorStop(1,"#ededed")
 
355
        this.graph.ctx.moveTo(this.graph.options.width-this.graph.options.rightPadding, (this.graph.options.height-this.volume_size-graph_bottom)-((this.listModel.get(0).close-this.min_value)*this.value_adjust).parseFix(2))
 
356
        this.graph.ctx.lineTo(this.graph.options.width-this.graph.options.rightPadding, this.chartLineBottom)
 
357
 
 
358
 
 
359
        this.graph.ctx.moveTo(this.graph.options.leftPadding, (this.graph.options.height-this.volume_size-graph_bottom)-((this.listModel.get(this.past_len).close-this.min_value)*this.value_adjust).parseFix(2))
 
360
        this.graph.ctx.lineTo(this.graph.options.leftPadding, this.chartLineBottom)
 
361
 
 
362
        this.graph.ctx.closePath()
 
363
        this.graph.ctx.stroke()
324
364
 
325
365
    } // Endo draw()
326
366
 
328
368
 
329
369
 
330
370
var graph = new Graph({ topPadding: 20, rightPadding: 5, bottomPadding: 0, leftPadding: 5, showDayRange: "no",
331
 
                        dateTextColor: "#3D1D04", tradePointColor: "#434343", volumeLineColor: "#2C2832"
 
371
                        dateTextColor: "#33495c", tradePointColor: "#434343", volumeLineColor: "#162633"
332
372
                      })
333
373
 
334
374
var newChart = function(listModel) {