<!DOCTYPE html>
<html lang="en">
<head>
    <!--
    This file acts as a simple benchmark to be executed in (phone) browsers.
    It compiles tictoc.js (embedded) below using the generated compiler.
    Just share this folder and point your browser to this file.
    -->
    <meta charset="UTF-8">
    <title>js_tooling Benchmark page</title>
    <script src="js_tooling.js"></script>
    <script>
        // enable logging
        JS_TOOLING_LOGGING = true;
    </script>
    <script>
        var tictoc = "\r\n\/*global Rocky:true *\/\r\n\/*eslint max-len: [2, 100, 4]*\/\r\n\r\nif (typeof (Rocky) === \'undefined\') {\r\n  Rocky = {};\r\n}\r\n\r\n(function() {\r\n  Rocky.WebAPIBinding = function(canvasElement) {\r\n    var _private = {};\r\n    this._private = _private;\r\n\r\n    _private.subscriptions = {\r\n      emit: function(name, event) {\r\n        var f = this[name];\r\n        if (typeof f === \'function\') {\r\n          f(event);\r\n        }\r\n      }\r\n    };\r\n\r\n    \/\/ -----------------\r\n    if (typeof Rocky.bindCanvas === \'function\') {\r\n      _private.binding = Rocky.bindCanvas(canvasElement);\r\n    } else if (typeof Rocky.bindNative === \'function\') {\r\n      _private.binding = Rocky.bindNative(canvasElement);\r\n    } else {\r\n      throw new Error(\'cannot create a binding\');\r\n    }\r\n    _private.binding.update_proc = function(ctx, bounds) {\r\n      var ctx2D = new Rocky.CanvasRenderingContext2D(_private.binding, ctx, bounds);\r\n      _private.subscriptions.emit(\'draw\', {context: ctx2D});\r\n    };\r\n    this.requestDraw = function() {\r\n      _private.binding.mark_dirty();\r\n    };\r\n    \/\/ -----------------\r\n  };\r\n\r\n  Rocky.WebAPIBinding.prototype.on = function(event, callback) {\r\n    \/\/ workaround for speed reasons: this is a poor shadow of a real event emitter\u2026\r\n    if ([\'draw\', \'minutechange\'].indexOf(event) < 0) {\r\n      throw new Error(\'unknown event \' + event);\r\n    }\r\n    if (typeof this._private.subscriptions[event] !== \'undefined\') {\r\n      throw new Error(\'event can only be bound once \' + event);\r\n    }\r\n\r\n    this._private.subscriptions[event] = callback.bind(this);\r\n\r\n    if (event === \'minutechange\') {\r\n      var tickService = new Rocky.TickService(callback);\r\n      tickService.schedule();\r\n    }\r\n  };\r\n\r\n})();\r\n\r\n\/*global Rocky:true *\/\r\n\r\nif (typeof (Rocky) === \'undefined\') {\r\n  Rocky = {};\r\n}\r\n\r\n(function() {\r\n\r\n  Rocky.CanvasRenderingContext2D = function(rockyBinding, ctx, bounds) {\r\n    this.binding = rockyBinding;\r\n    this.cPtr = ctx;\r\n    this.canvas = {\r\n      clientWidth: bounds.w,\r\n      clientHeight: bounds.h\r\n    };\r\n    this.beginPath();\r\n  };\r\n\r\n  Rocky.CanvasRenderingContext2D.prototype.fillRect = function(x, y, width, height) {\r\n    this.binding.graphics_fill_rect(this.cPtr, [x, y, width, height], 0, 0);\r\n  };\r\n\r\n  Object.defineProperties(Rocky.CanvasRenderingContext2D.prototype, {\r\n    \'fillStyle\': {\r\n      get: function() {\r\n        return \'not implement, yet\';\r\n      },\r\n      set: function(value) {\r\n        var color = Rocky.GColorFromStyle(value);\r\n        this.binding.graphics_context_set_fill_color(this.cPtr, color);\r\n      }\r\n    },\r\n    \'strokeStyle\': {\r\n      get: function() {\r\n        return \'not implement, yet\';\r\n      },\r\n      set: function(value) {\r\n        var color = Rocky.GColorFromStyle(value);\r\n        this.binding.graphics_context_set_stroke_color(this.cPtr, color);\r\n      }\r\n    },\r\n    \'lineWidth\': {\r\n      get: function() {\r\n        return \'not implement, yet\';\r\n      },\r\n      set: function(value) {\r\n        this.binding.graphics_context_set_stroke_width(this.cPtr, value);\r\n      }\r\n    }\r\n  });\r\n\r\n  Rocky.CanvasRenderingContext2D.prototype.beginPath = function() {\r\n    this.currentPath = new Rocky.Path2D();\r\n  };\r\n\r\n  Rocky.CanvasRenderingContext2D.prototype.stroke = function(path) {\r\n    path = path || this.currentPath;\r\n    \/\/ TODO: implement this fully\r\n    this.binding.graphics_draw_line(this.cPtr, path.p0, path.p1);\r\n  };\r\n\r\n  \/\/ forward path calls to current Path2D instance\r\n  [\'moveTo\', \'lineTo\'].forEach(function(s) {\r\n    Rocky.CanvasRenderingContext2D.prototype[s] = function() {\r\n      var f = this.currentPath[s];\r\n      f.apply(this.currentPath, arguments);\r\n    };\r\n  });\r\n})();\r\n\r\n\/*global Rocky:true *\/\r\n\r\nif (typeof (Rocky) === \'undefined\') {\r\n  Rocky = {};\r\n}\r\n\r\n(function() {\r\n  var colorNames = {\r\n    black: 0xC0,\r\n    red: 0xF0,\r\n    white: 0xFF\r\n  };\r\n\r\n  Rocky.GColorFromStyle = function(style) {\r\n    \/\/ TODO: implement fully\r\n    return colorNames[style];\r\n  };\r\n})();\r\n\/*global Rocky:true *\/\r\n\r\nif (typeof (Rocky) === \'undefined\') {\r\n  Rocky = {};\r\n}\r\n\r\n(function() {\r\n  Rocky.Path2D = function() {\r\n  };\r\n\r\n  var rounded = function(v) {\r\n    return Math.round(v * 8) \/ 8;\r\n  };\r\n\r\n  \/\/ TODO: implement this correctly\r\n  Rocky.Path2D.prototype.moveTo = function(x, y) {\r\n    this.p0 = [rounded(x), rounded(y)];\r\n  };\r\n  Rocky.Path2D.prototype.lineTo = function(x, y) {\r\n    this.p1 = [rounded(x), rounded(y)];\r\n  };\r\n\r\n})();\r\n\/*global Rocky:true *\/\r\n\r\nif (typeof (Rocky) === \'undefined\') {\r\n  Rocky = {};\r\n}\r\n\r\n(function() {\r\n  function clockwiseRad(fraction) {\r\n    \/\/ TODO: figure out if this is actually correct orientation for Canvas APIs\r\n    return (1.5 - fraction) * 2 * Math.PI;\r\n  }\r\n\r\n  Rocky.WatchfaceHelper = function(date) {\r\n    date = date || new Date();\r\n    var secondFraction = date.getSeconds() \/ 60;\r\n    var minuteFraction = (date.getMinutes()) \/ 60;\r\n    var hourFraction = (date.getHours() % 12 + minuteFraction) \/ 12;\r\n    this.secondAngle = clockwiseRad(secondFraction);\r\n    this.minuteAngle = clockwiseRad(minuteFraction);\r\n    this.hourAngle = clockwiseRad(hourFraction);\r\n  };\r\n})();\r\n\/*global Rocky:true *\/\r\n\r\nif (typeof (Rocky) === \'undefined\') {\r\n  Rocky = {};\r\n}\r\n\r\n(function() {\r\n  Rocky.TickService = function(listener) {\r\n    this.listener = listener;\r\n  };\r\n\r\n  Rocky.TickService.Events = {\r\n    SecondChange: \'secondchange\',\r\n    MinuteChange: \'minutechange\',\r\n    HourChange: \'hourchange\',\r\n    DayChange: \'daychange\'\r\n  };\r\n\r\n  var segmentSizes = [\r\n    [\'secondchange\', 1000],\r\n    [\'minutechange\', 1000 * 60],\r\n    [\'hourchange\', 1000 * 60 * 60],\r\n    [\'daychange\', 1000 * 60 * 60 * 24]\r\n  ];\r\n\r\n  Rocky.TickService.prototype.eventObject = function(date) {\r\n    var now = date.getTime();\r\n    var result = {date: date};\r\n    for (var i = 0; i < segmentSizes.length; i++) {\r\n      var segment = segmentSizes[i];\r\n      var eventName = segment[0];\r\n      var segmentSize = segment[1];\r\n      var msUntilEndOfSegment = segmentSize - now % segmentSize;\r\n      result[eventName] = msUntilEndOfSegment === 0;\r\n    }\r\n\r\n    return result;\r\n  };\r\n\r\n  Rocky.TickService.prototype.schedule = function() {\r\n    var now = new Date();\r\n\r\n    var event = this.eventObject(now);\r\n    this.listener(event);\r\n\r\n    \/\/ we\'re iterating from the smaller to the larger segments\r\n    \/\/ which is ok (as they are multiples)\r\n    \/\/ we fire an event for the smallest segment\r\n    var nowMs = now.getTime();\r\n    \/\/ HACK! always assumes minute minute change!\r\n\r\n    var segment = segmentSizes[1];\r\n    var eventName = segment[0];\r\n    var segmentSize = segment[1];\r\n\r\n    var msUntilEndOfSegment = segmentSize - nowMs % segmentSize;\r\n\r\n    \/\/ prepare the passed date object to reflect the future date\r\n    var futureDate = new Date(nowMs + msUntilEndOfSegment);\r\n    this.timeoutId = setTimeout(function() {\r\n      this.listener(eventName, this.eventObject(futureDate));\r\n      this.schedule();\r\n    }.bind(this), msUntilEndOfSegment);\r\n  };\r\n})();\r\nvar rocky = new Rocky.WebAPIBinding();\r\n\/*global rocky, Rocky:false *\/\r\n\/\/ in the future, we will replace the singleton\r\n\/\/ `rocky` as well as the namespace `Rocky`, e.g.\r\n\/\/ `Rocky.tween` and `Rocky.WatchfaceHelper` with modules\r\n\r\n\/\/ book keeping so that we can easily animate the two hands for the watchface\r\n\/\/ .scale\/.angle are updated by tween\/event handler (see below)\r\nvar renderState = {\r\n  minute: {style: \'white\', scale: 0.80, angle: 0},\r\n  hour: {style: \'red\', scale: 0.51, angle: 0}\r\n};\r\n\r\n\/\/ helper function for the draw function (see below)\r\n\/\/ extracted as a standalone function to satisfy common believe in efficient JS code\r\n\/\/ TODO: verify that this has actually any effect on byte code level\r\nvar drawHand = function(handState, ctx, cx, cy, maxRadius) {\r\n  ctx.lineWidth = 8;\r\n  ctx.strokeStyle = handState.style;\r\n  ctx.beginPath();\r\n  ctx.moveTo(cx, cy);\r\n  ctx.lineTo(cx + Math.sin(handState.angle) * handState.scale * maxRadius,\r\n             cy + Math.cos(handState.angle) * handState.scale * maxRadius);\r\n  ctx.stroke();\r\n};\r\n\r\n\/\/ the \'draw\' event is being emitted after each call to rocky.requestDraw() but\r\n\/\/ at most once for each screen update, even if .requestDraw() is called frequently\r\n\/\/ the \'draw\' event might also fire at other meaningful times (e.g. upon launch)\r\nrocky.on(\'draw\', function(drawEvent) {\r\n  var ctx = drawEvent.context;\r\n  var w = ctx.canvas.clientWidth;\r\n  var h = ctx.canvas.clientHeight;\r\n\r\n  \/\/ clear canvas on each render\r\n  ctx.fillStyle = \'black\';\r\n  ctx.fillRect(0, 0, w, h);\r\n\r\n  \/\/ center point\r\n  var cx = w \/ 2;\r\n  var cy = h \/ 2;\r\n  var maxRadius = Math.min(w, h - 2 * 10) \/ 2;\r\n  drawHand(renderState.minute, ctx, cx, cy, maxRadius);\r\n  drawHand(renderState.hour, ctx, cx, cy, maxRadius);\r\n\r\n  \/\/ Draw a 12 o clock indicator\r\n  drawHand({style: \'white\', scale: 0, angle: 0}, ctx, cx, 8, 0);\r\n  \/\/ overdraw center so that no white part of the minute hand is visible\r\n  drawHand({style: \'red\', scale: 0, angle: 0}, ctx, cx, cy, 0);\r\n});\r\n\r\n\/\/ listener is called on each full minute and once immediately after registration\r\nrocky.on(\'minutechange\', function(e) {\r\n  \/\/ WatchfaceHelper will later be extracted as npm module\r\n  var wfh = new Rocky.WatchfaceHelper(e.date);\r\n  renderState.minute.angle = wfh.minuteAngle;\r\n  renderState.hour.angle = wfh.hourAngle;\r\n  rocky.requestDraw();\r\n});\r\n";
        function testGenerator(remainingCalls) {
            var logLines = [];
            var logOriginal = console.log;
            console.log = function(s) {logLines.push(s);};
            var result = createSnapshot(tictoc);
            console.log = logOriginal;
            document.writeln('<pre>');
            logLines.forEach(function(s) {
                document.writeln(s);
            });
            document.write(JSON.stringify(result));
            document.writeln('</pre>');
            if (remainingCalls > 0) {
                setTimeout(function(){testGenerator(remainingCalls - 1);}, 200);
            }
        }
        setTimeout(function(){testGenerator(3);}, 200);
    </script>
</head>
<body>

</body>
</html>