<commit_before>'use strict';

if (!MODULE) { var MODULE = {}; }

MODULE.LevelScreen = (function() {
  var LevelScreen = function() {
    var self = this;

    this.screen = document.getElementById('screen-level');

    this.level_el = this.screen.getElementsByClassName('level-container')[0];
    this.grid_el = this.screen.getElementsByClassName('grid-container')[0];

    this.footer = this.screen.getElementsByTagName('footer')[0];
    this.header = this.screen.getElementsByTagName('header')[0];

    this.playcontrols = this.footer.getElementsByClassName('play-controls')[0];
    this.stopcontrols = this.footer.getElementsByClassName('stop-controls')[0];

    this.generation = this.header.getElementsByClassName('generation')[0];
    this.played = this.header.getElementsByClassName('played')[0];

    this.title = this.header.getElementsByClassName('title')[0];

    this.autowin = this.header.getElementsByClassName('autowin')[0];
    this.autowin_goal = this.autowin.getElementsByClassName('autowin-gen')[0];

    this.maxplay = this.header.getElementsByClassName('maxplay')[0];
    this.maxplayval = this.maxplay.getElementsByClassName('maxplay-val')[0];

    this.segment_generation = this.header.getElementsByClassName('segment-gen')[0];
    this.segment_played = this.header.getElementsByClassName('segment-played')[0];
    this.segment_red = this.header.getElementsByClassName('segment-red')[0];
    this.segment_green = this.header.getElementsByClassName('segment-green')[0];

    this.maxred_current = this.segment_red.getElementsByClassName('current')[0];
    this.maxred_limit = this.segment_red.getElementsByClassName('limit')[0];

    this.mingreen_current = this.segment_green.getElementsByClassName('current')[0];
    this.mingreen_limit = this.segment_green.getElementsByClassName('limit')[0];

    this.buttons = {
      play: this.footer.getElementsByClassName('play')[0],
      stop: this.footer.getElementsByClassName('stop')[0],
      clear: this.footer.getElementsByClassName('clear')[0],
      help: this.footer.getElementsByClassName('help')[0],
      library: this.footer.getElementsByClassName('library')[0],
      exit: this.footer.getElementsByClassName('exit')[0]
    };

    this.level = null;
    this.grid = null;
    this.level_id = null;
    this.library = null;

    this.buttons.play.onclick = this.onPlay.bind(this);
    this.buttons.stop.onclick = this.onStop.bind(this);
    this.buttons.clear.onclick = this.onClear.bind(this);
    this.buttons.help.onclick = this.onHelp.bind(this);
    this.buttons.library.onclick = this.onLibrary.bind(this);
    this.buttons.exit.onclick = this.onExit.bind(this);
  };

  LevelScreen.HEADER_HEIGHT = 50;
  LevelScreen.FOOTER_HEIGHT = 50;

  LevelScreen.prototype.display = function(level_id) {
    var self = this;

    this.playcontrols.classList.add('hide');
    this.stopcontrols.classList.remove('hide');
    this.header.className = '';
    this.footer.className = '';

    this.level_id = level_id;

    var campaign = app.content.data.campaign[level_id];
    this.title.innerHTML = campaign.name;

    var constraints = {
      width: app.device.viewport.width,
      height: app.device.viewport.height - (LevelScreen.HEADER_HEIGHT + LevelScreen.FOOTER_HEIGHT)
    };

    console.log('constraints', constraints);

    this.level = new MODULE.Level(campaign, this.level_el, constraints);

    this.grid = new MODULE.Grid(
      this.level.dimensions.width,
      this.level.dimensions.height,
      this.level.size,
      this.grid_el
    );

    this.level
    .on('generation', function(generation) {
      self.generation.textContent = generation;
    })
    .on('play-count', function(played) {
      self.played.textContent = played;
    })
    .on('status', function(status) {
      if (status === MODULE.Level.STATUS.DONE) {
        self.win();
      } else if (status === MODULE.Level.STATUS.LOSE) {
        self.lose();
      }
    })
    .on('win', function(data) {
      var my_level = app.storage.get('level', 0);
      var max_level = Object.keys(app.content.data.campaign).length;
      var levelup = false;

      if (data.level === max_level) {
        // TODO: Congratulate user on winning
        console.log("Beat the final level... Now what?");
      }

      if (data.level === my_level + 1) {
        levelup = true;
        app.storage.set('level', data.level);
        self.displayAdIfNeeded(data.level);
      }

      var rank = app.rank.get(data.level, data.generation, data.played);

      var rank_up = app.rank.report(data.level, rank.id);

      console.log('Rank up?', rank_up, rank);

      var content = LevelScreen.resultModal(rank, campaign.win, data.generation, data.played);

      if (levelup || rank_up) {
        app.modal.show(
          content, [{
            text: "Watch"
          },{
            text: "Next Level",
            highlight: true,
            callback: function() {
              app.screen.display('level', self.level_id + 1);
            }
          }], true
        );
      }

      app.analytics.track('LEVEL-WIN', {
        level: data.level,
        generation: data.generation,
        played: data.played,
        levelup: levelup
      });
    })
    .on('lose', function(data) {
      var rank = app.rank.getFailure();

      var content = LevelScreen.resultModal(rank, campaign.lose, data.generation, data.played);

      app.modal.show(
        content, [{
          text: "Watch"
        },{
          text: "Try Again",
          highlight: true,
          callback: function() {
            self.onStop();
          }
        }], true
      );
    })
    .on('green-count', function(count) {
      self.mingreen_current.textContent = count;
    })
    .on('red-count', function(count) {
      self.maxred_current.textContent = count;
    });

    this.level.initialize();

    if (level_id > app.storage.get('level')) {
      this.incomplete();
      this.intro();
    } else {
      this.complete();
    }

    this.setGenerationGoal(this.level.autowin);
    this.setMaxPlay(this.level.maxplay);
    this.togglePlayedSegment(!!this.level.playables.length);
    this.toggleRedSegment(this.level.maxred);
    this.toggleGreenSegment(this.level.mingreen);

    var gamefield = this.level.gamefield;
    var finger = new Hammer(gamefield);
    finger.get('pinch').set({enable: true});

    /*
    finger.add(new Hammer.Pan({
      direction: Hammer.DIRECTION_ALL,
      threshold: 0
    }));
    */

    finger.on('pinchstart', function() {
      self.level.scaleStart();
    });

    finger.on('pinch', function(event) {
      self.level.scale(event.scale);
      self.grid.scale(self.level.size);
    });

    finger.on('pinchend', function() {
      self.level.scaleEnd();
    });

    // finger.on('pan', function(event) {});

    finger.on('tap', function(event) {
      self.level.onTap(event);
    });

    // But hammer, I don't want you to stop my panning!
    gamefield.setAttribute('style', '');

    app.audio.playMusic('chapter-' + this.level.chapter);

    this.screen.style.display = 'block';

    this.library = null;

    app.analytics.track('SCREEN-LEVEL', {
      level: level_id
    });
  };

  LevelScreen.prototype.hide = function() {
    this.level.onStop();
    this.level = null;
    this.setGenerationGoal(0);
    this.setMaxPlay(0);
    this.screen.style.display = 'none';
  };

  LevelScreen.prototype.displayAdIfNeeded = function(level) {
    var min = app.content.data.dictionary.ad_min_level;
    var mod = app.content.data.dictionary.ad_mod_level;

    if (level >= min && level % mod === 0) {
      app.interstitial.show();
    }
  };

  LevelScreen.resultModal = function(rank, flavor, gen, played) {
    var html = "
\n";
      html += "

\n";
      html += "
\n";
        html += "
" + rank.name + "
\n";
        html += "
Gen: " + gen + " Played: " + played + "
\n";
      html += "
\n";
    html += "
\n";
    html += "
\n" + flavor + "
\n";

    return html;
  };

  LevelScreen.prototype.setGenerationGoal = function(generation) {
    this.autowin_goal.textContent = generation;

    this.autowin.classList.toggle('hide', !generation);
  };

  LevelScreen.prototype.setMaxPlay = function(maxplay) {
    this.maxplayval.textContent = maxplay;

    this.maxplay.classList.toggle('hide', !maxplay);
  };

  LevelScreen.prototype.togglePlayedSegment = function(count) {
    this.segment_played.classList.toggle('hide', !count);
    this.buttons.clear.classList.toggle('hide', !count);
    this.buttons.play.classList.toggle('cycle', !count);
  };

  LevelScreen.prototype.toggleRedSegment = function(count) {
    this.maxred_limit.textContent = count;
    this.segment_red.classList.toggle('hide', !count);
  };

  LevelScreen.prototype.toggleGreenSegment = function(count) {
    this.mingreen_limit.textContent = count;
    this.segment_green.classList.toggle('hide', !count);
  };

  LevelScreen.prototype.complete = function() {
    this.header.classList.add('done');
    this.footer.classList.add('done');
    this.buttons.exit.classList.add('cycle');
    this.buttons.play.classList.remove('cycle');
  };

  LevelScreen.prototype.win = function() {
    this.header.classList.add('win');
    this.footer.classList.add('win');
    this.buttons.exit.classList.add('cycle');
    this.buttons.play.classList.remove('cycle');
  };

  LevelScreen.prototype.lose = function() {
    this.header.classList.add('lose');
    this.footer.classList.add('lose');
  };

  LevelScreen.prototype.incomplete = function() {
    this.header.classList.remove('done');
    this.footer.classList.remove('done');
    this.buttons.exit.classList.remove('cycle');
  };

  LevelScreen.prototype.intro = function() {
    app.modal.show("
" + this.level.name + "
" + this.level.description, [{
      text: "Play Level",
      highlight: true
    }]);
  };

  LevelScreen.prototype.getLibrary = function() {
    if (this.library) {
      return this.library;
    }

    var self = this;

    var library = '
';

    Object.keys(app.content.data.library).forEach(function(lib) {
      var item = app.content.data.library[lib];

      if (item.id > self.level.library) {
        return;
      }

      library +=
        '
' +
          '' + item.name + '
' +
          '' +
        '
';
    });

    library += '
';

    this.library = library;

    return this.library;
  };

  LevelScreen.prototype.onPlay = function() {
    app.audio.playSound('play');
    this.playcontrols.classList.remove('hide');
    this.stopcontrols.classList.add('hide');

    this.header.classList.add('playing');
    this.footer.classList.add('playing');

    app.analytics.track('LEVEL-START', {
      level: this.level_id
    });

    this.level.onPlay();
  };

  LevelScreen.prototype.onStop = function() {
    app.audio.playSound('stop');
    this.playcontrols.classList.add('hide');
    this.stopcontrols.classList.remove('hide');

    this.header.classList.remove('playing');
    this.footer.classList.remove('playing');
    //this.header.classList.add('win');
    //this.footer.classList.add('win');

    app.analytics.track('LEVEL-STOP', {
      level: this.level_id,
      generation: this.level.generation
    });

    this.unLose();
    this.level.onStop();
  };

  LevelScreen.prototype.unLose = function() {
    this.header.classList.remove('lose');
    this.footer.classList.remove('lose');
  };

  LevelScreen.prototype.onClear = function() {
    app.audio.playSound('clear');

    app.analytics.track('LEVEL-CLEAR', {
      level: this.level_id
    });

    this.level.onClear();
  };

  LevelScreen.prototype.onHelp = function() {
    this.intro();

    app.analytics.track('LEVEL-HELP', {
      level: this.level_id
    });
  };

  LevelScreen.prototype.onLibrary = function() {
    app.modal.show(this.getLibrary(), [{
      text: 'Dismiss',
      highlight: true
    }]);

    app.analytics.track('LEVEL-LIBRARY', {
      level: this.level_id
    });
  };

  LevelScreen.prototype.onExit = function() {
    app.audio.playSound('back');
    app.screen.display('campaign');
  };

  return LevelScreen;
}());
<commit_msg>prevent going past final level<commit_after>'use strict';

if (!MODULE) { var MODULE = {}; }

MODULE.LevelScreen = (function() {
  var LevelScreen = function() {
    var self = this;

    this.screen = document.getElementById('screen-level');

    this.level_el = this.screen.getElementsByClassName('level-container')[0];
    this.grid_el = this.screen.getElementsByClassName('grid-container')[0];

    this.footer = this.screen.getElementsByTagName('footer')[0];
    this.header = this.screen.getElementsByTagName('header')[0];

    this.playcontrols = this.footer.getElementsByClassName('play-controls')[0];
    this.stopcontrols = this.footer.getElementsByClassName('stop-controls')[0];

    this.generation = this.header.getElementsByClassName('generation')[0];
    this.played = this.header.getElementsByClassName('played')[0];

    this.title = this.header.getElementsByClassName('title')[0];

    this.autowin = this.header.getElementsByClassName('autowin')[0];
    this.autowin_goal = this.autowin.getElementsByClassName('autowin-gen')[0];

    this.maxplay = this.header.getElementsByClassName('maxplay')[0];
    this.maxplayval = this.maxplay.getElementsByClassName('maxplay-val')[0];

    this.segment_generation = this.header.getElementsByClassName('segment-gen')[0];
    this.segment_played = this.header.getElementsByClassName('segment-played')[0];
    this.segment_red = this.header.getElementsByClassName('segment-red')[0];
    this.segment_green = this.header.getElementsByClassName('segment-green')[0];

    this.maxred_current = this.segment_red.getElementsByClassName('current')[0];
    this.maxred_limit = this.segment_red.getElementsByClassName('limit')[0];

    this.mingreen_current = this.segment_green.getElementsByClassName('current')[0];
    this.mingreen_limit = this.segment_green.getElementsByClassName('limit')[0];

    this.buttons = {
      play: this.footer.getElementsByClassName('play')[0],
      stop: this.footer.getElementsByClassName('stop')[0],
      clear: this.footer.getElementsByClassName('clear')[0],
      help: this.footer.getElementsByClassName('help')[0],
      library: this.footer.getElementsByClassName('library')[0],
      exit: this.footer.getElementsByClassName('exit')[0]
    };

    this.level = null;
    this.grid = null;
    this.level_id = null;
    this.library = null;

    this.buttons.play.onclick = this.onPlay.bind(this);
    this.buttons.stop.onclick = this.onStop.bind(this);
    this.buttons.clear.onclick = this.onClear.bind(this);
    this.buttons.help.onclick = this.onHelp.bind(this);
    this.buttons.library.onclick = this.onLibrary.bind(this);
    this.buttons.exit.onclick = this.onExit.bind(this);
  };

  LevelScreen.HEADER_HEIGHT = 50;
  LevelScreen.FOOTER_HEIGHT = 50;

  LevelScreen.prototype.display = function(level_id) {
    var self = this;

    this.playcontrols.classList.add('hide');
    this.stopcontrols.classList.remove('hide');
    this.header.className = '';
    this.footer.className = '';

    this.level_id = level_id;

    var campaign = app.content.data.campaign[level_id];
    this.title.innerHTML = campaign.name;

    var constraints = {
      width: app.device.viewport.width,
      height: app.device.viewport.height - (LevelScreen.HEADER_HEIGHT + LevelScreen.FOOTER_HEIGHT)
    };

    console.log('constraints', constraints);

    this.level = new MODULE.Level(campaign, this.level_el, constraints);

    this.grid = new MODULE.Grid(
      this.level.dimensions.width,
      this.level.dimensions.height,
      this.level.size,
      this.grid_el
    );

    this.level
    .on('generation', function(generation) {
      self.generation.textContent = generation;
    })
    .on('play-count', function(played) {
      self.played.textContent = played;
    })
    .on('status', function(status) {
      if (status === MODULE.Level.STATUS.DONE) {
        self.win();
      } else if (status === MODULE.Level.STATUS.LOSE) {
        self.lose();
      }
    })
    .on('win', function(data) {
      var my_level = app.storage.get('level', 0);
      var max_level = Object.keys(app.content.data.campaign).length;
      var levelup = false;
      var end_game = false;

      if (data.level === max_level) {
        // TODO: Congratulate user on winning
        end_game = true;
        console.log("Beat the final level... Now what?");
      }

      if (data.level === my_level + 1) {
        levelup = true;
        app.storage.set('level', data.level);
        self.displayAdIfNeeded(data.level);
      }

      var rank = app.rank.get(data.level, data.generation, data.played);

      var rank_up = app.rank.report(data.level, rank.id);

      console.log('Rank up?', rank_up, rank);

      var content = LevelScreen.resultModal(rank, campaign.win, data.generation, data.played);

      var buttons = [{
        text: "Watch"
      }];

      if (!end_game) {
        buttons.push({
          text: "Next Level",
          highlight: true,
          callback: function() {
            app.screen.display('level', self.level_id + 1);
          }
        });
      }

      if (levelup || rank_up) {
        app.modal.show(content, buttons, true);
      }

      app.analytics.track('LEVEL-WIN', {
        level: data.level,
        generation: data.generation,
        played: data.played,
        levelup: levelup
      });
    })
    .on('lose', function(data) {
      var rank = app.rank.getFailure();

      var content = LevelScreen.resultModal(rank, campaign.lose, data.generation, data.played);

      app.modal.show(
        content, [{
          text: "Watch"
        },{
          text: "Try Again",
          highlight: true,
          callback: function() {
            self.onStop();
          }
        }], true
      );
    })
    .on('green-count', function(count) {
      self.mingreen_current.textContent = count;
    })
    .on('red-count', function(count) {
      self.maxred_current.textContent = count;
    });

    this.level.initialize();

    if (level_id > app.storage.get('level')) {
      this.incomplete();
      this.intro();
    } else {
      this.complete();
    }

    this.setGenerationGoal(this.level.autowin);
    this.setMaxPlay(this.level.maxplay);
    this.togglePlayedSegment(!!this.level.playables.length);
    this.toggleRedSegment(this.level.maxred);
    this.toggleGreenSegment(this.level.mingreen);

    var gamefield = this.level.gamefield;
    var finger = new Hammer(gamefield);
    finger.get('pinch').set({enable: true});

    /*
    finger.add(new Hammer.Pan({
      direction: Hammer.DIRECTION_ALL,
      threshold: 0
    }));
    */

    finger.on('pinchstart', function() {
      self.level.scaleStart();
    });

    finger.on('pinch', function(event) {
      self.level.scale(event.scale);
      self.grid.scale(self.level.size);
    });

    finger.on('pinchend', function() {
      self.level.scaleEnd();
    });

    // finger.on('pan', function(event) {});

    finger.on('tap', function(event) {
      self.level.onTap(event);
    });

    // But hammer, I don't want you to stop my panning!
    gamefield.setAttribute('style', '');

    app.audio.playMusic('chapter-' + this.level.chapter);

    this.screen.style.display = 'block';

    this.library = null;

    app.analytics.track('SCREEN-LEVEL', {
      level: level_id
    });
  };

  LevelScreen.prototype.hide = function() {
    this.level.onStop();
    this.level = null;
    this.setGenerationGoal(0);
    this.setMaxPlay(0);
    this.screen.style.display = 'none';
  };

  LevelScreen.prototype.displayAdIfNeeded = function(level) {
    var min = app.content.data.dictionary.ad_min_level;
    var mod = app.content.data.dictionary.ad_mod_level;

    if (level >= min && level % mod === 0) {
      app.interstitial.show();
    }
  };

  LevelScreen.resultModal = function(rank, flavor, gen, played) {
    var html = "
\n";
      html += "

\n";
      html += "
\n";
        html += "
" + rank.name + "
\n";
        html += "
Gen: " + gen + " Played: " + played + "
\n";
      html += "
\n";
    html += "
\n";
    html += "
\n" + flavor + "
\n";

    return html;
  };

  LevelScreen.prototype.setGenerationGoal = function(generation) {
    this.autowin_goal.textContent = generation;

    this.autowin.classList.toggle('hide', !generation);
  };

  LevelScreen.prototype.setMaxPlay = function(maxplay) {
    this.maxplayval.textContent = maxplay;

    this.maxplay.classList.toggle('hide', !maxplay);
  };

  LevelScreen.prototype.togglePlayedSegment = function(count) {
    this.segment_played.classList.toggle('hide', !count);
    this.buttons.clear.classList.toggle('hide', !count);
    this.buttons.play.classList.toggle('cycle', !count);
  };

  LevelScreen.prototype.toggleRedSegment = function(count) {
    this.maxred_limit.textContent = count;
    this.segment_red.classList.toggle('hide', !count);
  };

  LevelScreen.prototype.toggleGreenSegment = function(count) {
    this.mingreen_limit.textContent = count;
    this.segment_green.classList.toggle('hide', !count);
  };

  LevelScreen.prototype.complete = function() {
    this.header.classList.add('done');
    this.footer.classList.add('done');
    this.buttons.exit.classList.add('cycle');
    this.buttons.play.classList.remove('cycle');
  };

  LevelScreen.prototype.win = function() {
    this.header.classList.add('win');
    this.footer.classList.add('win');
    this.buttons.exit.classList.add('cycle');
    this.buttons.play.classList.remove('cycle');
  };

  LevelScreen.prototype.lose = function() {
    this.header.classList.add('lose');
    this.footer.classList.add('lose');
  };

  LevelScreen.prototype.incomplete = function() {
    this.header.classList.remove('done');
    this.footer.classList.remove('done');
    this.buttons.exit.classList.remove('cycle');
  };

  LevelScreen.prototype.intro = function() {
    app.modal.show("
" + this.level.name + "
" + this.level.description, [{
      text: "Play Level",
      highlight: true
    }]);
  };

  LevelScreen.prototype.getLibrary = function() {
    if (this.library) {
      return this.library;
    }

    var self = this;

    var library = '
';

    Object.keys(app.content.data.library).forEach(function(lib) {
      var item = app.content.data.library[lib];

      if (item.id > self.level.library) {
        return;
      }

      library +=
        '
' +
          '' + item.name + '
' +
          '' +
        '
';
    });

    library += '
';

    this.library = library;

    return this.library;
  };

  LevelScreen.prototype.onPlay = function() {
    app.audio.playSound('play');
    this.playcontrols.classList.remove('hide');
    this.stopcontrols.classList.add('hide');

    this.header.classList.add('playing');
    this.footer.classList.add('playing');

    app.analytics.track('LEVEL-START', {
      level: this.level_id
    });

    this.level.onPlay();
  };

  LevelScreen.prototype.onStop = function() {
    app.audio.playSound('stop');
    this.playcontrols.classList.add('hide');
    this.stopcontrols.classList.remove('hide');

    this.header.classList.remove('playing');
    this.footer.classList.remove('playing');
    //this.header.classList.add('win');
    //this.footer.classList.add('win');

    app.analytics.track('LEVEL-STOP', {
      level: this.level_id,
      generation: this.level.generation
    });

    this.unLose();
    this.level.onStop();
  };

  LevelScreen.prototype.unLose = function() {
    this.header.classList.remove('lose');
    this.footer.classList.remove('lose');
  };

  LevelScreen.prototype.onClear = function() {
    app.audio.playSound('clear');

    app.analytics.track('LEVEL-CLEAR', {
      level: this.level_id
    });

    this.level.onClear();
  };

  LevelScreen.prototype.onHelp = function() {
    this.intro();

    app.analytics.track('LEVEL-HELP', {
      level: this.level_id
    });
  };

  LevelScreen.prototype.onLibrary = function() {
    app.modal.show(this.getLibrary(), [{
      text: 'Dismiss',
      highlight: true
    }]);

    app.analytics.track('LEVEL-LIBRARY', {
      level: this.level_id
    });
  };

  LevelScreen.prototype.onExit = function() {
    app.audio.playSound('back');
    app.screen.display('campaign');
  };

  return LevelScreen;
}());
<|endoftext|>


<commit_before>var obj_canvas,
obj_c,
cp_canvas = null;

var canvas
var video_canvas,
vid_c;

var brown_const=0;

var vid_width = 640;
var vid_height = 480;

var svg_led;
var context;

var l = 80,
	n = 4,
	v = 1/4;

var n_max = 20;

function setupD3() {
    canvas = d3.select("#canvasArea").append("canvas")
        .attr("width", vid_width)
        .attr("height", vid_height);

    svg_led = d3.select("#canvasArea").append("svg:svg")
        .attr("width", 150)
        .attr("height", 150)
        .attr("viewBox", "0 0 300 300")
        .style("opacity", "0.5");

    var shape_bg = svg_led.append("svg:rect")
    							.attr("width", 300)
    							.attr("height", 300)
    							.style("fill","#000000");

    var shape_stage = svg_led.append("svg:rect")
    							.attr("x", 112.5)
    							.attr("y", 112.5)
    							.attr("width", 75)
    							.attr("height", 75)
    							.style("fill", "#111111");

    var g_ledL = svg_led.append("svg:g")
    						.attr("transform", "matrix(1 0 0 -1 55 150)");
    			g_ledL.append("svg:polygon")
    						.attr("points", "-39.042,8.417 24.708,8.417 24.7,6.191 20.958,5.667 6.708,1.417 6.708,6.167 -39.042,6.167 	");
    			g_ledL.append("svg:polygon")
    						.attr("points", "-38.792,-8.333 21.458,-8.333 24.208,-6.333 24.208,3.667 6.708,-2.333 6.708,-5.833 -38.792,-5.833 	");
    var led_L = g_ledL.append("svg:path")
    						.attr("d", "M39.042,0.834c0-3.697-0.483-7.667-4.069-10.966c-8.452-7.775-36.53-7.701-38.847-7.701c-3.728,0-4.75,7.909-4.75,17.667c0,9.757,1.022,17.667,4.75,17.667c2.282,0,32.307,0.417,38.792-6.494C38.095,7.62,39.042,4.622,39.042,0.834z")
    						.style("fill", "#ffffff")
    						.style("opacity", "0");

    var g_ledR = svg_led.append("svg:g")
    						.attr("transform", "matrix(-1 0 0 1 245 150)");
    			g_ledR.append("svg:polygon")
    						.attr("points", "-39.042,8.417 24.708,8.417 24.7,6.191 20.958,5.667 6.708,1.417 6.708,6.167 -39.042,6.167 	");
    			g_ledR.append("svg:polygon")
    						.attr("points", "-38.792,-8.333 21.458,-8.333 24.208,-6.333 24.208,3.667 6.708,-2.333 6.708,-5.833 -38.792,-5.833 	");
    var led_R = g_ledR.append("svg:path")
    						.attr("d", "M39.042,0.834c0-3.697-0.483-7.667-4.069-10.966c-8.452-7.775-36.53-7.701-38.847-7.701c-3.728,0-4.75,7.909-4.75,17.667c0,9.757,1.022,17.667,4.75,17.667c2.282,0,32.307,0.417,38.792-6.494C38.095,7.62,39.042,4.622,39.042,0.834z")
    						.style("fill", "#ffffff")
    						.style("opacity", "0");

    var g_ledU = svg_led.append("svg:g")
    						.attr("transform", "matrix(0 1 1 0 150 55)");
    			g_ledU.append("svg:polygon")
    						.attr("points", "-39.042,8.417 24.708,8.417 24.7,6.191 20.958,5.667 6.708,1.417 6.708,6.167 -39.042,6.167 	");
    			g_ledU.append("svg:polygon")
    						.attr("points", "-38.792,-8.333 21.458,-8.333 24.208,-6.333 24.208,3.667 6.708,-2.333 6.708,-5.833 -38.792,-5.833 	");
    var led_U = g_ledU.append("svg:path")
    						.attr("d", "M39.042,0.834c0-3.697-0.483-7.667-4.069-10.966c-8.452-7.775-36.53-7.701-38.847-7.701c-3.728,0-4.75,7.909-4.75,17.667c0,9.757,1.022,17.667,4.75,17.667c2.282,0,32.307,0.417,38.792-6.494C38.095,7.62,39.042,4.622,39.042,0.834z")
    						.style("fill", "#ffffff")
    						.style("opacity", "0");

    var g_ledD = svg_led.append("svg:g")
    						.attr("transform", "matrix(0 -1 -1 0 150 245)");
    			g_ledD.append("svg:polygon")
    						.attr("points", "-39.042,8.417 24.708,8.417 24.7,6.191 20.958,5.667 6.708,1.417 6.708,6.167 -39.042,6.167 	");
    			g_ledD.append("svg:polygon")
    						.attr("points", "-38.792,-8.333 21.458,-8.333 24.208,-6.333 24.208,3.667 6.708,-2.333 6.708,-5.833 -38.792,-5.833 	");
    var led_D = g_ledD.append("svg:path")
    						.attr("d", "M39.042,0.834c0-3.697-0.483-7.667-4.069-10.966c-8.452-7.775-36.53-7.701-38.847-7.701c-3.728,0-4.75,7.909-4.75,17.667c0,9.757,1.022,17.667,4.75,17.667c2.282,0,32.307,0.417,38.792-6.494C38.095,7.62,39.042,4.622,39.042,0.834z")
    						.style("fill", "#ffffff")
    						.style("opacity", "0");

    context = canvas.node().getContext("2d");

	var w = 640,
    	h = 480,
    	m = 20,
    	radius = l/2+10,
    	degrees = 180 / Math.PI;

	var objects = d3.range(n_max).map(function() {
  		var x = 40 + Math.random() * (w-40), y = 40 + Math.random() * (h-40);
  		return {
    		vx: 0,
    		vy: 0,
    		path: d3.range(m).map(function() { return [x, y]; }),
    		count: 0,
    		active: 0,
    		size: l,
    		color: "#FA6600"
  		};
	});

	var svg = d3.select("#canvasArea").append("svg:svg")
    	.attr("width", vid_width)
    	.attr("height", vid_height);

	var g = svg.selectAll("g")
    	.data(objects)
		.enter().append("svg:g");

	var box = g.append("svg:rect");

	function drawObjects(){
		for (var i = -1; ++i < n;) {
    		var object = objects[i],
        		x = object.path[0][0],
        		y = object.path[0][1];

    		// stuck at the walls.
    		if (x < 0)
    		{
    			object.path[0][0] = l;
    		}
    		if(x > w-l)
    		{
    			object.path[0][0] = w-l;
    		}
    		if (y < 0)
    		{
    			object.path[0][1] = l;
    		}
    		if (y > h-l)
    		{
    			object.path[0][1] = h-l;
    		}
    		object.size = l;
    		object.active = (i<n)?1:0;
    	}
    	box.attr("opacity", function(d,i){
    		return d.active;
    	});
		box.attr("width", function(d,i){
			return d.size;
		});
		box.attr("height", function(d,i){
			return d.size;
		});
		box.attr("stroke", function(d,i){
			return d.color;
		});
  		box.attr("transform", function(d) {
    		return "translate(" + d.path[0] + ")";
  		});

  		led_U.style("opacity",arrow.int1);
  		led_L.style("opacity",arrow.int2);
  		led_D.style("opacity",arrow.int3);
  		led_R.style("opacity",arrow.int4);
  	}

	d3.timer(function() {
  		drawObjects();
	});

	window.setInterval(getVideo, 1000/20);

	function getVideo(){
        getVidFrame("http://171.65.102.132:8080/?action=snapshot?t=" + new Date().getTime(), function(image) {
            context.clearRect(0, 0, vid_width, vid_height);
            context.drawImage(image, 0, 0, vid_width, vid_height);
        });

    	function getVidFrame(path, callback) {
            var image = new Image;
            image.src = path;
            image.onload = function() {
                callback(image);
				//console.log(new Date().getTime());
            };
        }
    }
}
<commit_msg>combine d3 svg<commit_after>var obj_canvas,
obj_c,
cp_canvas = null;

var canvas
var video_canvas,
vid_c;

var brown_const=0;

var vid_width = 640;
var vid_height = 480;

var svg_led;
var context;

var l = 80,
	n = 4,
	v = 1/4;

var n_max = 20;

function setupD3() {
    canvas = d3.select("#canvasArea").append("canvas")
        .attr("width", vid_width)
        .attr("height", vid_height);

    svg_led = d3.select("#canvasArea").append("svg:svg")
        .attr("width", 150)
        .attr("height", 150)
        .attr("x", 300)
        .attr("viewBox", "50 50 200 200")
        .style("opacity", "0.5");

    var shape_bg = svg_led.append("svg:rect")
    							.attr("width", 300)
    							.attr("height", 300)
    							.style("fill","#000000");

    var shape_stage = svg_led.append("svg:rect")
    							.attr("x", 112.5)
    							.attr("y", 112.5)
    							.attr("width", 75)
    							.attr("height", 75)
    							.style("fill", "#111111");

    var g_ledL = svg_led.append("svg:g")
    						.attr("transform", "matrix(1 0 0 -1 55 150)");
    			g_ledL.append("svg:polygon")
    						.attr("points", "-39.042,8.417 24.708,8.417 24.7,6.191 20.958,5.667 6.708,1.417 6.708,6.167 -39.042,6.167 	");
    			g_ledL.append("svg:polygon")
    						.attr("points", "-38.792,-8.333 21.458,-8.333 24.208,-6.333 24.208,3.667 6.708,-2.333 6.708,-5.833 -38.792,-5.833 	");
    var led_L = g_ledL.append("svg:path")
    						.attr("d", "M39.042,0.834c0-3.697-0.483-7.667-4.069-10.966c-8.452-7.775-36.53-7.701-38.847-7.701c-3.728,0-4.75,7.909-4.75,17.667c0,9.757,1.022,17.667,4.75,17.667c2.282,0,32.307,0.417,38.792-6.494C38.095,7.62,39.042,4.622,39.042,0.834z")
    						.style("fill", "#ffffff")
    						.style("opacity", "0");

    var g_ledR = svg_led.append("svg:g")
    						.attr("transform", "matrix(-1 0 0 1 245 150)");
    			g_ledR.append("svg:polygon")
    						.attr("points", "-39.042,8.417 24.708,8.417 24.7,6.191 20.958,5.667 6.708,1.417 6.708,6.167 -39.042,6.167 	");
    			g_ledR.append("svg:polygon")
    						.attr("points", "-38.792,-8.333 21.458,-8.333 24.208,-6.333 24.208,3.667 6.708,-2.333 6.708,-5.833 -38.792,-5.833 	");
    var led_R = g_ledR.append("svg:path")
    						.attr("d", "M39.042,0.834c0-3.697-0.483-7.667-4.069-10.966c-8.452-7.775-36.53-7.701-38.847-7.701c-3.728,0-4.75,7.909-4.75,17.667c0,9.757,1.022,17.667,4.75,17.667c2.282,0,32.307,0.417,38.792-6.494C38.095,7.62,39.042,4.622,39.042,0.834z")
    						.style("fill", "#ffffff")
    						.style("opacity", "0");

    var g_ledU = svg_led.append("svg:g")
    						.attr("transform", "matrix(0 1 1 0 150 55)");
    			g_ledU.append("svg:polygon")
    						.attr("points", "-39.042,8.417 24.708,8.417 24.7,6.191 20.958,5.667 6.708,1.417 6.708,6.167 -39.042,6.167 	");
    			g_ledU.append("svg:polygon")
    						.attr("points", "-38.792,-8.333 21.458,-8.333 24.208,-6.333 24.208,3.667 6.708,-2.333 6.708,-5.833 -38.792,-5.833 	");
    var led_U = g_ledU.append("svg:path")
    						.attr("d", "M39.042,0.834c0-3.697-0.483-7.667-4.069-10.966c-8.452-7.775-36.53-7.701-38.847-7.701c-3.728,0-4.75,7.909-4.75,17.667c0,9.757,1.022,17.667,4.75,17.667c2.282,0,32.307,0.417,38.792-6.494C38.095,7.62,39.042,4.622,39.042,0.834z")
    						.style("fill", "#ffffff")
    						.style("opacity", "0");

    var g_ledD = svg_led.append("svg:g")
    						.attr("transform", "matrix(0 -1 -1 0 150 245)");
    			g_ledD.append("svg:polygon")
    						.attr("points", "-39.042,8.417 24.708,8.417 24.7,6.191 20.958,5.667 6.708,1.417 6.708,6.167 -39.042,6.167 	");
    			g_ledD.append("svg:polygon")
    						.attr("points", "-38.792,-8.333 21.458,-8.333 24.208,-6.333 24.208,3.667 6.708,-2.333 6.708,-5.833 -38.792,-5.833 	");
    var led_D = g_ledD.append("svg:path")
    						.attr("d", "M39.042,0.834c0-3.697-0.483-7.667-4.069-10.966c-8.452-7.775-36.53-7.701-38.847-7.701c-3.728,0-4.75,7.909-4.75,17.667c0,9.757,1.022,17.667,4.75,17.667c2.282,0,32.307,0.417,38.792-6.494C38.095,7.62,39.042,4.622,39.042,0.834z")
    						.style("fill", "#ffffff")
    						.style("opacity", "0");

    context = canvas.node().getContext("2d");

	var w = 640,
    	h = 480,
    	m = 20,
    	radius = l/2+10,
    	degrees = 180 / Math.PI;

	var objects = d3.range(n_max).map(function() {
  		var x = 40 + Math.random() * (w-40), y = 40 + Math.random() * (h-40);
  		return {
    		vx: 0,
    		vy: 0,
    		path: d3.range(m).map(function() { return [x, y]; }),
    		count: 0,
    		active: 0,
    		size: l,
    		color: "#FA6600"
  		};
	});

	var svg = d3.select("#canvasArea").append("svg:svg")
    	.attr("width", vid_width)
    	.attr("height", vid_height);

	var g = svg.selectAll("g")
    	.data(objects)
		.enter().append("svg:g");

	var box = g.append("svg:rect");

	function drawObjects(){
		for (var i = -1; ++i < n;) {
    		var object = objects[i],
        		x = object.path[0][0],
        		y = object.path[0][1];

    		// stuck at the walls.
    		if (x < 0)
    		{
    			object.path[0][0] = l;
    		}
    		if(x > w-l)
    		{
    			object.path[0][0] = w-l;
    		}
    		if (y < 0)
    		{
    			object.path[0][1] = l;
    		}
    		if (y > h-l)
    		{
    			object.path[0][1] = h-l;
    		}
    		object.size = l;
    		object.active = (i<n)?1:0;
    	}
    	box.attr("opacity", function(d,i){
    		return d.active;
    	});
		box.attr("width", function(d,i){
			return d.size;
		});
		box.attr("height", function(d,i){
			return d.size;
		});
		box.attr("stroke", function(d,i){
			return d.color;
		});
  		box.attr("transform", function(d) {
    		return "translate(" + d.path[0] + ")";
  		});

  		led_U.style("opacity",arrow.int1);
  		led_L.style("opacity",arrow.int2);
  		led_D.style("opacity",arrow.int3);
  		led_R.style("opacity",arrow.int4);
  	}

	d3.timer(function() {
  		drawObjects();
	});

	window.setInterval(getVideo, 1000/20);

	function getVideo(){
        getVidFrame("http://171.65.102.132:8080/?action=snapshot?t=" + new Date().getTime(), function(image) {
            context.clearRect(0, 0, vid_width, vid_height);
            context.drawImage(image, 0, 0, vid_width, vid_height);
        });

    	function getVidFrame(path, callback) {
            var image = new Image;
            image.src = path;
            image.onload = function() {
                callback(image);
				//console.log(new Date().getTime());
            };
        }
    }
}
<|endoftext|>


<commit_before>// rotation.js
(function(isNative) {

    if(isNative) return;

    function orthogonal(v) {
        if ((Math.abs(v._data[1]) >= 0.9*Math.abs(v._data[0])) && (Math.abs(v._data[2]) >= 0.9*Math.abs(v._data[0])))
            return new window.XML3DVec3(0.0, -v._data[2], v._data[1]);
          else
            if ((Math.abs(v._data[0]) >= 0.9*Math.abs(v._data[1])) && (Math.abs(v._data[2]) >= 0.9*Math.abs(v._data[1])))
              return new window.XML3DVec3(-v._data[2], 0.0, v._data[0]);
            else
              return new window.XML3DVec3(-v._data[1], v._data[0], 0.0);
    }

    /**
     * Creates an instance of XML3DRotation. XML3DRotation represents a
     * three-dimensional vector as a 3-tuple floating point values.
     * @constructor
     * @this {XML3DRotation}
     * @param {XML3DVec3=} axis
     * @param {number=} angle
     * @param {function(XML3DVec3=)=} cb Called, if value has changed.
     *                                   Has this as first parameter.
     */
    var XML3DRotation = function(axis, angle, cb) {
        var that = this;
        this._data = new Float32Array(4);

        var vec_cb = function() {
            that._updateQuaternion();
            if (that._callback)
                that._callback(that);
        };

        /** @private */
        this._axis = new window.XML3DVec3(0, 0, 1, vec_cb);
        /** @private */
        this._angle = 0;

        this._updateQuaternion();

        if(axis !== undefined && axis !== null) {
            this.set(axis, angle);
        }

        /** @private */
        this._callback = typeof cb == 'function' ? cb : 0;
    };

    var p = XML3DRotation.prototype;

    /**
     * The set method copies the values from other.
     * @param {Object} other another XML3DRotation, Float32Array or XML3DVec3. In the last case the 2nd argument is considered.
     * @param {number=} angle
     */
    p.set = function(other, angle) {
        if(other.axis && other.angle) {
            this.setAxisAngle(other.axis, other.angle);
        } else if(other.length && other.length >= 4) {
            this._setQuaternion(other);
        } else if(other._data && other._data.length && other._data.length === 3) {
            this.setAxisAngle(other, angle);
        } else {
            XML3D.debug.logError("XML3DRotation.set(): invalid argument given. Expect XML3DRotation or Float32Array.");
        }
    };

    /** @type {number} */
    Object.defineProperty(p, "axis", {
        /** @this {XML3DRotation} * */
        get : function() {
            return this._axis;
        },
        set : function() {
            throw Error("Can't set axis. XML3DRotation::axis is readonly.");
        },
        configurable : false,
        enumerable : false
    });

    /** @type {number} */
    Object.defineProperty(p, "angle", {
        /** @this {XML3DRotation} * */
        get : function() {
            return this._angle;
        },
        set : function(angle) {
            this._angle = angle;
            this._updateQuaternion();
            if (this._callback)
                this._callback(this);
    },
    configurable : false,
    enumerable : false
    });

    /**
     * String representation of the XML3DRotation.
     * @override
     * @this {XML3DRotation}
     * @return {string} Human-readable representation of this XML3DRotation.
     */
    p.toString = function() {
        return "[object XML3DRotation]";
    };

    /**
     * Replaces the existing rotation with the axis-angle representation passed
     * as argument
     */
    p.setAxisAngle = function(axis, angle) {
        if (typeof axis != 'object' || isNaN(angle)) {
            throw new Error("Illegal axis and/or angle values: " + "( axis="
                    + axis + " angle=" + angle + " )");
        }

        // TODO: slice?
        this._axis._data[0] = axis._data[0];
        this._axis._data[1] = axis._data[1];
        this._axis._data[2] = axis._data[2];
        this._angle = angle;
        this._updateQuaternion();
        if (this._callback)
            this._callback(this);
    };

    /**
     * Replaces the existing rotation with one computed from the two vectors
     * passed as arguments. {XML3DVec} from First vector {XML3DVec} from Second
     * vector
     */
    p.setRotation = function(from, to) {
        var a = from.normalize();
        var b = to.normalize();

        var axis = a.cross(b);
        if (!axis.length()) {
            // from and to are parallel
            axis = orthogonal(a);
        };
        // This function will also callback
        this.setAxisAngle(axis, Math.acos(a.dot(b)));
    };

    p._updateQuaternion = function() {
        var l = this._axis.length();
        if (l > 0.00001) {
            var s = Math.sin(this._angle / 2) / l;
            this._data[0] = this._axis.x * s;
            this._data[1] = this._axis.y * s;
            this._data[2] = this._axis.z * s;
            this._data[3] = Math.cos(this._angle / 2);
        } else {
            XML3D.math.quat.set(this._data, 0, 0, 0, 1);
        }
    };

    /**
     * Replaces the existing matrix with one computed from parsing the passed
     * string.
     * @param str String to parse
     */
    p.setAxisAngleValue = function(str) {
        var m = /^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*$/.exec(str);
        if (!m)
            throw new Error("Could not parse AxisAngle string: " + str);

        // This function will also callback
        this.setAxisAngle(new window.XML3DVec3(+m[1], +m[2], +m[3]), +m[4]);
    };

    /**
     * Linear interpolation of this rotation rot0 with the passed rotation rot1
     * with factor t. The result is (1-t)rot0 + t rot1. Typically realized with
     * a spherical linear interpolation based on quaternions.
     * @param {XML3DRotation} rot1 the passed rotation
     * @param {number} t the factor
     */
    p.interpolate = function(rot1, t) {
        var dest = XML3D.math.quat.create(), result = new XML3DRotation();
        XML3D.math.quat.slerp(dest, this._data, rot1._data, t);
        result._setQuaternion(dest);
        return result;
    };

    /**
     * Replaces the existing rotation with the quaternion representation passed
     * as argument
     * @param {XML3DVec3} vector
     * @param {number} w
     */
    p.setQuaternion = function(vector, scalar) {
        this._setQuaternion( [ vector.x, vector.y, vector.z, scalar ]);
    };

    /**
     * Returns a XML3DMatrix that describes this 3D rotation in a
     * 4x4 matrix representation.
     * @return {XML3DMatrix} Rotation matrix
     */
    p.toMatrix = function() {
        var q = XML3D.math.quat.copy(XML3D.math.quat.create(), this._data);
        var m = new window.XML3DMatrix();
        XML3D.math.mat4.fromRotationTranslation(m._data, q, [0, 0, 0]);
        return m;
    };

    /**
     * Rotates the vector passed as parameter with this rotation
     * representation. The result is returned as new vector instance.
     * Neither this nor the inputVector are changed.
     * 4x4 matrix representation.
     * @param {XML3DVec3} inputVector
     * @return {XML3DVec3} The rotated vector
     */
    p.rotateVec3 = function(inputVector) {
        var result = new window.XML3DVec3();
        XML3D.math.vec3.transformQuat(result._data, inputVector._data, this._data)
        return result;
    };

    /**
     * Replaces the existing rotation with the quaternion representation passed
     * as argument
     * @private
     * @param {Array} quat
     */
    p._setQuaternion = function(q) {
        var s = Math.sqrt(1 - q[3] * q[3]);
        if (s < 0.001 || isNaN(s)) {
            this._axis._data[0] = 0;
            this._axis._data[1] = 0;
            this._axis._data[2] = 1;
            this._angle = 0;
        } else {
            s = 1 / s;
            this._axis._data[0] = q[0] * s;
            this._axis._data[1] = q[1] * s;
            this._axis._data[2] = q[2] * s;
            this._angle = 2 * Math.acos(q[3]);
        }
        this._data = XML3D.math.quat.copy(XML3D.math.quat.create(), q);
        if (this._callback)
            this._callback(this);
    };

    /**
     * Multiplies this rotation with the passed rotation. This rotation is not
     * changed.
     *
     * @param {XML3DRotation} rot1
     * @return {XML3DVec3} The result
     */
    p.multiply = function(rot1) {
        var result = new XML3DRotation(), q = XML3D.math.quat.create();
        XML3D.math.quat.multiply(q, this._data, rot1._data);
        result._setQuaternion(q);
        return result;
    };

    /**
     * Returns the normalized version of this rotation. Result is a newly
     * created vector. This is not modified.
     */
    p.normalize = function(that) {
        var na = this._axis.normalize();
        return new XML3DRotation(na, this._angle);
    };

    /**
     * Returns the quaternion, that underlies this rotation.
     *
     * @return {Float32Array}
     */
    p.getQuaternion = function() {
        return XML3D.math.quat.copy(XML3D.math.quat.create(), this._data);
    };

    /**
     * Set this rotation based on the given base vectors.
     *
     * @param {XML3DVec3} xAxis
     * @param {XML3DVec3} yAxis
     * @param {XML3DVec3} zAxis
     */
    p.setFromBasis = function(xAxis, yAxis, zAxis) {
        var q = XML3D.math.quat.create();
        XML3D.math.quat.setFromBasis(xAxis._data, yAxis._data, zAxis._data, q);
        this._setQuaternion(q);
    };

    XML3D.XML3DRotation = XML3DRotation;
    window.XML3DRotation = XML3DRotation;

}(XML3D._native));
<commit_msg>Fixed bug in XML3DRotation::set<commit_after>// rotation.js
(function(isNative) {

    if(isNative) return;

    function orthogonal(v) {
        if ((Math.abs(v._data[1]) >= 0.9*Math.abs(v._data[0])) && (Math.abs(v._data[2]) >= 0.9*Math.abs(v._data[0])))
            return new window.XML3DVec3(0.0, -v._data[2], v._data[1]);
          else
            if ((Math.abs(v._data[0]) >= 0.9*Math.abs(v._data[1])) && (Math.abs(v._data[2]) >= 0.9*Math.abs(v._data[1])))
              return new window.XML3DVec3(-v._data[2], 0.0, v._data[0]);
            else
              return new window.XML3DVec3(-v._data[1], v._data[0], 0.0);
    }

    /**
     * Creates an instance of XML3DRotation. XML3DRotation represents a
     * three-dimensional vector as a 3-tuple floating point values.
     * @constructor
     * @this {XML3DRotation}
     * @param {XML3DVec3=} axis
     * @param {number=} angle
     * @param {function(XML3DVec3=)=} cb Called, if value has changed.
     *                                   Has this as first parameter.
     */
    var XML3DRotation = function(axis, angle, cb) {
        var that = this;
        this._data = new Float32Array(4);

        var vec_cb = function() {
            that._updateQuaternion();
            if (that._callback)
                that._callback(that);
        };

        /** @private */
        this._axis = new window.XML3DVec3(0, 0, 1, vec_cb);
        /** @private */
        this._angle = 0;

        this._updateQuaternion();

        if(axis !== undefined && axis !== null) {
            this.set(axis, angle);
        }

        /** @private */
        this._callback = typeof cb == 'function' ? cb : 0;
    };

    var p = XML3DRotation.prototype;

    /**
     * The set method copies the values from other.
     * @param {Object} other another XML3DRotation, Float32Array or XML3DVec3. In the last case the 2nd argument is considered.
     * @param {number=} angle
     */
    p.set = function(other, angle) {
        if(other.axis && other.angle !== undefined) {
            this.setAxisAngle(other.axis, other.angle);
        } else if(other.length && other.length >= 4) {
            this._setQuaternion(other);
        } else if(other._data && other._data.length && other._data.length === 3) {
            this.setAxisAngle(other, angle);
        } else {
            XML3D.debug.logError("XML3DRotation.set(): invalid argument given. Expect XML3DRotation or Float32Array.");
        }
    };

    /** @type {number} */
    Object.defineProperty(p, "axis", {
        /** @this {XML3DRotation} * */
        get : function() {
            return this._axis;
        },
        set : function() {
            throw Error("Can't set axis. XML3DRotation::axis is readonly.");
        },
        configurable : false,
        enumerable : false
    });

    /** @type {number} */
    Object.defineProperty(p, "angle", {
        /** @this {XML3DRotation} * */
        get : function() {
            return this._angle;
        },
        set : function(angle) {
            this._angle = angle;
            this._updateQuaternion();
            if (this._callback)
                this._callback(this);
    },
    configurable : false,
    enumerable : false
    });

    /**
     * String representation of the XML3DRotation.
     * @override
     * @this {XML3DRotation}
     * @return {string} Human-readable representation of this XML3DRotation.
     */
    p.toString = function() {
        return "[object XML3DRotation]";
    };

    /**
     * Replaces the existing rotation with the axis-angle representation passed
     * as argument
     */
    p.setAxisAngle = function(axis, angle) {
        if (typeof axis != 'object' || isNaN(angle)) {
            throw new Error("Illegal axis and/or angle values: " + "( axis="
                    + axis + " angle=" + angle + " )");
        }

        // TODO: slice?
        this._axis._data[0] = axis._data[0];
        this._axis._data[1] = axis._data[1];
        this._axis._data[2] = axis._data[2];
        this._angle = angle;
        this._updateQuaternion();
        if (this._callback)
            this._callback(this);
    };

    /**
     * Replaces the existing rotation with one computed from the two vectors
     * passed as arguments. {XML3DVec} from First vector {XML3DVec} from Second
     * vector
     */
    p.setRotation = function(from, to) {
        var a = from.normalize();
        var b = to.normalize();

        var axis = a.cross(b);
        if (!axis.length()) {
            // from and to are parallel
            axis = orthogonal(a);
        };
        // This function will also callback
        this.setAxisAngle(axis, Math.acos(a.dot(b)));
    };

    p._updateQuaternion = function() {
        var l = this._axis.length();
        if (l > 0.00001) {
            var s = Math.sin(this._angle / 2) / l;
            this._data[0] = this._axis.x * s;
            this._data[1] = this._axis.y * s;
            this._data[2] = this._axis.z * s;
            this._data[3] = Math.cos(this._angle / 2);
        } else {
            XML3D.math.quat.set(this._data, 0, 0, 0, 1);
        }
    };

    /**
     * Replaces the existing matrix with one computed from parsing the passed
     * string.
     * @param str String to parse
     */
    p.setAxisAngleValue = function(str) {
        var m = /^\s*(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s*$/.exec(str);
        if (!m)
            throw new Error("Could not parse AxisAngle string: " + str);

        // This function will also callback
        this.setAxisAngle(new window.XML3DVec3(+m[1], +m[2], +m[3]), +m[4]);
    };

    /**
     * Linear interpolation of this rotation rot0 with the passed rotation rot1
     * with factor t. The result is (1-t)rot0 + t rot1. Typically realized with
     * a spherical linear interpolation based on quaternions.
     * @param {XML3DRotation} rot1 the passed rotation
     * @param {number} t the factor
     */
    p.interpolate = function(rot1, t) {
        var dest = XML3D.math.quat.create(), result = new XML3DRotation();
        XML3D.math.quat.slerp(dest, this._data, rot1._data, t);
        result._setQuaternion(dest);
        return result;
    };

    /**
     * Replaces the existing rotation with the quaternion representation passed
     * as argument
     * @param {XML3DVec3} vector
     * @param {number} w
     */
    p.setQuaternion = function(vector, scalar) {
        this._setQuaternion( [ vector.x, vector.y, vector.z, scalar ]);
    };

    /**
     * Returns a XML3DMatrix that describes this 3D rotation in a
     * 4x4 matrix representation.
     * @return {XML3DMatrix} Rotation matrix
     */
    p.toMatrix = function() {
        var q = XML3D.math.quat.copy(XML3D.math.quat.create(), this._data);
        var m = new window.XML3DMatrix();
        XML3D.math.mat4.fromRotationTranslation(m._data, q, [0, 0, 0]);
        return m;
    };

    /**
     * Rotates the vector passed as parameter with this rotation
     * representation. The result is returned as new vector instance.
     * Neither this nor the inputVector are changed.
     * 4x4 matrix representation.
     * @param {XML3DVec3} inputVector
     * @return {XML3DVec3} The rotated vector
     */
    p.rotateVec3 = function(inputVector) {
        var result = new window.XML3DVec3();
        XML3D.math.vec3.transformQuat(result._data, inputVector._data, this._data)
        return result;
    };

    /**
     * Replaces the existing rotation with the quaternion representation passed
     * as argument
     * @private
     * @param {Array} quat
     */
    p._setQuaternion = function(q) {
        var s = Math.sqrt(1 - q[3] * q[3]);
        if (s < 0.001 || isNaN(s)) {
            this._axis._data[0] = 0;
            this._axis._data[1] = 0;
            this._axis._data[2] = 1;
            this._angle = 0;
        } else {
            s = 1 / s;
            this._axis._data[0] = q[0] * s;
            this._axis._data[1] = q[1] * s;
            this._axis._data[2] = q[2] * s;
            this._angle = 2 * Math.acos(q[3]);
        }
        this._data = XML3D.math.quat.copy(XML3D.math.quat.create(), q);
        if (this._callback)
            this._callback(this);
    };

    /**
     * Multiplies this rotation with the passed rotation. This rotation is not
     * changed.
     *
     * @param {XML3DRotation} rot1
     * @return {XML3DVec3} The result
     */
    p.multiply = function(rot1) {
        var result = new XML3DRotation(), q = XML3D.math.quat.create();
        XML3D.math.quat.multiply(q, this._data, rot1._data);
        result._setQuaternion(q);
        return result;
    };

    /**
     * Returns the normalized version of this rotation. Result is a newly
     * created vector. This is not modified.
     */
    p.normalize = function(that) {
        var na = this._axis.normalize();
        return new XML3DRotation(na, this._angle);
    };

    /**
     * Returns the quaternion, that underlies this rotation.
     *
     * @return {Float32Array}
     */
    p.getQuaternion = function() {
        return XML3D.math.quat.copy(XML3D.math.quat.create(), this._data);
    };

    /**
     * Set this rotation based on the given base vectors.
     *
     * @param {XML3DVec3} xAxis
     * @param {XML3DVec3} yAxis
     * @param {XML3DVec3} zAxis
     */
    p.setFromBasis = function(xAxis, yAxis, zAxis) {
        var q = XML3D.math.quat.create();
        XML3D.math.quat.setFromBasis(xAxis._data, yAxis._data, zAxis._data, q);
        this._setQuaternion(q);
    };

    XML3D.XML3DRotation = XML3DRotation;
    window.XML3DRotation = XML3DRotation;

}(XML3D._native));
<|endoftext|>


<commit_before>var fs = require('fs');
var path = require('path');

var constants = require('../../tasks/util/constants');
var getOptions = require('../../tasks/util/get_image_request_options');

// packages inside the image server docker
var request = require('request');
var test = require('tape');
var gm = require('gm');

var touch = function(fileName) {
    fs.closeSync(fs.openSync(fileName, 'w'));
};

// make artifact folders
if(!fs.existsSync(constants.pathToTestImagesDiff)) {
    fs.mkdirSync(constants.pathToTestImagesDiff);
}
if(!fs.existsSync(constants.pathToTestImages)) {
    fs.mkdirSync(constants.pathToTestImages);
}

var userFileName = process.argv[2];

// run the test(s)
if(!userFileName) runAll();
else runSingle(userFileName);

function runAll () {
    test('testing mocks', function (t) {

        var allMocks = fs.readdirSync(constants.pathToTestImageMocks);

        /*
         * Some test cases exhibit run-to-run randomness;
         * skip over these few test cases for now.
         *
         * More info:
         * https://github.com/plotly/plotly.js/issues/62
         *
         * 41 test cases are removed:
         * - font-wishlist (1 test case)
         * - all gl2d (38)
         * - gl3d_bunny-hull (1)
         * - polar_scatter (1)
         */
        var mocks = allMocks.filter(function(mock) {
            return !(
                mock === 'font-wishlist.json' ||
                mock.indexOf('gl2d') !== -1 ||
                mock === 'gl3d_bunny-hull.json' ||
                mock === 'polar_scatter.json'
            );
        });

        var BASE_TIMEOUT = 500,
            BATCH_SIZE = 5,
            cnt = 0;

        for(var i = 0; i < allMocks.length; i++) {
            setTimeout(function() {
                testMock(allMocks[cnt++], t);
            }, BASE_TIMEOUT * Math.floor(i / BATCH_SIZE) * BATCH_SIZE);
        t.plan(mocks.length);

        }

    });
}

function runSingle (userFileName) {
    test('testing single mock: ' + userFileName, function (t) {
        t.plan(1);
        testMock(userFileName, t);
    });
}

function testMock (fileName, t) {
    var figure = require(path.join(constants.pathToTestImageMocks, fileName));
    var bodyMock = {
        figure: figure,
        format: 'png',
        scale: 1
    };

    var imageFileName = fileName.split('.')[0] + '.png';
    var savedImagePath = path.join(constants.pathToTestImages, imageFileName);
    var diffPath = path.join(constants.pathToTestImagesDiff, 'diff-' + imageFileName);
    var savedImageStream = fs.createWriteStream(savedImagePath);
    var options = getOptions(bodyMock, 'http://localhost:9010/');

    function checkImage () {
        var options = {
            file: diffPath,
            highlightColor: 'purple',
            tolerance: 0.0
        };

        gm.compare(
            savedImagePath,
            path.join(constants.pathToTestImageBaselines, imageFileName),
            options,
            onEqualityCheck
        );
    }

    function onEqualityCheck (err, isEqual) {
        if (err) {
            touch(diffPath);
            return console.error(err, imageFileName);
        }
        if (isEqual) {
            fs.unlinkSync(diffPath);
        }

        t.ok(isEqual, imageFileName + ' should be pixel perfect');
    }

    request(options)
        .pipe(savedImageStream)
        .on('close', checkImage);
}
<commit_msg>lint + add comments<commit_after>var fs = require('fs');
var path = require('path');

var constants = require('../../tasks/util/constants');
var getOptions = require('../../tasks/util/get_image_request_options');

// packages inside the image server docker
var request = require('request');
var test = require('tape');
var gm = require('gm');

var touch = function(fileName) {
    fs.closeSync(fs.openSync(fileName, 'w'));
};

// make artifact folders
if(!fs.existsSync(constants.pathToTestImagesDiff)) {
    fs.mkdirSync(constants.pathToTestImagesDiff);
}
if(!fs.existsSync(constants.pathToTestImages)) {
    fs.mkdirSync(constants.pathToTestImages);
}

var userFileName = process.argv[2];

// run the test(s)
if(!userFileName) runAll();
else runSingle(userFileName);

function runAll () {
    test('testing mocks', function (t) {

        var allMocks = fs.readdirSync(constants.pathToTestImageMocks);

        /*
         * Some test cases exhibit run-to-run randomness;
         * skip over these few test cases for now.
         *
         * More info:
         * https://github.com/plotly/plotly.js/issues/62
         *
         * 41 test cases are removed:
         * - font-wishlist (1 test case)
         * - all gl2d (38)
         * - gl3d_bunny-hull (1)
         * - polar_scatter (1)
         */
        var mocks = allMocks.filter(function(mock) {
            return !(
                mock === 'font-wishlist.json' ||
                mock.indexOf('gl2d') !== -1 ||
                mock === 'gl3d_bunny-hull.json' ||
                mock === 'polar_scatter.json'
            );
        });

        var BASE_TIMEOUT = 500,  // base timeout time
            BATCH_SIZE = 5,      // size of each test 'batch'
            cnt = 0;

        function testFunction() {
            testMock(mocks[cnt++], t);
        }

        t.plan(mocks.length);

        for(var i = 0; i < mocks.length; i++) {
            setTimeout(testFunction,
                BASE_TIMEOUT * Math.floor(i / BATCH_SIZE) * BATCH_SIZE);
        }

    });
}

function runSingle (userFileName) {
    test('testing single mock: ' + userFileName, function (t) {
        t.plan(1);
        testMock(userFileName, t);
    });
}

function testMock (fileName, t) {
    var figure = require(path.join(constants.pathToTestImageMocks, fileName));
    var bodyMock = {
        figure: figure,
        format: 'png',
        scale: 1
    };

    var imageFileName = fileName.split('.')[0] + '.png';
    var savedImagePath = path.join(constants.pathToTestImages, imageFileName);
    var diffPath = path.join(constants.pathToTestImagesDiff, 'diff-' + imageFileName);
    var savedImageStream = fs.createWriteStream(savedImagePath);
    var options = getOptions(bodyMock, 'http://localhost:9010/');

    function checkImage () {
        var options = {
            file: diffPath,
            highlightColor: 'purple',
            tolerance: 0.0
        };

        gm.compare(
            savedImagePath,
            path.join(constants.pathToTestImageBaselines, imageFileName),
            options,
            onEqualityCheck
        );
    }

    function onEqualityCheck (err, isEqual) {
        if (err) {
            touch(diffPath);
            return console.error(err, imageFileName);
        }
        if (isEqual) {
            fs.unlinkSync(diffPath);
        }

        t.ok(isEqual, imageFileName + ' should be pixel perfect');
    }

    request(options)
        .pipe(savedImageStream)
        .on('close', checkImage);
}
<|endoftext|>


<commit_before>var fs = require('fs');

var getMockList = require('./assets/get_mock_list');
var getRequestOpts = require('./assets/get_image_request_options');
var getImagePaths = require('./assets/get_image_paths');

// packages inside the image server docker
var test = require('tape');
var request = require('request');
var gm = require('gm');

// pixel comparison tolerance
var TOLERANCE = 1e-6;

// wait time between each test batch
var BATCH_WAIT = 500;

// number of tests in each test batch
var BATCH_SIZE = 5;

// wait time between each test in test queue
var QUEUE_WAIT = 10;

/**
 *  Image pixel comparison test script.
 *
 *  Called by `tasks/test_image.sh in `npm run test-image`.
 *
 *  CLI arguments:
 *
 *  1. 'pattern' : glob determining which mock(s) are to be tested
 *  2. --queue : if sent, the image will be run in queue instead of in batch.
 *      Makes the test run significantly longer, but is recommended on weak hardware.
 *
 *  Examples:
 *
 *  Run all tests in batch:
 *
 *      npm run test-image
 *
 *  Run the 'contour_nolines' test:
 *
 *      npm run test-image -- contour_nolines
 *
 *  Run all gl3d image test in queue:
 *
 *      npm run test-image -- gl3d_* --queue
 */

var pattern = process.argv[2];
var mockList = getMockList(pattern);
var isInQueue = (process.argv[3] === '--queue');

if(mockList.length === 0) {
    throw new Error('No mocks found with pattern ' + pattern);
}

mockList = mockList.filter(untestableFilter);

if(mockList.length === 0) {
    throw new Error('All mocks found with pattern ' + pattern + ' are currently untestable');
}

// main
if(isInQueue) {
    runInQueue(mockList);
}
else {
    runInBatch(mockList);
}

/* Test cases:
 *
 * - font-wishlist
 * - all gl2d
 * - all mapbox
 *
 * don't behave consistently from run-to-run and/or
 * machine-to-machine; skip over them for now.
 *
 */
function untestableFilter(mockName) {
    return !(
        mockName === 'font-wishlist' ||
        mockName.indexOf('gl2d_') !== -1 ||
        mockName.indexOf('mapbox_') !== -1
    );
}

function runInBatch(mockList) {
    var running = 0;

    // remove mapbox mocks if circle ci

    test('testing mocks in batch', function(t) {
        t.plan(mockList.length);

        for(var i = 0; i < mockList.length; i++) {
            run(mockList[i], t);
        }
    });

    function run(mockName, t) {
        if(running >= BATCH_SIZE) {
            setTimeout(function() {
                run(mockName, t);
            }, BATCH_WAIT);
            return;
        }
        running++;

        // throttle the number of tests running concurrently

        comparePixels(mockName, function(isEqual, mockName) {
            running--;
            t.ok(isEqual, mockName + ' should be pixel perfect');
        });
    }
}

function runInQueue(mockList) {
    var index = 0;

    test('testing mocks in queue', function(t) {
        t.plan(mockList.length);

        run(mockList[index], t);
    });

    function run(mockName, t) {
        comparePixels(mockName, function(isEqual, mockName) {
            t.ok(isEqual, mockName + ' should be pixel perfect');

            index++;
            if(index < mockList.length) {
                setTimeout(function() {
                    run(mockList[index], t);
                }, QUEUE_WAIT);
            }
        });
    }
}

function comparePixels(mockName, cb) {
    var requestOpts = getRequestOpts({ mockName: mockName }),
        imagePaths = getImagePaths(mockName),
        saveImageStream = fs.createWriteStream(imagePaths.test);

    function checkImage() {
        var gmOpts = {
            file: imagePaths.diff,
            highlightColor: 'purple',
            tolerance: TOLERANCE
        };

        /*
         * N.B. The non-zero tolerance was added in
         * https://github.com/plotly/plotly.js/pull/243
         * where some legend mocks started generating different png outputs
         * on `npm run test-image` and `npm run test-image -- mock.json`.
         *
         * Note that the svg outputs for the problematic mocks were the same
         * and playing around with the batch size and timeout durations
         * did not seem to affect the results.
         *
         * With the above tolerance individual `npm run test-image` and
         * `npm run test-image -- mock.json` give the same result.
         *
         * Further investigation is needed.
         */

        gm.compare(
            imagePaths.test,
            imagePaths.baseline,
            gmOpts,
            onEqualityCheck
        );
    }

    function onEqualityCheck(err, isEqual) {
        if(err) {
            touch(imagePaths.diff);
            return console.error(err, mockName);
        }
        if(isEqual) {
            fs.unlinkSync(imagePaths.diff);
        }

        cb(isEqual, mockName);
    }

    request(requestOpts)
        .pipe(saveImageStream)
        .on('close', checkImage);
}

function touch(filePath) {
    fs.closeSync(fs.openSync(filePath, 'w'));
}
<commit_msg>test-runner: check if baseline img exist before px comparison<commit_after>var fs = require('fs');

var getMockList = require('./assets/get_mock_list');
var getRequestOpts = require('./assets/get_image_request_options');
var getImagePaths = require('./assets/get_image_paths');

// packages inside the image server docker
var test = require('tape');
var request = require('request');
var gm = require('gm');

// pixel comparison tolerance
var TOLERANCE = 1e-6;

// wait time between each test batch
var BATCH_WAIT = 500;

// number of tests in each test batch
var BATCH_SIZE = 5;

// wait time between each test in test queue
var QUEUE_WAIT = 10;

/**
 *  Image pixel comparison test script.
 *
 *  Called by `tasks/test_image.sh in `npm run test-image`.
 *
 *  CLI arguments:
 *
 *  1. 'pattern' : glob determining which mock(s) are to be tested
 *  2. --queue : if sent, the image will be run in queue instead of in batch.
 *      Makes the test run significantly longer, but is recommended on weak hardware.
 *
 *  Examples:
 *
 *  Run all tests in batch:
 *
 *      npm run test-image
 *
 *  Run the 'contour_nolines' test:
 *
 *      npm run test-image -- contour_nolines
 *
 *  Run all gl3d image test in queue:
 *
 *      npm run test-image -- gl3d_* --queue
 */

var pattern = process.argv[2];
var mockList = getMockList(pattern);
var isInQueue = (process.argv[3] === '--queue');

if(mockList.length === 0) {
    throw new Error('No mocks found with pattern ' + pattern);
}

mockList = mockList.filter(untestableFilter);

if(mockList.length === 0) {
    throw new Error('All mocks found with pattern ' + pattern + ' are currently untestable');
}

// main
if(isInQueue) {
    runInQueue(mockList);
}
else {
    runInBatch(mockList);
}

/* Test cases:
 *
 * - font-wishlist
 * - all gl2d
 * - all mapbox
 *
 * don't behave consistently from run-to-run and/or
 * machine-to-machine; skip over them for now.
 *
 */
function untestableFilter(mockName) {
    return !(
        mockName === 'font-wishlist' ||
        mockName.indexOf('gl2d_') !== -1 ||
        mockName.indexOf('mapbox_') !== -1
    );
}

function runInBatch(mockList) {
    var running = 0;

    // remove mapbox mocks if circle ci

    test('testing mocks in batch', function(t) {
        t.plan(mockList.length);

        for(var i = 0; i < mockList.length; i++) {
            run(mockList[i], t);
        }
    });

    function run(mockName, t) {
        if(running >= BATCH_SIZE) {
            setTimeout(function() {
                run(mockName, t);
            }, BATCH_WAIT);
            return;
        }
        running++;

        // throttle the number of tests running concurrently

        comparePixels(mockName, function(isEqual, mockName) {
            running--;
            t.ok(isEqual, mockName + ' should be pixel perfect');
        });
    }
}

function runInQueue(mockList) {
    var index = 0;

    test('testing mocks in queue', function(t) {
        t.plan(mockList.length);

        run(mockList[index], t);
    });

    function run(mockName, t) {
        comparePixels(mockName, function(isEqual, mockName) {
            t.ok(isEqual, mockName + ' should be pixel perfect');

            index++;
            if(index < mockList.length) {
                setTimeout(function() {
                    run(mockList[index], t);
                }, QUEUE_WAIT);
            }
        });
    }
}

function comparePixels(mockName, cb) {
    var requestOpts = getRequestOpts({ mockName: mockName }),
        imagePaths = getImagePaths(mockName),
        saveImageStream = fs.createWriteStream(imagePaths.test);

    function checkImage() {

        // baseline image must be generated first
        if(!doesFileExist(imagePaths.baseline)) {
            var err = new Error('baseline image not found');
            return onEqualityCheck(err, false);
        }

        /*
         * N.B. The non-zero tolerance was added in
         * https://github.com/plotly/plotly.js/pull/243
         * where some legend mocks started generating different png outputs
         * on `npm run test-image` and `npm run test-image -- mock.json`.
         *
         * Note that the svg outputs for the problematic mocks were the same
         * and playing around with the batch size and timeout durations
         * did not seem to affect the results.
         *
         * With the above tolerance individual `npm run test-image` and
         * `npm run test-image -- mock.json` give the same result.
         *
         * Further investigation is needed.
         */

        var gmOpts = {
            file: imagePaths.diff,
            highlightColor: 'purple',
            tolerance: TOLERANCE
        };

        gm.compare(
            imagePaths.test,
            imagePaths.baseline,
            gmOpts,
            onEqualityCheck
        );
    }

    function onEqualityCheck(err, isEqual) {
        if(err) {
            touch(imagePaths.diff);
            return console.error(err, mockName);
        }
        if(isEqual) {
            fs.unlinkSync(imagePaths.diff);
        }

        cb(isEqual, mockName);
    }

    request(requestOpts)
        .pipe(saveImageStream)
        .on('close', checkImage);
}

function doesFileExist(filePath) {
    try {
        if(fs.statSync(filePath).isFile()) return true;
    }
    catch(e) {
        return false;
    }

    return false;
}

function touch(filePath) {
    fs.closeSync(fs.openSync(filePath, 'w'));
}
<|endoftext|>


<commit_before>import {mobileQuery, responsiveAction} from 'ghost/utils/mobile';

var PostsView = Ember.View.extend({
    target: Ember.computed.alias('controller'),
    classNames: ['content-view-container'],
    tagName: 'section',

    mobileInteractions: function () {
        Ember.run.scheduleOnce('afterRender', this, function () {
            var self = this;

            $(window).resize(function () {
                if (!mobileQuery.matches) {
                    self.send('resetContentPreview');
                }
            });

            // ### Show content preview when swiping left on content list
            $('.manage').on('click', '.content-list ol li', function (event) {
                responsiveAction(event, '(max-width: 800px)', function () {
                    self.send('showContentPreview');
                });
            });

            // ### Hide content preview
            $('.manage').on('click', '.content-preview .button-back', function (event) {
                responsiveAction(event, '(max-width: 800px)', function () {
                    self.send('hideContentPreview');
                });
            });
        });
    }.on('didInsertElement'),
});

export default PostsView;
<commit_msg>Fix Ghost icon is not clickable<commit_after>import {mobileQuery, responsiveAction} from 'ghost/utils/mobile';

var PostsView = Ember.View.extend({
    target: Ember.computed.alias('controller'),
    classNames: ['content-view-container'],
    tagName: 'section',

    mobileInteractions: function () {
        Ember.run.scheduleOnce('afterRender', this, function () {
            var self = this;

            $(window).resize(function () {
                if (!mobileQuery.matches) {
                    self.send('resetContentPreview');
                }
            });

            // ### Show content preview when swiping left on content list
            $('.manage').on('click', '.content-list ol li', function (event) {
                responsiveAction(event, '(max-width: 800px)', function () {
                    self.send('showContentPreview');
                });
            });

            // ### Hide content preview
            $('.manage').on('click', '.content-preview .button-back', function (event) {
                responsiveAction(event, '(max-width: 800px)', function () {
                    self.send('hideContentPreview');
                });
            });
            $('[data-off-canvas]').attr('href', this.get('controller.ghostPaths.blogRoot'));
        });
    }.on('didInsertElement'),
});

export default PostsView;
<|endoftext|>


<commit_before>// Copyright 2010 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Helper functions for registering gjstest test cases. Use it as follows:
//
//     /** @constructor */
//     function MyTestFixture() {
//       // This constructor will be called for each test. Do per-test setup
//       // here.
//       this.objectUnderTest_ = ...;
//     }
//     registerTestSuite(MyTestFixture);
//
//     // Add a test case named ReturnsFalse to the fixture. The full name of
//     // test, as reported in the test output, is MyTestFixture.ReturnsFalse.
//     addTest(MyTestFixture, function ReturnsFalse() {
//       expectFalse(this.objectUnderTest_.bar());
//     });
//

/**
 * Register a test constructor to be executed by the test runner.
 *
 * Any enumerable property of ctor.prototype whose value is a function will be
 * treated as a test function, unless:
 *
 *  *  The property's name ends with an underscore. Use this to create private
 *     helper functions.
 *
 *  *  The property's name is 'tearDown'. This name is reserved for tear-down
 *     helpers. If you use leading-upper-case CamelCase for test names, you
 *     don't need to worry about this exception.
 *
 * Rather than attaching tests to ctor.prototype directly, consider using the
 * addTest function below. See its documentation for the benefits of doing so.
 *
 * @param {!Function} ctor
 *     A constructor for the test suite class.
 */
gjstest.registerTestSuite = function(ctor) {
  if (!(ctor instanceof Function)) {
    throw new TypeError('registerTestSuite() requires a function');
  }

  // Make sure this constructor hasn't already been registered.
  if (gjstest.internal.testSuites.indexOf(ctor) != -1) {
    throw new Error('Test suite already registered: ' + ctor.name);
  }

  gjstest.internal.testSuites.push(ctor);
};

/**
 * Add a test function to the supplied test suite. The function's name is used
 * to decide on the test name, so it must have one (see the top of the file for
 * an example).
 *
 * The function's name must not fit into one of the exceptions on test function
 * names listed above; in this case addTest will throw an error to make sure
 * that you realize your test will not be executed.
 *
 * Note that this function *does* correctly handle the case of magic property
 * names like 'constructor'. JS adds a 'constructor' property to Foo.prototype
 * for any function named Foo, but marks it as non-enumerable. The
 * non-enumerable bit is sticky, which means that if you simply say
 *
 *     MyTest.prototype.constructor = function() {
 *       expectTrue(false);
 *     };
 *
 * then the test function will never be run. In contrast, saying
 *
 *     addTest(MyTest, function constructor() {
 *       expectTrue(false);
 *     });
 *
 * will work as expected.
 *
 * @param {!Function} testSuite
 *     The test suite class, which must have previously been registered with
 *     registerTestSuite.
 *
 * @param {!Function} testFunc
 *     The test function.
 */
gjstest.addTest = function(testSuite, testFunc) {
  // Check types.
  if (!(testSuite instanceof Function)) {
    throw new TypeError('addTest() requires a function for the test suite.');
  }

  if (!(testFunc instanceof Function)) {
    throw new TypeError('addTest() requires a function for the test function.');
  }

  // Make sure the suite has been registered.
  if (gjstest.internal.testSuites.indexOf(testSuite) == -1) {
    throw new Error('Test suite has not been registered: ' + testSuite.name);
  }

  // Make sure the test function's name is legal.
  var testFuncName = testFunc.name;
  if (!testFuncName) {
    throw new Error('Test functions must have names.');
  }

  if (/_$/.test(testFuncName) || testFuncName == 'tearDown') {
    throw new Error('Illegal test function name: ' + testFuncName);
  }

  // Make sure the test name hasn't already been used. We must check both for
  // the existence of the property and its enumerability because there may be a
  // default non-emurable property (e.g. 'constructor' or 'prototype').
  var suitePrototype = testSuite.prototype;
  var hasOwnProperty = Object.prototype.hasOwnProperty;
  var propertyIsEnumerable = Object.prototype.propertyIsEnumerable;

  if (hasOwnProperty.apply(suitePrototype, [testFuncName]) &&
      propertyIsEnumerable.apply(suitePrototype, [testFuncName])) {
    throw new Error('Test function already registered: ' + testFuncName);
  }

  // Make sure the property is enumerable.
  Object.defineProperty(
      suitePrototype,
      testFuncName,
      {
        value: testFunc,
        writable: true,
        configurable: true,
        enumerable: true,
      });
};

////////////////////////////////////////////////////////////////////////
// Implementation details
////////////////////////////////////////////////////////////////////////

/**
 * A list of test suites that have been registered.
 * @type {!Array.<!Function>}
 */
gjstest.internal.testSuites = [];

/**
 * Given a constructor and the name of a test method on that contructor, return
 * a function that will execute the test.
 *
 * @param {!Function} ctor
 * @param {string} propertyName
 * @return {function()}
 *
 * @private
 */
gjstest.internal.makeTestFunction_ = function(ctor, propertyName) {
  return function() {
    // Run the test, making sure we run the tearDown method, if any, regardless
    // of whether an error is thrown.
    try {
      var instance = new ctor();
      instance[propertyName]();
    } finally {
      // NOTE(jacobsa): We quote 'tearDown' to stop the complaining the JS
      // compiler does as of 2011-01-19.
      var tearDown = instance && instance['tearDown'];
      tearDown && tearDown.apply(instance);
    }
  };
};

/**
 * Given a constructor registered with registerTestSuite, return a map from full
 * test names (e.g. FooTest.doesBar) to functions that can be executed to run
 * the particular test.
 *
 * @param {!Function} ctor
 * @return {!Object.<function()>}
 */
gjstest.internal.getTestFunctions = function(ctor) {
  var result = {};

  function addTestFunction(name) {
    // Compute the full name for the test, and create a function that performs
    // the appropriate test.
    var fullName = ctor.name + '.' + name;
    result[fullName] = gjstest.internal.makeTestFunction_(ctor, name);
  }

  // Consider each enumerable key belonging directly to the constructor's
  // prototype.
  Object.keys(ctor.prototype).forEach(function(key) {
    // Skip this property if it's private or the tearDown method.
    if (/_$/.test(key) || key == 'tearDown') {
      return;
    }

    // Skip this property if it's not a function.
    if (!(ctor.prototype[key] instanceof Function)) {
      return;
    }

    addTestFunction(key);
  });

  return result;
};
<commit_msg>Fixed a trailing comma.<commit_after>// Copyright 2010 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Helper functions for registering gjstest test cases. Use it as follows:
//
//     /** @constructor */
//     function MyTestFixture() {
//       // This constructor will be called for each test. Do per-test setup
//       // here.
//       this.objectUnderTest_ = ...;
//     }
//     registerTestSuite(MyTestFixture);
//
//     // Add a test case named ReturnsFalse to the fixture. The full name of
//     // test, as reported in the test output, is MyTestFixture.ReturnsFalse.
//     addTest(MyTestFixture, function ReturnsFalse() {
//       expectFalse(this.objectUnderTest_.bar());
//     });
//

/**
 * Register a test constructor to be executed by the test runner.
 *
 * Any enumerable property of ctor.prototype whose value is a function will be
 * treated as a test function, unless:
 *
 *  *  The property's name ends with an underscore. Use this to create private
 *     helper functions.
 *
 *  *  The property's name is 'tearDown'. This name is reserved for tear-down
 *     helpers. If you use leading-upper-case CamelCase for test names, you
 *     don't need to worry about this exception.
 *
 * Rather than attaching tests to ctor.prototype directly, consider using the
 * addTest function below. See its documentation for the benefits of doing so.
 *
 * @param {!Function} ctor
 *     A constructor for the test suite class.
 */
gjstest.registerTestSuite = function(ctor) {
  if (!(ctor instanceof Function)) {
    throw new TypeError('registerTestSuite() requires a function');
  }

  // Make sure this constructor hasn't already been registered.
  if (gjstest.internal.testSuites.indexOf(ctor) != -1) {
    throw new Error('Test suite already registered: ' + ctor.name);
  }

  gjstest.internal.testSuites.push(ctor);
};

/**
 * Add a test function to the supplied test suite. The function's name is used
 * to decide on the test name, so it must have one (see the top of the file for
 * an example).
 *
 * The function's name must not fit into one of the exceptions on test function
 * names listed above; in this case addTest will throw an error to make sure
 * that you realize your test will not be executed.
 *
 * Note that this function *does* correctly handle the case of magic property
 * names like 'constructor'. JS adds a 'constructor' property to Foo.prototype
 * for any function named Foo, but marks it as non-enumerable. The
 * non-enumerable bit is sticky, which means that if you simply say
 *
 *     MyTest.prototype.constructor = function() {
 *       expectTrue(false);
 *     };
 *
 * then the test function will never be run. In contrast, saying
 *
 *     addTest(MyTest, function constructor() {
 *       expectTrue(false);
 *     });
 *
 * will work as expected.
 *
 * @param {!Function} testSuite
 *     The test suite class, which must have previously been registered with
 *     registerTestSuite.
 *
 * @param {!Function} testFunc
 *     The test function.
 */
gjstest.addTest = function(testSuite, testFunc) {
  // Check types.
  if (!(testSuite instanceof Function)) {
    throw new TypeError('addTest() requires a function for the test suite.');
  }

  if (!(testFunc instanceof Function)) {
    throw new TypeError('addTest() requires a function for the test function.');
  }

  // Make sure the suite has been registered.
  if (gjstest.internal.testSuites.indexOf(testSuite) == -1) {
    throw new Error('Test suite has not been registered: ' + testSuite.name);
  }

  // Make sure the test function's name is legal.
  var testFuncName = testFunc.name;
  if (!testFuncName) {
    throw new Error('Test functions must have names.');
  }

  if (/_$/.test(testFuncName) || testFuncName == 'tearDown') {
    throw new Error('Illegal test function name: ' + testFuncName);
  }

  // Make sure the test name hasn't already been used. We must check both for
  // the existence of the property and its enumerability because there may be a
  // default non-emurable property (e.g. 'constructor' or 'prototype').
  var suitePrototype = testSuite.prototype;
  var hasOwnProperty = Object.prototype.hasOwnProperty;
  var propertyIsEnumerable = Object.prototype.propertyIsEnumerable;

  if (hasOwnProperty.apply(suitePrototype, [testFuncName]) &&
      propertyIsEnumerable.apply(suitePrototype, [testFuncName])) {
    throw new Error('Test function already registered: ' + testFuncName);
  }

  // Make sure the property is enumerable.
  Object.defineProperty(
      suitePrototype,
      testFuncName,
      {
        value: testFunc,
        writable: true,
        configurable: true,
        enumerable: true
      });
};

////////////////////////////////////////////////////////////////////////
// Implementation details
////////////////////////////////////////////////////////////////////////

/**
 * A list of test suites that have been registered.
 * @type {!Array.<!Function>}
 */
gjstest.internal.testSuites = [];

/**
 * Given a constructor and the name of a test method on that contructor, return
 * a function that will execute the test.
 *
 * @param {!Function} ctor
 * @param {string} propertyName
 * @return {function()}
 *
 * @private
 */
gjstest.internal.makeTestFunction_ = function(ctor, propertyName) {
  return function() {
    // Run the test, making sure we run the tearDown method, if any, regardless
    // of whether an error is thrown.
    try {
      var instance = new ctor();
      instance[propertyName]();
    } finally {
      // NOTE(jacobsa): We quote 'tearDown' to stop the complaining the JS
      // compiler does as of 2011-01-19.
      var tearDown = instance && instance['tearDown'];
      tearDown && tearDown.apply(instance);
    }
  };
};

/**
 * Given a constructor registered with registerTestSuite, return a map from full
 * test names (e.g. FooTest.doesBar) to functions that can be executed to run
 * the particular test.
 *
 * @param {!Function} ctor
 * @return {!Object.<function()>}
 */
gjstest.internal.getTestFunctions = function(ctor) {
  var result = {};

  function addTestFunction(name) {
    // Compute the full name for the test, and create a function that performs
    // the appropriate test.
    var fullName = ctor.name + '.' + name;
    result[fullName] = gjstest.internal.makeTestFunction_(ctor, name);
  }

  // Consider each enumerable key belonging directly to the constructor's
  // prototype.
  Object.keys(ctor.prototype).forEach(function(key) {
    // Skip this property if it's private or the tearDown method.
    if (/_$/.test(key) || key == 'tearDown') {
      return;
    }

    // Skip this property if it's not a function.
    if (!(ctor.prototype[key] instanceof Function)) {
      return;
    }

    addTestFunction(key);
  });

  return result;
};
<|endoftext|>