<commit_before>// ==UserScript==
// @name        yt-looper
// @description Adds a button on YouTube, Imgur and SoundCloud to open current resource in yt.aergia.eu looper
// @namespace   https://yt.aergia.eu
// @icon        https://i.imgur.com/EGgL1nx.png
// @include     http://www.youtube.com/watch*
// @include     http://youtube.com/watch*
// @include     https://www.youtube.com/watch*
// @include     https://youtube.com/watch*
// @include     https://imgur.com/*
// @include     http://imgur.com/*
// @include     http://soundcloud.com/*
// @include     https://soundcloud.com/*
// @version     1.6.2
// @updateURL   https://yt.aergia.eu/yt-looper.user.js
// @downloadURL https://yt.aergia.eu/yt-looper.user.js
// @require     https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @grant       none
// ==/UserScript==
(function ($, undefined) {
  'use strict';
  var youtubeHandler = function () {
    var humanize = function (sec) {
      var sec_num = parseInt(sec, 10);
      var hours = Math.floor(sec_num / 3600);
      var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
      var seconds = sec_num - (hours * 3600) - (minutes * 60);
      hours = hours > 0 ? (hours + 'h')  : '';
      minutes = minutes > 0 ? (minutes + 'm')  : '';
      seconds = seconds > 0 ? (seconds + 's')  : '';
      return sec === 0 ? '0s' : hours + minutes + seconds;
    };
    var getYtPlayer = function () {
      var doc = window.document;
      var ytplayer = doc.getElementById('movie_player') || doc.getElementById('movie_player-flash');
      return ytplayer;
    };
    var renderLooperActions = function () {
      $('#yt-looper-start').remove();
      $('#yt-looper-end').remove();
      $('#yt-looper').remove();
      $('#yt-looper-interval').remove();
      var $button = $('<button id="yt-looper" class="yt-uix-button yt-uix-button-opacity yt-uix-button-has-icon yt-uix-tooltip" title="Open in yt-looper">↻ yt.aergia.eu </button>').hide();
      var $intervals = $('').hide();
      var input = '<input class="yt-uix-form-input-text title-input" style="width:50px!important;margin-right:0.25em;" />';
      var $secondaryActions = $('div.watch-action-buttons > div.watch-secondary-actions');
      var start = humanize(0);
      var end = humanize(getYtPlayer().getDuration());
      var $start = $(input).attr('id', 'yt-looper-start').val(start).attr('title', 'Start of loop');
      var $end = $(input).attr('id', 'yt-looper-end').val(end).attr('title', 'End of loop');
      $secondaryActions.prepend($button);
      $intervals.append($start).append($end).insertBefore($button);
      $button.show();
      $button.click(function () {
        var url = window.location.href;
        url = url.replace(/.*youtube.com\/watch/, 'https://yt.aergia.eu/');
        if (getYtPlayer().getPlayerState() === 2) {
          url = url.replace(/[&#]t=[^&#]*/g, '');
          url = url + '#t=' + $('#yt-looper-start').val() + ';' + $('#yt-looper-end').val();
        }
        window.open(url);
      });
    };
    window.onYouTubePlayerReady = function () {
      console.log('yt-looper @ onYouTubePlayerReady');
      var newVideo = true;
      window.ytPlayerStateChanged = function (state) {
        if (state === 2) {
          $('#yt-looper-start').val(humanize(getYtPlayer().getCurrentTime()));
          $('#yt-looper-interval').fadeIn('slow');
        } else if (state === 1) {
          // youtube redraws div.watch-action-buttons AFTER 5 and -1 events
          // and 1 is the only one we have after GUI stabilizes
          // ANGST.
          newVideo = true;
        } else {
          $('#yt-looper-interval').hide();
          $('#yt-looper-start').val(humanize(0));
        }
        if (newVideo) {
          renderLooperActions();
          newVideo = false;
        }
      };
      renderLooperActions();
      getYtPlayer().addEventListener('onStateChange', 'ytPlayerStateChanged');
    };
  };
  var imgurHandler = function () {
    var imgurId = window.location.pathname.replace(/\/(?:[^\/]+\/)*/, '');
    // display button only on single-image pages
    if (/[a-zA-Z0-9]+/.test(imgurId) && ($('div.post-image img').length === 1 || $('div.post-image video').length === 1)) {
      console.log('yt-looper @ imgurHandler()');
      var url = 'https://yt.aergia.eu/#i=' + imgurId;
      var html = '↻ yt.aergia.eu';
      var $a = $(html).click(function () {
        window.open(url);
      });
      $('#main-nav ul').append($('
').append($a));
    }
  };
  var soundCloudHandler = function () {
    if ($('#yt-looper').length === 0) {
      var $soundActions = $('div.l-about-top div.soundActions');
      if ($soundActions.length === 1) {
        console.log('soundCloudHandler(): adding yt-looper button..');
        var scId = window.location.pathname.replace(/^\//, '');
        var url = 'https://yt.aergia.eu/#s=' + scId;
        var $button = $('<button id="yt-looper" class="sc-button sc-button-medium" title="Open in yt-looper">↻ yt.aergia.eu</button>');
        $button.click(function () {
          window.open(url);
        });
        $('div.sc-button-group', $soundActions).first().append($button);
      }
    }
  };
  switch (window.location.hostname.replace('www.', '')) {
    case 'youtube.com':
      youtubeHandler();
      break;
    case 'imgur.com':
      imgurHandler();
      break;
    case 'soundcloud.com':
      // yeah.. so this happened..
      window.setInterval(soundCloudHandler, 1000);
      break;
  }
}(window.jQuery.noConflict(true)));
// vim:ts=2:sw=2:et:
<commit_msg>Userscript: fix new, reactive Imgur GUI<commit_after>// ==UserScript==
// @name        yt-looper
// @description Adds a button on YouTube, Imgur and SoundCloud to open current resource in yt.aergia.eu looper
// @namespace   https://yt.aergia.eu
// @icon        https://i.imgur.com/EGgL1nx.png
// @include     http://www.youtube.com/watch*
// @include     http://youtube.com/watch*
// @include     https://www.youtube.com/watch*
// @include     https://youtube.com/watch*
// @include     https://imgur.com/*
// @include     http://imgur.com/*
// @include     http://soundcloud.com/*
// @include     https://soundcloud.com/*
// @version     1.6.3
// @updateURL   https://yt.aergia.eu/yt-looper.user.js
// @downloadURL https://yt.aergia.eu/yt-looper.user.js
// @require     https://cdn.jsdelivr.net/jquery/3.0.0-alpha1/jquery.min.js
// @grant       none
// ==/UserScript==
(function ($, undefined) {
  'use strict';
  var youtubeHandler = function () {
    var humanize = function (sec) {
      var sec_num = parseInt(sec, 10);
      var hours = Math.floor(sec_num / 3600);
      var minutes = Math.floor((sec_num - (hours * 3600)) / 60);
      var seconds = sec_num - (hours * 3600) - (minutes * 60);
      hours = hours > 0 ? (hours + 'h')  : '';
      minutes = minutes > 0 ? (minutes + 'm')  : '';
      seconds = seconds > 0 ? (seconds + 's')  : '';
      return sec === 0 ? '0s' : hours + minutes + seconds;
    };
    var getYtPlayer = function () {
      var doc = window.document;
      var ytplayer = doc.getElementById('movie_player') || doc.getElementById('movie_player-flash');
      return ytplayer;
    };
    var renderLooperActions = function() {
      $('#yt-looper-start').remove();
      $('#yt-looper-end').remove();
      $('#yt-looper').remove();
      $('#yt-looper-interval').remove();
      var $button = $('<button id="yt-looper" class="yt-uix-button yt-uix-button-opacity yt-uix-button-has-icon yt-uix-tooltip" title="Open in yt-looper">↻ yt.aergia.eu </button>').hide();
      var $intervals = $('').hide();
      var input = '<input class="yt-uix-form-input-text title-input" style="width:50px!important;margin-right:0.25em;" />';
      var $secondaryActions = $('div.watch-action-buttons > div.watch-secondary-actions');
      var start = humanize(0);
      var end = humanize(getYtPlayer().getDuration());
      var $start = $(input).attr('id', 'yt-looper-start').val(start).attr('title', 'Start of loop');
      var $end = $(input).attr('id', 'yt-looper-end').val(end).attr('title', 'End of loop');
      $secondaryActions.prepend($button);
      $intervals.append($start).append($end).insertBefore($button);
      $button.show();
      $button.click(function () {
        var url = window.location.href;
        url = url.replace(/.*youtube.com\/watch/, 'https://yt.aergia.eu/');
        if (getYtPlayer().getPlayerState() === 2) {
          url = url.replace(/[&#]t=[^&#]*/g, '');
          url = url + '#t=' + $('#yt-looper-start').val() + ';' + $('#yt-looper-end').val();
        }
        window.open(url);
      });
    };

    var initButton = function() {
      window.onYouTubePlayerReady = function () {
        console.log('yt-looper @ onYouTubePlayerReady');
        var newVideo = true;
        window.ytPlayerStateChanged = function (state) {
          if (state === 2) {
            $('#yt-looper-start').val(humanize(getYtPlayer().getCurrentTime()));
            $('#yt-looper-interval').fadeIn('slow');
          } else if (state === 1) {
            // youtube redraws div.watch-action-buttons AFTER 5 and -1 events
            // and 1 is the only one we have after GUI stabilizes
            // ANGST.
            newVideo = true;
          } else {
            $('#yt-looper-interval').hide();
            $('#yt-looper-start').val(humanize(0));
          }
          if (newVideo) {
            renderLooperActions();
            newVideo = false;
          }
        };
        renderLooperActions();
        getYtPlayer().addEventListener('onStateChange', 'ytPlayerStateChanged');
      };
    };
    initButton();
  };
  var imgurHandler = function () {
    var $oldButton = $('li#yt-looper');
    var imgurId = window.location.pathname.replace(/\/(?:[^\/]+\/)*/, '');
    var renderButton = function() {
      // display button only on single-image pages
      if (/[a-zA-Z0-9]+/.test(imgurId) && ($('div.post-image img').length === 1 || $('div.post-image video').length === 1)) {
        console.log('yt-looper @ imgurHandler('+imgurId+')');
        var url = 'https://yt.aergia.eu/#i=' + imgurId;
        var html = '↻ yt.aergia.eu';
        var $a = $(html).click(function () {
          window.open(url);
        });
        var $newButton = $('
').append($a).data('imgurId', imgurId);
        $('#main-nav ul').append($newButton);

      }
    };
    if ($oldButton.length === 0 || $oldButton.data('imgurId') != imgurId) {
      $oldButton.remove();
      renderButton();
    }
  };
  var soundCloudHandler = function () {
    if ($('#yt-looper').length === 0) {
      var $soundActions = $('div.l-about-top div.soundActions');
      if ($soundActions.length === 1) {
        console.log('soundCloudHandler(): adding yt-looper button..');
        var scId = window.location.pathname.replace(/^\//, '');
        var url = 'https://yt.aergia.eu/#s=' + scId;
        var $button = $('<button id="yt-looper" class="sc-button sc-button-medium" title="Open in yt-looper">↻ yt.aergia.eu</button>');
        $button.click(function () {
          window.open(url);
        });
        $('div.sc-button-group', $soundActions).first().append($button);
      }
    }
  };
  switch (window.location.hostname.replace('www.', '')) {
    case 'youtube.com':
      youtubeHandler();
      break;
    case 'imgur.com':
      window.setInterval(imgurHandler, 1000); // quick hack for reactive gui
      break;
    case 'soundcloud.com':
      window.setInterval(soundCloudHandler, 1000); // same
      break;
  }
}(window.jQuery.noConflict(true)));
// vim:ts=2:sw=2:et:
<|endoftext|>


<commit_before>module.exports = {
  docdash: {
    scripts: ['ltsv.css'],
    sort: false
  },
  opts: {
    destination: './docs',
    private: true,
    readme: './README.md',
    recurse: true,
    template: './node_modules/@mocha/docdash'
  },
  source: {
    excludePattern: 'node_modules/',
    include: ['.'],
    includePattern: '\\.mjs$'
  }
};
<commit_msg>remove .jsdocrc.js<commit_after><|endoftext|>


<commit_before>/*global Arenite:true*/
/*jshint evil:true*/
// Collection of utility functions to handle loading resources.
Arenite.Loader = function (arenite) {

  var _createCORSRequest = function (method, url) {
    var xhr = new XMLHttpRequest();
    xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
    xhr.setRequestHeader("Access-Control-Allow-Origin", window.location.origin);
    if (arenite.config.withCredentials){
    if ('withCredentials' in xhr) {
      xhr.open(method, url, true);
      xhr.withCredentials = true;
    } else if (typeof XDomainRequest !== 'undefined') {
      xhr = new XDomainRequest();
      xhr.open(method, url);
    } else {
      xhr.open(method, url, true);
    }
    return xhr;
  };

  var _loadResource = function (url, callback, error) {
    var req = _createCORSRequest('GET', url);
    req.onreadystatechange = function () {
      if (req.readyState === 4) {
        if (req.status % 100 < 4) {
          callback(req);
        } else if (typeof error === 'function') {
          error(req);
        }
      }
    };
    req.send();
  };

  var _loadScriptWithTag = function (url, callback) {
    var head = document.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    script.async = true;
    script.type = 'text/javascript';
    if (navigator.appVersion.indexOf("MSIE 9") === -1) { //IE 9 calls the callback twice
      script.onreadystatechange = function () {
        if (this.readyState === 'complete') {
          callback();
        }
      };
    }
    script.onload = callback;
    script.src = url;
    head.appendChild(script);
  };

  var _loadScriptAsResource = function (url, callback) {
    _loadResource(url, function (req) {
      //analyze the script for "annotation" type configurations
      arenite.annotation.processAnnotations(req.responseText);
      var file_tag = '\n//@ sourceURL=' + window.location.origin + '/' + url + '\n//# sourceURL=' + window.location.origin + '/' + url;
      eval(req.responseText + file_tag);
      callback();
    });
  };

  var _sameOrigin = function (url) {
    var loc = window.location;
    var a = document.createElement('a');
    a.href = url;
    return a.hostname === loc.hostname &&
      a.port === loc.port &&
      a.protocol === loc.protocol;
  };

  var _loadScriptFrom = function (url, callback) {
    if (_sameOrigin(url)) {
      _loadScriptAsResource(url, callback);
    } else {
      _loadScriptWithTag(url, callback);
    }
  };

  var _loadScript = function (script, done) {
    if (typeof script === 'string') {
      _loadScriptFrom(script, done);
    } else {
      _loadScriptFrom(script.url, function () {
        arenite.object.keys(script.instances).forEach(function (instance) {
          arenite.context.add(instance, window[script.instances[instance]]);
          if (typeof script.init === 'function') {
            script.init(arenite);
          }
          delete window[script.instances[instance]];
        });
        if (typeof done === 'function') {
          done();
        }
      });
    }
  };

  return {
    loader: {
      //###loader.loadResource
      // Load a given resource using ajax.
      //

      // loadResource(url, callback, error)
      //

      //where *url* is the url to fetch from,  *callback* is a function called with the ajax request after
      // it's succesful completion and *error* is an optional callback to handle errors when fetching the resource
      loadResource: _loadResource,
      //###loader.loadScript
      // Load a script. This method will choose the best method to load the script (using tag or ajax) depending on the
      // origin of the script. Additionally it can extract variables exposed in the window object and register them as
      // instances in arenite.
      //

      // loadScript(script, callback)
      //

      //where *script* is either a url string or a structure defining a url and instances to be extracted from
      // the window object into arenite and *callback* is the callback for when the script is loaded.
      loadScript: _loadScript
    }
  };
};
<commit_msg>improving loader for IE<commit_after>/*global Arenite:true*/
/*jshint evil:true*/
// Collection of utility functions to handle loading resources.
Arenite.Loader = function (arenite) {

  var _createCORSRequest = function (method, url) {
    var xhr = new XMLHttpRequest();
    xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
    xhr.setRequestHeader("Access-Control-Allow-Origin", window.location.origin);
    if (arenite.config.withCredentials){
      if ('withCredentials' in xhr) {
        xhr.open(method, url, true);
        xhr.withCredentials = true;
      } else if (typeof XDomainRequest !== 'undefined') {
        xhr = new XDomainRequest();
        xhr.open(method, url);
      } else {
        xhr=null
      }
    } else {
      xhr.open(method, url, true);
    }
    return xhr;
  };

  var _loadResource = function (url, callback, error) {
    var req = _createCORSRequest('GET', url);
    req.onreadystatechange = function () {
      if (req.readyState === 4) {
        if (req.status % 100 < 4) {
          callback(req);
        } else if (typeof error === 'function') {
          error(req);
        }
      }
    };
    req.send();
  };

  var _loadScriptWithTag = function (url, callback) {
    var head = document.getElementsByTagName('head')[0];
    var script = document.createElement('script');
    script.async = true;
    script.type = 'text/javascript';
    if (navigator.appVersion.indexOf("MSIE 9") === -1) { //IE 9 calls the callback twice
      script.onreadystatechange = function () {
        if (this.readyState === 'complete') {
          callback();
        }
      };
    }
    script.onload = callback;
    script.src = url;
    head.appendChild(script);
  };

  var _loadScriptAsResource = function (url, callback) {
    _loadResource(url, function (req) {
      //analyze the script for "annotation" type configurations
      arenite.annotation.processAnnotations(req.responseText);
      var file_tag = '\n//@ sourceURL=' + window.location.origin + '/' + url + '\n//# sourceURL=' + window.location.origin + '/' + url;
      eval(req.responseText + file_tag);
      callback();
    });
  };

  var _sameOrigin = function (url) {
    var loc = window.location;
    var a = document.createElement('a');
    a.href = url;
    return a.hostname === loc.hostname &&
      a.port === loc.port &&
      a.protocol === loc.protocol;
  };

  var _loadScriptFrom = function (url, callback) {
    if (_sameOrigin(url)) {
      _loadScriptAsResource(url, callback);
    } else {
      _loadScriptWithTag(url, callback);
    }
  };

  var _loadScript = function (script, done) {
    if (typeof script === 'string') {
      _loadScriptFrom(script, done);
    } else {
      _loadScriptFrom(script.url, function () {
        arenite.object.keys(script.instances).forEach(function (instance) {
          arenite.context.add(instance, window[script.instances[instance]]);
          if (typeof script.init === 'function') {
            script.init(arenite);
          }
          delete window[script.instances[instance]];
        });
        if (typeof done === 'function') {
          done();
        }
      });
    }
  };

  return {
    loader: {
      //###loader.loadResource
      // Load a given resource using ajax.
      //

      // loadResource(url, callback, error)
      //

      //where *url* is the url to fetch from,  *callback* is a function called with the ajax request after
      // it's succesful completion and *error* is an optional callback to handle errors when fetching the resource
      loadResource: _loadResource,
      //###loader.loadScript
      // Load a script. This method will choose the best method to load the script (using tag or ajax) depending on the
      // origin of the script. Additionally it can extract variables exposed in the window object and register them as
      // instances in arenite.
      //

      // loadScript(script, callback)
      //

      //where *script* is either a url string or a structure defining a url and instances to be extracted from
      // the window object into arenite and *callback* is the callback for when the script is loaded.
      loadScript: _loadScript
    }
  };
};
<|endoftext|>


<commit_before>#!coffee -pl
# -*- mode: litcoffee -*-

# Phoenix.app config

## Prologue

This is a nice, fairly comprehensive, relatively self-documenting,
configuration for [Phoenix 2](https://github.com/kasper/phoenix/tree/2.0),
a lightweight scriptable OS X window manager.

## [Jump straight to the bindings](#bindings)

## Usage

Build Phoenix.app from [kasper/phoenix 2.0 branch](https://github.com/kasper/phoenix/tree/2.0)

You will need XCode command line tools installed.

```bash
git clone https://github.com/kasper/phoenix
cd phoenix
git checkout 2.0
xcodebuild clean build
```

Now (re)place the Phoenix app into `/Applications/`

```bash
cd build/Release/
rm -rf /Applications/Phoenix.app
mv Phoenix.app /Applications/
```

Install this Phoenix config...

```bash
cd
git clone git@github.com:opsmanager/Phoenix-config
cd Phoenix-config
make

open -a Phoenix
```

You'll need to enable the security / accessibility settings (a pop up will
tell you this on first start.) Phoenix will then need to be run again.

```bash
open -a Phoenix
```

Now is a good time to activate **Open at Login** on the Phoenix OS X
menu item, if you like.

### Install CoffeeScript

If you don't have CoffeeScript installed, you'll need to install
node/npm (use [`brew`](http://brew.sh))

```shell
brew install node
npm install -g coffee-script
```

I assume you know what you're doing, if not, I wish you luck on your
diligent googling. (let's face it you got this far, you can get a
couple of command line tools installed, right?)

... If you need a hint, install `node` and `npm` first.

## The Config proper begins here...

    Phoenix.notify "Phoenix config loading"

## Debugging helpers

    debug = (o, label="obj: ")->
      Phoenix.log label
      Phoenix.log JSON.stringify(o)

## Basic Settings

    MARGIN_X     = 3
    MARGIN_Y     = 3
    GRID_WIDTH   = 20
    GRID_HEIGHT  = 16

## Methods

### Underscore extensions

    _.mixin
      flatmap: (list, iteratee, context) ->
        _.flatten _.map list, iteratee, context

### Helpers

    focused = -> Window.focusedWindow()
    windows = -> Window.visibleWindows()

    Window::screenRect = -> @screen().visibleFrameInRectangle()

    Window::fullGridFrame = -> @calculateGrid y: 0, x: 0, width: 1, height: 1

### Window Grid

Snap all windows to grid layout

    snapAllToGrid = ->
      _.map visible(), (win) -> win.snapToGrid()

Change grid width or height

    changeGridWidth = (n) ->
      GRID_WIDTH = Math.max 1, GRID_WIDTH + n
      Phoenix.notify "grid is #{GRID_WIDTH} tiles wide"
      snapAllToGrid()

    changeGridHeight = (n) ->
      GRID_HEIGHT = Math.max 1, GRID_HEIGHT + n
      Phoenix.notify "grid is #{GRID_HEIGHT} tiles high"
      snapAllToGrid()

Get the current grid as `{x:, y:, width:, height:}`

    Window::getGrid = ->
      frame = @frame()
      gridWidth = @screenRect().width / GRID_WIDTH
      gridHeight = @screenRect().height / GRID_HEIGHT

      y: Math.round (frame.y - @screenRect().y) / gridHeight
      x: Math.round (frame.x - @screenRect().x) / gridWidth
      width: Math.max 1, Math.round frame.width / gridWidth
      height: Math.max 1, Math.round frame.height / gridHeight

Set the current grid from an object `{x:, y:, width:, height:}`

    Window::setGrid = (grid, screen) ->
      gridWidth = @screenRect().width / GRID_WIDTH
      gridHeight = @screenRect().height / GRID_HEIGHT

      @setFrame
        y: ((grid.y * gridHeight) + @screenRect().y) + MARGIN_Y
        x: ((grid.x * gridWidth) + @screenRect().x) + MARGIN_X
        width: (grid.width * gridWidth) - (MARGIN_X * 2.0)
        height: (grid.height * gridHeight) - (MARGIN_Y * 2.0)

Snap the current window to the grid

    Window::snapToGrid = ->
      @setGrid @getGrid(), @screen() if @isNormal()

Calculate the grid based on the parameters, `x`, `y`, `width`, `height`, (returning an object `{x:,y:,width:,height:}`)

    Window::calculateGrid = ({x, y, width, height}) ->
      y:      Math.round(y * @screenRect().height) + MARGIN_Y + @screenRect().y
      x:      Math.round(x * @screenRect().width) + MARGIN_X + @screenRect().x
      width:  Math.round(width * @screenRect().width) - 2.0 * MARGIN_X
      height: Math.round(height * @screenRect().height) - 2.0 * MARGIN_Y

Window to grid

    Window::toGrid = ({x, y, width, height}) ->
      rect = @calculateGrid {x, y, width, height}
      @setFrame rect
      @

Window top right point

    Window::topRight = ->
      x: @frame().x + @frame().width
      y: @frame().y

Windows on the left

    Window::toLeft = ->
      _.filter @windowsToWest(), (win)->
        win.topLeft().x < @topLeft().x - 10

Windows on the right

    Window::toRight = ->
      _.filter @windowsToEast(), (win) ->
        win.topRight().x > @topRight().x + 10

### Window information

    Window::info = ->
      f = @frame()
      "[#{@app().processIdentifier()}] #{@app().name()} : #{@title()}\n{x:#{f.x}, y:#{f.y}, width:#{f.width}, height:#{f.height}}\n"

Sort any window collection by most recently with focus. We use
`info()` as a way of identifying the windows in place. Not too
performant, but with collections of this size, it's not a problem.

    Window.sortByMostRecent = (windows)->
      allVisible = visibleInOrder()
      _.sortBy windows, (win)->
        _.map(allVisible, (w)->
          w.info()).indexOf win.info()

### Window moving and sizing

Temporary storage for frames

    lastFrames = {}

Set a window to full screen

    Window::toFullScreen = ->
      unless _.isEqual @frame(), @fullGridFrame()
        @rememberFrame()
        @toGrid
          y: 0
          x: 0
          width: 1
          height: 1
      else if lastFrames[@uid()]
        @setFrame lastFrames[@uid()]
        @forgetFrame()

Remember and forget frames

    Window::uid           = -> "#{@app().name()}::#{@title()}"
    Window::rememberFrame = -> lastFrames[@uid()] = @frame()
    Window::forgetFrame   = -> delete lastFrames[@uid()]

Set a window to top / bottom / left / right

    Window::toTopHalf     = -> @toGrid x:0,   y:0,   width:1,    height:0.5
    Window::toBottomHalf  = -> @toGrid x:0,   y:0.5, width:1,    height:0.5
    Window::toLeftHalf    = -> @toGrid x:0,   y:0,   width:0.5,  height:1
    Window::toRightHalf   = -> @toGrid x:0.5, y:0,   width:0.5,  height:1

    Window::toTopRight    = -> @toGrid x:0.5, y:0,   width:0.5,  height:0.5
    Window::toBottomRight = -> @toGrid x:0.5, y:0.5, width:0.5,  height:0.5
    Window::toTopLeft     = -> @toGrid x:0,   y:0,   width:0.5,  height:0.5
    Window::toBottomLeft  = -> @toGrid x:0,   y:0.5, width:0.5,  height:0.5
    Window::toCenter      = -> @toGrid x:0.25, y:0.25, width:0.5,  height:0.5

Move the current window to the next / previous screen

    moveWindowToNextScreen = ->
      focused().setGrid focused().getGrid(), focused().screen().nextScreen()

    moveWindowToPreviousScreen = ->
      focused().setGrid focused().getGrid(), focused().screen().previousScreen()

Move the current window around the grid

    windowLeftOneColumn = ->
      frame = focused().getGrid()
      frame.x = Math.max(frame.x - 1, 0)
      focused().setGrid frame, focused().screen()

    windowDownOneRow = ->
      frame = focused().getGrid()
      frame.y = Math.min Math.floor(frame.y + 1), GRID_HEIGHT - 1
      focused().setGrid frame, focused().screen()

    windowUpOneRow = ->
      frame = focused().getGrid()
      frame.y = Math.max Math.floor(frame.y - 1), 0
      focused().setGrid frame, focused().screen()

    windowRightOneColumn = ->
      frame = focused().getGrid()
      frame.x = Math.min(frame.x + 1, GRID_WIDTH - frame.width)
      focused().setGrid frame, focused().screen()

Resize the current window on the grid

    windowGrowOneGridColumn = ->
      frame = focused().getGrid()
      frame.width = Math.min(frame.width + 1, GRID_WIDTH - frame.x)
      focused().setGrid frame, focused().screen()

    windowShrinkOneGridColumn = ->
      frame = focused().getGrid()
      frame.width = Math.max(frame.width - 1, 1)
      focused().setGrid frame, focused().screen()

    windowGrowOneGridRow = ->
      frame = focused().getGrid()
      frame.height = Math.min frame.height + 1, GRID_HEIGHT
      focused().setGrid frame, focused().screen()

    windowShrinkOneGridRow = ->
      frame = focused().getGrid()
      frame.height = Math.max frame.height - 1, 1
      focused().setGrid frame, focused().screen()

Expand the current window's height to vertically fill the screen

    windowToFullHeight = ->
      frame = focused().getGrid()
      frame.y = 0
      frame.height = GRID_HEIGHT
      focused().setGrid frame, focused().screen()

### Applications

Select the first window for an app

    App::firstWindow = -> @visibleWindows()[0]

Find an app by it's `name` - this is problematic when the App window
has no title bar. Fair warning.

Find all apps with `name`

    App.allWithName = (name) ->
      _.filter App.runningApps(), (app) -> app.name() is name

    App.byName = (name) ->
      app = _.first App.allWithName name
      app.show()
      app

Focus or start an app with `name`

    App.focusOrStart = (name) ->
      apps = App.allWithName name
      if _.isEmpty apps
        Phoenix.notify "Starting #{name}"
        App.launch name
      else
        Phoenix.notify "Switching to #{name}"
      windows = _.flatmap apps, (x) -> x.windows()
      activeWindows = _.reject windows, (win) -> win.isMinimized()
      if _.isEmpty(activeWindows)
        App.launch name
      _.each activeWindows, (win) -> win.focus()

### Binding alias

Alias `Phoenix.bind` as `key_binding`, to make the binding table extra
readable.

    keys = []

The `key_binding` method includes the unused `description` parameter,
This is to allow future functionality ie. help mechanisms, describe bindings etc.

    key_binding = (key, description, modifier, fn)-> keys.push Phoenix.bind(key, modifier, fn)

## Bindings

Mash is Cmd + Alt/Opt + Ctrl pressed together.

    mash = 'cmd-alt-ctrl'.split '-'

Move the current window to the top / bottom / left / right half of the screen
and fill it.

    key_binding 'up',    'To Top Half',         mash, -> Window.focusedWindow().toTopHalf()
    key_binding 'down',  'To Bottom Half',      mash, -> Window.focusedWindow().toBottomHalf()
    key_binding 'left',  'To Left Half',        mash, -> Window.focusedWindow().toLeftHalf()
    key_binding 'right', 'To Right Half',       mash, -> Window.focusedWindow().toRightHalf()

Move to the corners of the screen

    key_binding 'Q', 'Top Left',                mash, -> Window.focusedWindow().toTopLeft()
    key_binding 'A', 'Bottom Left',             mash, -> Window.focusedWindow().toBottomLeft()
    key_binding 'W', 'Top Right',               mash, -> Window.focusedWindow().toTopRight()
    key_binding 'S', 'Bottom Right',            mash, -> Window.focusedWindow().toBottomRight()
    key_binding 'D', 'Center',                  mash, -> Window.focusedWindow().toCenter()

Toggle maximize for the current window

    key_binding 'space', 'Maximize Window',     mash, -> Window.focusedWindow().toFullScreen()

## Application config

Replace these with apps that you want...

    ITERM    = "iTerm2"
    VIM      = "MacVim"
    EMACS    = "Emacs"
    TERMINAL = "iTerm2"
    FINDER   = "Finder"

We use an automator app to launch Chrome in remote-debugging-mode (on
port 9222). You may not like or want this

    CHROME   = "ChromeLauncher"

Switch to or lauch apps - fix these up to use whatever Apps you want on speed dial.

    key_binding 'E', 'Launch Emacs',            mash, -> App.focusOrStart EMACS
    key_binding 'V', 'Launch Vim',              mash, -> App.focusOrStart VIM
    key_binding 'T', 'Launch iTerm2',           mash, -> App.focusOrStart ITERM
    key_binding 'C', 'Launch Chrome',           mash, -> App.focusOrStart CHROME
    key_binding 'F', 'Launch Finder',           mash, -> App.focusOrStart FINDER

Move window between screens

    key_binding 'N', 'To Next Screen',          mash, -> moveWindowToNextScreen()
    key_binding 'P', 'To Previous Screen',      mash, -> moveWindowToPreviousScreen()

Setting the grid size

    key_binding '=', 'Increase Grid Columns',   mash, -> changeGridWidth +1
    key_binding '-', 'Reduce Grid Columns',     mash, -> changeGridWidth -1
    key_binding '[', 'Increase Grid Rows',      mash, -> changeGridHeight +1
    key_binding ']', 'Reduce Grid Rows',        mash, -> changeGridHeight -1

Snap current window or all windows to the grid

    key_binding ';', 'Snap focused to grid',    mash, -> Window.focusedWindow().snapToGrid()
    key_binding "'", 'Snap all to grid',        mash, -> visible().map (win)-> win.snapToGrid()

Move the current window around the grid

    key_binding 'H', 'Move Grid Left',          mash, -> windowLeftOneColumn()
    key_binding 'J', 'Move Grid Down',          mash, -> windowDownOneRow()
    key_binding 'K', 'Move Grid Up',            mash, -> windowUpOneRow()
    key_binding 'L', 'Move Grid Right',         mash, -> windowRightOneColumn()

Size the current window on the grid

    key_binding 'U', 'Window Full Height',      mash, -> windowToFullHeight()
    key_binding 'I', 'Shrink by One Column',    mash, -> windowShrinkOneGridColumn()
    key_binding 'O', 'Grow by One Column',      mash, -> windowGrowOneGridColumn()
    key_binding ',', 'Shrink by One Row',       mash, -> windowShrinkOneGridRow()
    key_binding '.', 'Grow by One Row',         mash, -> windowGrowOneGridRow()

All done...

    Phoenix.notify "Loaded"
<commit_msg>Fine tune phoenix<commit_after>#!coffee -pl
# -*- mode: litcoffee -*-

# Phoenix.app config

## Prologue

This is a nice, fairly comprehensive, relatively self-documenting,
configuration for [Phoenix 2](https://github.com/kasper/phoenix/tree/2.0),
a lightweight scriptable OS X window manager.

## [Jump straight to the bindings](#bindings)

## Usage

Build Phoenix.app from [kasper/phoenix 2.0 branch](https://github.com/kasper/phoenix/tree/2.0)

You will need XCode command line tools installed.

```bash
git clone https://github.com/kasper/phoenix
cd phoenix
git checkout 2.0
xcodebuild clean build
```

Now (re)place the Phoenix app into `/Applications/`

```bash
cd build/Release/
rm -rf /Applications/Phoenix.app
mv Phoenix.app /Applications/
```

Install this Phoenix config...

```bash
cd
git clone git@github.com:opsmanager/Phoenix-config
cd Phoenix-config
make

open -a Phoenix
```

You'll need to enable the security / accessibility settings (a pop up will
tell you this on first start.) Phoenix will then need to be run again.

```bash
open -a Phoenix
```

Now is a good time to activate **Open at Login** on the Phoenix OS X
menu item, if you like.

### Install CoffeeScript

If you don't have CoffeeScript installed, you'll need to install
node/npm (use [`brew`](http://brew.sh))

```shell
brew install node
npm install -g coffee-script
```

I assume you know what you're doing, if not, I wish you luck on your
diligent googling. (let's face it you got this far, you can get a
couple of command line tools installed, right?)

... If you need a hint, install `node` and `npm` first.

## The Config proper begins here...

    Phoenix.notify "Phoenix config loading"

## Debugging helpers

    debug = (o, label="obj: ")->
      Phoenix.log label
      Phoenix.log JSON.stringify(o)

## Basic Settings

    MARGIN_X     = 0
    MARGIN_Y     = 0
    GRID_WIDTH   = 16
    GRID_HEIGHT  = 9

## Methods

### Underscore extensions

    _.mixin
      flatmap: (list, iteratee, context) ->
        _.flatten _.map list, iteratee, context

### Helpers

    focused = -> Window.focusedWindow()
    windows = -> Window.visibleWindows()

    Window::screenRect = -> @screen().visibleFrameInRectangle()

    Window::fullGridFrame = -> @calculateGrid y: 0, x: 0, width: 1, height: 1

### Window Grid

Snap all windows to grid layout

    snapAllToGrid = ->
      _.map visible(), (win) -> win.snapToGrid()

Change grid width or height

    changeGridWidth = (n) ->
      GRID_WIDTH = Math.max 1, GRID_WIDTH + n
      Phoenix.notify "grid is #{GRID_WIDTH} tiles wide"
      snapAllToGrid()

    changeGridHeight = (n) ->
      GRID_HEIGHT = Math.max 1, GRID_HEIGHT + n
      Phoenix.notify "grid is #{GRID_HEIGHT} tiles high"
      snapAllToGrid()

Get the current grid as `{x:, y:, width:, height:}`

    Window::getGrid = ->
      frame = @frame()
      gridWidth = @screenRect().width / GRID_WIDTH
      gridHeight = @screenRect().height / GRID_HEIGHT

      y: Math.round (frame.y - @screenRect().y) / gridHeight
      x: Math.round (frame.x - @screenRect().x) / gridWidth
      width: Math.max 1, Math.round frame.width / gridWidth
      height: Math.max 1, Math.round frame.height / gridHeight

Set the current grid from an object `{x:, y:, width:, height:}`

    Window::setGrid = (grid, screen) ->
      gridWidth = @screenRect().width / GRID_WIDTH
      gridHeight = @screenRect().height / GRID_HEIGHT

      @setFrame
        y: ((grid.y * gridHeight) + @screenRect().y) + MARGIN_Y
        x: ((grid.x * gridWidth) + @screenRect().x) + MARGIN_X
        width: (grid.width * gridWidth) - (MARGIN_X * 2.0)
        height: (grid.height * gridHeight) - (MARGIN_Y * 2.0)

Snap the current window to the grid

    Window::snapToGrid = ->
      @setGrid @getGrid(), @screen() if @isNormal()

Calculate the grid based on the parameters, `x`, `y`, `width`, `height`, (returning an object `{x:,y:,width:,height:}`)

    Window::calculateGrid = ({x, y, width, height}) ->
      y:      Math.round(y * @screenRect().height) + MARGIN_Y + @screenRect().y
      x:      Math.round(x * @screenRect().width) + MARGIN_X + @screenRect().x
      width:  Math.round(width * @screenRect().width) - 2.0 * MARGIN_X
      height: Math.round(height * @screenRect().height) - 2.0 * MARGIN_Y

Window to grid

    Window::toGrid = ({x, y, width, height}) ->
      rect = @calculateGrid {x, y, width, height}
      @setFrame rect
      @

Window top right point

    Window::topRight = ->
      x: @frame().x + @frame().width
      y: @frame().y

Windows on the left

    Window::toLeft = ->
      _.filter @windowsToWest(), (win)->
        win.topLeft().x < @topLeft().x - 10

Windows on the right

    Window::toRight = ->
      _.filter @windowsToEast(), (win) ->
        win.topRight().x > @topRight().x + 10

### Window information

    Window::info = ->
      f = @frame()
      "[#{@app().processIdentifier()}] #{@app().name()} : #{@title()}\n{x:#{f.x}, y:#{f.y}, width:#{f.width}, height:#{f.height}}\n"

Sort any window collection by most recently with focus. We use
`info()` as a way of identifying the windows in place. Not too
performant, but with collections of this size, it's not a problem.

    Window.sortByMostRecent = (windows)->
      allVisible = visibleInOrder()
      _.sortBy windows, (win)->
        _.map(allVisible, (w)->
          w.info()).indexOf win.info()

### Window moving and sizing

Temporary storage for frames

    lastFrames = {}

Set a window to full screen

    Window::toFullScreen = ->
      unless _.isEqual @frame(), @fullGridFrame()
        @rememberFrame()
        @toGrid
          y: 0
          x: 0
          width: 1
          height: 1
      else if lastFrames[@uid()]
        @setFrame lastFrames[@uid()]
        @forgetFrame()

Remember and forget frames

    Window::uid           = -> "#{@app().name()}::#{@title()}"
    Window::rememberFrame = -> lastFrames[@uid()] = @frame()
    Window::forgetFrame   = -> delete lastFrames[@uid()]

Set a window to top / bottom / left / right

    Window::toTopHalf     = -> @toGrid x:0,    y:0,    width:1,   height:0.5
    Window::toBottomHalf  = -> @toGrid x:0,    y:0.5,  width:1,   height:0.5
    Window::toLeftHalf    = -> @toGrid x:0,    y:0,    width:0.5, height:1
    Window::toRightHalf   = -> @toGrid x:0.5,  y:0,    width:0.5, height:1

    Window::toTopRight    = -> @toGrid x:0.5,  y:0,    width:0.5, height:0.5
    Window::toBottomRight = -> @toGrid x:0.5,  y:0.5,  width:0.5, height:0.5
    Window::toTopLeft     = -> @toGrid x:0,    y:0,    width:0.5, height:0.5
    Window::toBottomLeft  = -> @toGrid x:0,    y:0.5,  width:0.5, height:0.5
    Window::toCenter      = -> @toGrid x:0.25, y:0.25, width:0.5, height:0.5

Move the current window to the next / previous screen

    moveWindowToNextScreen = ->
      focused().setGrid focused().getGrid(), focused().screen().nextScreen()

    moveWindowToPreviousScreen = ->
      focused().setGrid focused().getGrid(), focused().screen().previousScreen()

Move the current window around the grid

    windowLeftOneColumn = ->
      frame = focused().getGrid()
      frame.x = Math.max(frame.x - 1, 0)
      focused().setGrid frame, focused().screen()

    windowDownOneRow = ->
      frame = focused().getGrid()
      frame.y = Math.min Math.floor(frame.y + 1), GRID_HEIGHT - 1
      focused().setGrid frame, focused().screen()

    windowUpOneRow = ->
      frame = focused().getGrid()
      frame.y = Math.max Math.floor(frame.y - 1), 0
      focused().setGrid frame, focused().screen()

    windowRightOneColumn = ->
      frame = focused().getGrid()
      frame.x = Math.min(frame.x + 1, GRID_WIDTH - frame.width)
      focused().setGrid frame, focused().screen()

Resize the current window on the grid

    windowGrowOneGridColumn = ->
      frame = focused().getGrid()
      frame.width = Math.min(frame.width + 1, GRID_WIDTH - frame.x)
      focused().setGrid frame, focused().screen()

    windowShrinkOneGridColumn = ->
      frame = focused().getGrid()
      frame.width = Math.max(frame.width - 1, 1)
      focused().setGrid frame, focused().screen()

    windowGrowOneGridRow = ->
      frame = focused().getGrid()
      frame.height = Math.min frame.height + 1, GRID_HEIGHT
      focused().setGrid frame, focused().screen()

    windowShrinkOneGridRow = ->
      frame = focused().getGrid()
      frame.height = Math.max frame.height - 1, 1
      focused().setGrid frame, focused().screen()

Expand the current window's height to vertically fill the screen

    windowToFullHeight = ->
      frame = focused().getGrid()
      frame.y = 0
      frame.height = GRID_HEIGHT
      focused().setGrid frame, focused().screen()

### Applications

Select the first window for an app

    App::firstWindow = -> @visibleWindows()[0]

Find an app by it's `name` - this is problematic when the App window
has no title bar. Fair warning.

Find all apps with `name`

    App.allWithName = (name) ->
      _.filter App.runningApps(), (app) -> app.name() is name

    App.byName = (name) ->
      app = _.first App.allWithName name
      app.show()
      app

Focus or start an app with `name`

    App.focusOrStart = (name) ->
      apps = App.allWithName name
      if _.isEmpty apps
        Phoenix.notify "Starting #{name}"
        App.launch name
      else
        Phoenix.notify "Switching to #{name}"
      windows = _.flatmap apps, (x) -> x.windows()
      activeWindows = _.reject windows, (win) -> win.isMinimized()
      if _.isEmpty(activeWindows)
        App.launch name
      _.each activeWindows, (win) -> win.focus()

### Binding alias

Alias `Phoenix.bind` as `key_binding`, to make the binding table extra
readable.

    keys = []

The `key_binding` method includes the unused `description` parameter,
This is to allow future functionality ie. help mechanisms, describe bindings etc.

    key_binding = (key, description, modifier, fn)-> keys.push Phoenix.bind(key, modifier, fn)

## Bindings

Mash is Cmd + Alt/Opt + Ctrl pressed together.

    mash = 'cmd-alt-ctrl'.split '-'

Move the current window to the top / bottom / left / right half of the screen
and fill it.

    key_binding 'up',    'To Top Half',         mash, -> Window.focusedWindow().toTopHalf()
    key_binding 'down',  'To Bottom Half',      mash, -> Window.focusedWindow().toBottomHalf()
    key_binding 'left',  'To Left Half',        mash, -> Window.focusedWindow().toLeftHalf()
    key_binding 'right', 'To Right Half',       mash, -> Window.focusedWindow().toRightHalf()

Move to the corners of the screen

    key_binding 'Q', 'Top Left',                mash, -> Window.focusedWindow().toTopLeft()
    key_binding 'A', 'Bottom Left',             mash, -> Window.focusedWindow().toBottomLeft()
    key_binding 'W', 'Top Right',               mash, -> Window.focusedWindow().toTopRight()
    key_binding 'S', 'Bottom Right',            mash, -> Window.focusedWindow().toBottomRight()
    key_binding 'D', 'Center',                  mash, -> Window.focusedWindow().toCenter()

Toggle maximize for the current window

    key_binding 'space', 'Maximize Window',     mash, -> Window.focusedWindow().toFullScreen()

## Application config

Replace these with apps that you want...

    ITERM    = "iTerm2"
    VIM      = "MacVim"
    EMACS    = "Emacs"
    TERMINAL = "iTerm2"
    FINDER   = "Finder"

We use an automator app to launch Chrome in remote-debugging-mode (on
port 9222). You may not like or want this

    CHROME   = "ChromeLauncher"

Switch to or lauch apps - fix these up to use whatever Apps you want on speed dial.

    key_binding 'E', 'Launch Emacs',            mash, -> App.focusOrStart EMACS
    key_binding 'V', 'Launch Vim',              mash, -> App.focusOrStart VIM
    key_binding 'T', 'Launch iTerm2',           mash, -> App.focusOrStart ITERM
    key_binding 'C', 'Launch Chrome',           mash, -> App.focusOrStart CHROME
    key_binding 'F', 'Launch Finder',           mash, -> App.focusOrStart FINDER

Move window between screens

    key_binding 'N', 'To Next Screen',          mash, -> moveWindowToNextScreen()
    key_binding 'P', 'To Previous Screen',      mash, -> moveWindowToPreviousScreen()

Setting the grid size

    key_binding '=', 'Increase Grid Columns',   mash, -> changeGridWidth +1
    key_binding '-', 'Reduce Grid Columns',     mash, -> changeGridWidth -1
    key_binding '[', 'Increase Grid Rows',      mash, -> changeGridHeight +1
    key_binding ']', 'Reduce Grid Rows',        mash, -> changeGridHeight -1

Snap current window or all windows to the grid

    key_binding ';', 'Snap focused to grid',    mash, -> Window.focusedWindow().snapToGrid()
    key_binding "'", 'Snap all to grid',        mash, -> visible().map (win)-> win.snapToGrid()

Move the current window around the grid

    key_binding 'H', 'Move Grid Left',          mash, -> windowLeftOneColumn()
    key_binding 'J', 'Move Grid Down',          mash, -> windowDownOneRow()
    key_binding 'K', 'Move Grid Up',            mash, -> windowUpOneRow()
    key_binding 'L', 'Move Grid Right',         mash, -> windowRightOneColumn()

Size the current window on the grid

    key_binding 'U', 'Window Full Height',      mash, -> windowToFullHeight()
    key_binding 'I', 'Shrink by One Column',    mash, -> windowShrinkOneGridColumn()
    key_binding 'O', 'Grow by One Column',      mash, -> windowGrowOneGridColumn()
    key_binding ',', 'Shrink by One Row',       mash, -> windowShrinkOneGridRow()
    key_binding '.', 'Grow by One Row',         mash, -> windowGrowOneGridRow()

All done...

    Phoenix.notify "Loaded"
<|endoftext|>


<commit_before>var Master = function(){
    // private
    var headContent = "",
        mainContent = "";

    /*headContent += "<meta charset='utf-8' />";
    headContent += "<meta http-equiv='X-UA-Compatible' content='chrome=1' />";
    headContent += "<meta name='description' content='Jonmann20.github.com : ' />";

    headContent += "<link rel='stylesheet' type='text/css' media='screen' href='css/style.css'>";
    headContent += "<title>Jonmann20.github.com</title>";*/

    // public
    return {
        head: function(str){
            headContent += str;
        }
        main: function(str){
            mainContent = str;
        },
        init: function(){
            $('head').html(headContent);
            $('main').html(mainContent);
        }
    };
}();
<commit_msg>Update master.js<commit_after>var Master = function(){
    // private
    var headContent = "",
        mainContent = "";

    headContent += "<meta charset='utf-8' />";
    headContent += "<meta http-equiv='X-UA-Compatible' content='chrome=1' />";
    headContent += "<meta name='description' content='Jonmann20.github.com : ' />";

    headContent += "<link rel='stylesheet' type='text/css' media='screen' href='css/style.css'>";
    headContent += "<title>Jonmann20.github.com</title>";

    // public
    return {
        head: function(str){
            headContent += str;
        },
        main: function(str){
            mainContent = str;
        },
        init: function(){
            $('head').html(headContent);
            $('main').html(mainContent);
        }
    };
}();
<|endoftext|>


<commit_before>/**
 * Module dependencies.
 */

var fs = require('fs');

/**
 * Expose `HTMLCov`.
 */

exports = module.exports = HTMLCov;

function HTMLCov (coverageData, filename) {
	var jade = require('jade'),
		file = __dirname + '/templates/coverage.jade',
		str = fs.readFileSync(file, 'utf8'),
		fn = jade.compile(str, { filename: file }),
		output = fn({
			cov: coverageData,
			coverageClass: coverageClass,
			coverageCategory: coverageCategory
		});
	if (!filename) {
		process.stdout.write();
	} else {
		fs.writeFileSync(filename, output);
	}
}

function coverageCategory(line) {
	return line.coverage === 0 ?
				'miss' :
				(line.statements ?
					'hit ' + (line.statements.toFixed(0) != 100 ? 'partial' : '')
					: '');
}

function coverageClass (n) {
	if (n >= 75) {
		return 'high';
	}
	if (n >= 50) {
		return 'medium';
	}
	if (n >= 25) {
		return 'low';
	}
	return 'terrible';
}
<commit_msg>update the html reporter to be able to pass back the html output in addition to writing it to disk<commit_after>/**
 * Module dependencies.
 */

var fs = require('fs');

/**
 * Expose `HTMLCov`.
 */

exports = module.exports = HTMLCov;

function HTMLCov (coverageData, filename) {
	var jade = require('jade'),
		file = __dirname + '/templates/coverage.jade',
		str = fs.readFileSync(file, 'utf8'),
		fn = jade.compile(str, { filename: file }),
		output = fn({
			cov: coverageData,
			coverageClass: coverageClass,
			coverageCategory: coverageCategory
		});
	if (!filename) {
		return output;
	} else {
		fs.writeFileSync(filename, output);
	}
}

function coverageCategory(line) {
	return line.coverage === 0 ?
				'miss' :
				(line.statements ?
					'hit ' + (line.statements.toFixed(0) != 100 ? 'partial' : '')
					: '');
}

function coverageClass (n) {
	if (n >= 75) {
		return 'high';
	}
	if (n >= 50) {
		return 'medium';
	}
	if (n >= 25) {
		return 'low';
	}
	return 'terrible';
}
<|endoftext|>


<commit_before>var Nearby = function() {
  this.meanRadius = 6371008.8; // The earth's mean radius.
  this.cf = this.meanRadius * Math.PI * 2; // The earth's circumference.
  this.expandings = [1000, 2000, 5000, 10000, 25000, 50000]; // distances to expand the searchbox in meters
  this.expandingsQueue = this.expandings.slice(); // makes a copy of the expandings array
  // the function is locally named to be able to be called recursevly
  this.getList = function getList(nearby, distFromPMerid, distFromEquator, lat, callback) {
    if (nearby.expandingsQueue.length == 0) {
      console.warn("Error: No artwors found within 50km");
      var confirm = $('<button/>', {
        id: 'buttonConfirm',
        class: 'base-full-button base-button base-button-highlight',
        text: i18next.t('base.confirm')
      });
      gui.showPopup(i18next.t('base.nothingNearbyTitle'),i18next.t('base.nothingNearby'), confirm);
      confirm.click(function() {
        gui.closePopup(null,null);
        history.go(-1);
      });
    } else {
      var expand = nearby.expandingsQueue.shift();
      // Added b to all variables to not confuse with a global top variable that is NaN
      btop     = distFromEquator + expand / 2;
      bbottom  = distFromEquator - expand / 2;
      // Calculate distances from prime meridian to left and right edges of bounding box.
      bright = distFromPMerid + expand / 2;
      bleft  = distFromPMerid - expand / 2;
      $.ajax({
        type: 'GET',
        async: false,
        url: 'http://offentligkonst.se/api/api.php?',
        data: {
          action: 'get',
          format: 'jsonp',
          json: 'compact',
          bbox: nearby.calculateBBox(btop, bbottom, bleft, bright, lat)
        },
        dataType: 'jsonp',
        success: function(success) {
          if(!$.isEmptyObject(success.body)) {
            var lists = success.body[0]["hit"]["list"]; // +";Q19748541"
            if(success.body.length > 1){
                for(var i = 1; i < success.body.length; i++){
                    var lis = success.body[i]["hit"]["list"];
                    if(!lists.match(lis)){
                        lists += ";"+lis;
                    }
                }
            }
            callback(lists);
          } else {
            getList(nearby, distFromPMerid, distFromEquator, lat, callback);
          };
        },
        fail: function(xhr, status, error){
          showErrorMessage('An error('+status+'): '+message.error);
        }
      });
    }
  };
  this.getUnlocated = function(position, successCallback, failCallback) {
    var lng       = position.longitude;
    var lat       = position.latitude;
    var accuracy  = position.accuracy;
    // Calculate users distance to prime meridian and equator.
    var distFromPMerid  = this.distanceFromPrimeMeridian(lng, lat);
    var distFromEquator = this.distanceFromEquator(lat);

    this.expandingsQueue = this.expandings.slice(); // makes a copy of the expandings array

    this.getList(this, distFromPMerid, distFromEquator, lat, successCallback, failCallback);
  };
  this.distanceFromPrimeMeridian = function(longitude, latitude) {
    return (longitude / 360) * Math.cos(this.toRadians(latitude)) * this.cf;
  };
  this.distanceFromEquator = function(latitude) {
    return (latitude / 180) * this.cf;
  };
  this.distFromPMeridToLong = function(distance, latitude) {
    return (distance * 360) / (Math.cos(this.toRadians(latitude)) * this.cf);
  };
  this.distFromEquatorToLat = function(distance) {
    return distance * 180 / this.cf;
  };
  this.toRadians = function(degrees) {
    return degrees * (Math.PI / 180);
  };
  this.calculateBBox = function(topDist, botDist, leftDist, rightDist, latitude) {
    // Convert these distances to longitudes and latitudes.
    // t = top, b = bottom, r = right and l = left
    var tLat = this.distFromEquatorToLat(topDist);
    var bLat = this.distFromEquatorToLat(botDist);
    var rLong = this.distFromPMeridToLong(rightDist, latitude);
    var lLong = this.distFromPMeridToLong(leftDist, latitude);
    // Calculate box edges.
    // Bottom left coord.
    var bl = lLong + "|" + bLat;
    // Top right coord.
    var tr = rLong + "|" + tLat;
    return bl + "|" + tr;
  };
};
<commit_msg>Changed distance calculation<commit_after>var Nearby = function() {
  this.meanRadius = 6371008.8; // The earth's mean radius.
  this.cf = this.meanRadius * Math.PI * 2; // The earth's circumference.
  this.expandings = [1000, 2000, 5000, 10000, 25000, 50000]; // distances to expand the searchbox in meters
  this.expandingsQueue = this.expandings.slice(); // makes a copy of the expandings array
  // the function is locally named to be able to be called recursevly
  this.getList = function getList(nearby, distFromPMerid, distFromEquator, lat, callback) {
    if (nearby.expandingsQueue.length == 0) {
      console.warn("Error: No artwors found within 50km");
      var confirm = $('<button/>', {
        id: 'buttonConfirm',
        class: 'base-full-button base-button base-button-highlight',
        text: i18next.t('base.confirm')
      });
      gui.showPopup(i18next.t('base.nothingNearbyTitle'),i18next.t('base.nothingNearby'), confirm);
      confirm.click(function() {
        gui.closePopup(null,null);
        history.go(-1);
      });
    } else {
      var expand = nearby.expandingsQueue.shift();
      // Added b to all variables to not confuse with a global top variable that is NaN
      btop     = distFromEquator + expand / 2;
      bbottom  = distFromEquator - expand / 2;
      // Calculate distances from prime meridian to left and right edges of bounding box.
      bright = distFromPMerid + expand / 2;
      bleft  = distFromPMerid - expand / 2;
      $.ajax({
        type: 'GET',
        async: false,
        url: 'http://offentligkonst.se/api/api.php?',
        data: {
          action: 'get',
          format: 'jsonp',
          json: 'compact',
          bbox: nearby.calculateBBox(btop, bbottom, bleft, bright, lat)
        },
        dataType: 'jsonp',
        success: function(success) {
          if(!$.isEmptyObject(success.body)) {
            var lists = success.body[0]["hit"]["list"]; // +";Q19748541"
            if(success.body.length > 1){
                for(var i = 1; i < success.body.length; i++){
                    var lis = success.body[i]["hit"]["list"];
                    if(!lists.match(lis)){
                        lists += ";"+lis;
                    }
                }
            }
            callback(lists);
          } else {
            getList(nearby, distFromPMerid, distFromEquator, lat, callback);
          };
        },
        fail: function(xhr, status, error){
          showErrorMessage('An error('+status+'): '+message.error);
        }
      });
    }
  };
  this.getUnlocated = function(position, successCallback, failCallback) {
    var lng       = position.longitude;
    var lat       = position.latitude;
    var accuracy  = position.accuracy;
    // Calculate users distance to prime meridian and equator.
    var distFromPMerid  = this.distanceFromPrimeMeridian(lng, lat);
    var distFromEquator = this.distanceFromEquator(lat);

    this.expandingsQueue = this.expandings.slice(); // makes a copy of the expandings array

    this.getList(this, distFromPMerid, distFromEquator, lat, successCallback, failCallback);
  };
  this.distanceFromPrimeMeridian = function(longitude, latitude) {
    return (longitude / 360) * Math.cos(this.toRadians(latitude)) * this.cf;
  };
  this.distanceFromEquator = function(latitude) {
    return (latitude / 180) * (this.cf/2);
  };
  this.distFromPMeridToLong = function(distance, latitude) {
    return (distance * 360) / (Math.cos(this.toRadians(latitude)) * this.cf);
  };
  this.distFromEquatorToLat = function(distance) {
    return distance * 180 / (this.cf/2);
  };
  this.toRadians = function(degrees) {
    return degrees * (Math.PI / 180);
  };
  this.calculateBBox = function(topDist, botDist, leftDist, rightDist, latitude) {
    // Convert these distances to longitudes and latitudes.
    // t = top, b = bottom, r = right and l = left
    var tLat = this.distFromEquatorToLat(topDist);
    var bLat = this.distFromEquatorToLat(botDist);
    var rLong = this.distFromPMeridToLong(rightDist, latitude);
    var lLong = this.distFromPMeridToLong(leftDist, latitude);
    // Calculate box edges.
    // Bottom left coord.
    var bl = lLong + "|" + bLat;
    // Top right coord.
    var tr = rLong + "|" + tLat;
    return bl + "|" + tr;
  };
};
<|endoftext|>


<commit_before>var Office = {
  debug: 1,
  debugStatus: {enabled: 0, string: 'coffee\nDebugging office status'},

  // Light limit, 0-860 is ON, 860-1023 is OFF
  lightLimit: 860,
  // Basic statuses have titles and messages (icons are fetched from affiliation)
  statuses: {
    'error': {title: 'Oops', color: 'LightGray', message: 'Klarte ikke hente kontorstatus'},
    'open': {title: 'Åpent', color: 'LimeGreen', message: 'Gratis kaffe og te til alle!'},
    'closed': {title: 'Lukket', color: 'yellow', message: 'Finn et komitemedlem for å åpne opp.'},
    'meeting': {title: 'Møte', color: 'red', message: 'Kontoret er opptatt'}, // titled meetings get names from calendar entries
  },
  // Food statuses have titles and icons (messages exist as calendar titles)
  foods: {
    'bun': {title: 'Boller', color: 'NavajoWhite', icon: './img/icon-bun.png', image: './img/image-bun.png'},
    'cake': {title: 'Kake', color: 'NavajoWhite', icon: './img/icon-cake.png', image: './img/image-cake.png'},
    'coffee': {title: 'Kaffekos', color: 'NavajoWhite', icon: './img/icon-coffee.png'},
    'pizza': {title: 'Pizza', color: 'NavajoWhite', icon: './img/icon-pizza.png', image: './img/image-pizza.png'},
    'taco': {title: 'Taco', color: 'NavajoWhite', icon: './img/icon-taco.png', image: './img/image-taco.png'},
    'waffle': {title: 'Vafler', color: 'NavajoWhite', icon: './img/icon-waffle.png', image: './img/image-waffle.png'},
  },

  get: function(callback) {
    if (callback == undefined) {
      console.log('ERROR: Callback is required. In the callback you should insert the results into the DOM.');
      return;
    }

    var self = this;
    this.getEventData( function(status, title, message) {

      if (status == 'free' || self.debugOpenOrClosed()) {
        // No events are active, check lights to find open or closed
        self.getLightData(callback);
      }
      else {
        // An event is active, either meeting or a food status
        if (self.debug) console.log('Office:\n- status is', status, '\n- title is', title, '\n- message is', message);
        callback(status, title, message);
      }
    });
  },

  getEventData: function(callback) {
    if (callback == undefined) {
      console.log('ERROR: Callback is required. In the callback you should insert the results into the DOM.');
      return;
    }

    var eventApi = Affiliation.org[localStorage.affiliationKey1].eventApi;

    // Receives info on current event from Onlines servers (without comments)
    // meeting        // current status
    // Møte: dotKom   // event title or 'No title'-meeting or nothing
    var self = this;
    Ajaxer.getPlainText({
      url: eventApi,
      success: function(data) {

        // Debug particular status?
        if (self.debug && self.debugStatus.enabled) {
          data = self.debugStatus.string;
        }

        var status = data.split('\n',2)[0]; // 'meeting'
        var title = data.split('\n',2)[1]; // 'Arbeidskveld med arrKom'

        if (self.debug) console.log('status is "'+status+'" and title is "'+title+'"');

        // empty meeting title?
        if (status == 'meeting' && title == '')
          title = self.statuses['meeting'].message;
        title = title.trim();

        // Temporary support for the old system, backwards compatibility
        if (isNumber(status)) {
          switch(Number(status)) {
            case 0: callback('free'); break;
            case 1: callback('meeting', self.statuses['meeting'].title, title); break;
            case 2: callback('waffle', self.foods.waffle.title, title); break;
            case 3:
            default: callback('error', self.statuses['error'].title, self.statuses['error'].message);
          }
        }
        else {
          status = status.trim();
          // Set the status from fetched data
          switch(status) {

            case 'free': callback('free'); break;

            case 'meeting': callback('meeting', self.statuses['meeting'].title, title); break;

            case 'bun': callback('bun', self.foods.bun.title, title); break;
            case 'cake': callback('cake', self.foods.cake.title, title); break;
            case 'coffee': callback('coffee', self.foods.coffee.title, title); break;
            case 'waffle': callback('waffle', self.foods.waffle.title, title); break;
            case 'pizza': callback('pizza', self.foods.pizza.title, title); break;
            case 'taco': callback('taco', self.foods.taco.title, title); break;

            case 'error':
            default: callback('error', self.statuses['error'].title, self.statuses['error'].message);
          }
        }

      },
      error: function(jqXHR, text, err) {
        if (self.debug) console.log('ERROR: Failed to get event data.');
        callback('error', self.statuses['error'].title, self.statuses['error'].message);
      },
    });
  },

  getLightData: function(callback) {
    if (callback == undefined) {
      console.log('ERROR: Callback is required. In the callback you should insert the results into the DOM.');
      return;
    }

    var lightApi = Affiliation.org[localStorage.affiliationKey1].lightApi;

    var debugStatus = null;
    if (this.debugOpenOrClosed())
      if (this.debugStatus.string.startsWith('closed'))
        debugStatus = 'closed';

    // Receives current light intensity from the office: OFF 0-lightLimit-1023 ON
    var self = this;
    Ajaxer.getPlainText({
      url: lightApi,
      success: function(data) {
        if (data > self.lightLimit || debugStatus == 'closed') {
          if (self.debug) console.log('Office:\n- status is closed\n- title is', self.statuses['closed'].title, '\n- message is', self.statuses['closed'].message);
          callback('closed', self.statuses['closed'].title, self.statuses['closed'].message);
        }
        else {
          if (self.debug) console.log('Office:\n- status is open\n- title is', self.statuses['open'].title, '\n- message is', self.statuses['open'].message);
          callback('open', self.statuses['open'].title, self.statuses['open'].message);
        }
      },
      error: function(jqXHR, err) {
        if (self.debug) console.log('ERROR: Failed to get light data.');
        callback('error', self.statuses['error'].title, self.statuses['error'].message);
      },
    });
  },

  debugOpenOrClosed: function() {
    if (Office.debug && Office.debugStatus.enabled) {
      if (Office.debugStatus.string.startsWith('open') || Office.debugStatus.string.startsWith('closed')) {
        return true;
      }
    }
    return false;
  },

}
<commit_msg>office: turned off office status debugging<commit_after>var Office = {
  debug: 0,
  debugStatus: {enabled: 0, string: 'coffee\nDebugging office status'},

  // Light limit, 0-860 is ON, 860-1023 is OFF
  lightLimit: 860,
  // Basic statuses have titles and messages (icons are fetched from affiliation)
  statuses: {
    'error': {title: 'Oops', color: 'LightGray', message: 'Klarte ikke hente kontorstatus'},
    'open': {title: 'Åpent', color: 'LimeGreen', message: 'Gratis kaffe og te til alle!'},
    'closed': {title: 'Lukket', color: 'yellow', message: 'Finn et komitemedlem for å åpne opp.'},
    'meeting': {title: 'Møte', color: 'red', message: 'Kontoret er opptatt'}, // titled meetings get names from calendar entries
  },
  // Food statuses have titles and icons (messages exist as calendar titles)
  foods: {
    'bun': {title: 'Boller', color: 'NavajoWhite', icon: './img/icon-bun.png', image: './img/image-bun.png'},
    'cake': {title: 'Kake', color: 'NavajoWhite', icon: './img/icon-cake.png', image: './img/image-cake.png'},
    'coffee': {title: 'Kaffekos', color: 'NavajoWhite', icon: './img/icon-coffee.png'},
    'pizza': {title: 'Pizza', color: 'NavajoWhite', icon: './img/icon-pizza.png', image: './img/image-pizza.png'},
    'taco': {title: 'Taco', color: 'NavajoWhite', icon: './img/icon-taco.png', image: './img/image-taco.png'},
    'waffle': {title: 'Vafler', color: 'NavajoWhite', icon: './img/icon-waffle.png', image: './img/image-waffle.png'},
  },

  get: function(callback) {
    if (callback == undefined) {
      console.log('ERROR: Callback is required. In the callback you should insert the results into the DOM.');
      return;
    }

    var self = this;
    this.getEventData( function(status, title, message) {

      if (status == 'free' || self.debugOpenOrClosed()) {
        // No events are active, check lights to find open or closed
        self.getLightData(callback);
      }
      else {
        // An event is active, either meeting or a food status
        if (self.debug) console.log('Office:\n- status is', status, '\n- title is', title, '\n- message is', message);
        callback(status, title, message);
      }
    });
  },

  getEventData: function(callback) {
    if (callback == undefined) {
      console.log('ERROR: Callback is required. In the callback you should insert the results into the DOM.');
      return;
    }

    var eventApi = Affiliation.org[localStorage.affiliationKey1].eventApi;

    // Receives info on current event from Onlines servers (without comments)
    // meeting        // current status
    // Møte: dotKom   // event title or 'No title'-meeting or nothing
    var self = this;
    Ajaxer.getPlainText({
      url: eventApi,
      success: function(data) {

        // Debug particular status?
        if (self.debug && self.debugStatus.enabled) {
          data = self.debugStatus.string;
        }

        var status = data.split('\n',2)[0]; // 'meeting'
        var title = data.split('\n',2)[1]; // 'Arbeidskveld med arrKom'

        if (self.debug) console.log('status is "'+status+'" and title is "'+title+'"');

        // empty meeting title?
        if (status == 'meeting' && title == '')
          title = self.statuses['meeting'].message;
        title = title.trim();

        // Temporary support for the old system, backwards compatibility
        if (isNumber(status)) {
          switch(Number(status)) {
            case 0: callback('free'); break;
            case 1: callback('meeting', self.statuses['meeting'].title, title); break;
            case 2: callback('waffle', self.foods.waffle.title, title); break;
            case 3:
            default: callback('error', self.statuses['error'].title, self.statuses['error'].message);
          }
        }
        else {
          status = status.trim();
          // Set the status from fetched data
          switch(status) {

            case 'free': callback('free'); break;

            case 'meeting': callback('meeting', self.statuses['meeting'].title, title); break;

            case 'bun': callback('bun', self.foods.bun.title, title); break;
            case 'cake': callback('cake', self.foods.cake.title, title); break;
            case 'coffee': callback('coffee', self.foods.coffee.title, title); break;
            case 'waffle': callback('waffle', self.foods.waffle.title, title); break;
            case 'pizza': callback('pizza', self.foods.pizza.title, title); break;
            case 'taco': callback('taco', self.foods.taco.title, title); break;

            case 'error':
            default: callback('error', self.statuses['error'].title, self.statuses['error'].message);
          }
        }

      },
      error: function(jqXHR, text, err) {
        if (self.debug) console.log('ERROR: Failed to get event data.');
        callback('error', self.statuses['error'].title, self.statuses['error'].message);
      },
    });
  },

  getLightData: function(callback) {
    if (callback == undefined) {
      console.log('ERROR: Callback is required. In the callback you should insert the results into the DOM.');
      return;
    }

    var lightApi = Affiliation.org[localStorage.affiliationKey1].lightApi;

    var debugStatus = null;
    if (this.debugOpenOrClosed())
      if (this.debugStatus.string.startsWith('closed'))
        debugStatus = 'closed';

    // Receives current light intensity from the office: OFF 0-lightLimit-1023 ON
    var self = this;
    Ajaxer.getPlainText({
      url: lightApi,
      success: function(data) {
        if (data > self.lightLimit || debugStatus == 'closed') {
          if (self.debug) console.log('Office:\n- status is closed\n- title is', self.statuses['closed'].title, '\n- message is', self.statuses['closed'].message);
          callback('closed', self.statuses['closed'].title, self.statuses['closed'].message);
        }
        else {
          if (self.debug) console.log('Office:\n- status is open\n- title is', self.statuses['open'].title, '\n- message is', self.statuses['open'].message);
          callback('open', self.statuses['open'].title, self.statuses['open'].message);
        }
      },
      error: function(jqXHR, err) {
        if (self.debug) console.log('ERROR: Failed to get light data.');
        callback('error', self.statuses['error'].title, self.statuses['error'].message);
      },
    });
  },

  debugOpenOrClosed: function() {
    if (Office.debug && Office.debugStatus.enabled) {
      if (Office.debugStatus.string.startsWith('open') || Office.debugStatus.string.startsWith('closed')) {
        return true;
      }
    }
    return false;
  },

}
<|endoftext|>


<commit_before>const pi = 3.14159265359;
const bohr2ang = 0.529177249;

class PhononWebpage {

    constructor(visualizer, dispersion) {
        this.k = 0;
        this.n = 0;
        this.nx = 1;
        this.ny = 1;
        this.nz = 1;
        this.amplitude = 0.5;

        //select visualization
        this.visualizer = visualizer;

        //select dispersion
        this.dispersion = dispersion;

        //bind some functions (TODO: improve this)
        this.exportXSF    = exportXSF.bind(this);
        this.exportPOSCAR = exportPOSCAR.bind(this);
    }

    getRepetitions() {
        /*
        read the number of repetitions in each direction and update it
        */
        this.nx = $('#nx').val();
        this.ny = $('#ny').val();
        this.nz = $('#nz').val();
    }

    setRepetitions(repetitions) {
        /*
        set the number of repetitions on the interface
        */

        if (repetitions) {
            this.nx = repetitions[0];
            this.ny = repetitions[1];
            this.nz = repetitions[2];
        }

        $('#nx').val(this.nx);
        $('#ny').val(this.ny);
        $('#nz').val(this.nz);
    }

    loadCustomFile(event) {
        /*
        find the type of file and call the corresponding function to read it

        two formats available:
            phonopy files (band.yaml)
            internal .json format (*.json) description available in ./phononweb/phononweb.py
        */

        let yaml = null;
        let json = null;

        for (let i=0; i<event.target.files.length; i++) {
            let file = event.target.files[i];
            if (file.name.indexOf(".yaml") > -1) { yaml = file; }
            if (file.name.indexOf(".json") > -1) { json = file; }
        }

        this.name = "Custom file"
        if      (json) {
            this.phonon = new PhononJson();
            this.phonon.getFromFile(json, this.loadCallback.bind(this) );
        }
        else if (yaml) {
            this.phonon = new PhononYaml();
            this.phonon.getFromFile(yaml, this.loadCallback.bind(this) );
        }
        else {
            alert("Ivalid file");
        }
    }

    loadURL(url_vars) {
        /*
        load file from post request in the url

        two formats available:
            phonopy files (band.yaml)
            internal .json format (*.json) description available in ./phononweb/phononweb.py
        */

        function onReadJson(text) {
            this.phonon = new PhononJson();
            this.phonon.getFromString(text, this.loadCallback.bind(this) );
        }

        function onReadYaml(text) {
            this.phonon = new PhononYaml();
            this.phonon.getFromString(text, this.loadCallback.bind(this) );
        }

        if (!("yaml" in url_vars) && !("json" in url_vars)) {
            alert("Ivalid url");
        }

        this.name = "Custom file";
        for (let key in url_vars) {
            if ( key == "yaml" ) { $.get(url_vars[key], onReadYaml.bind(this)); }
            if ( key == "json" ) { $.get(url_vars[key], onReadJson.bind(this)); }
            if ( key == "name" ) { $('#t1').html(url_vars[key]);     }
        }

    }

    loadId(id) {
        /*
        load a material from phonodb
        http://phonondb.mtl.kyoto-u.ac.jp
        */

        this.loadURL( { yaml: this.materials[id]['url'] } );
        this.name = this.materials[id]['name'] +  " mp-"+id+"";
        this.update();
    }

    loadLocal(folder='graphene') {
        /*
        read structure from a local file distributed with the repository
        */

        let readJson = function(string) {
            this.phonon = new PhononJson();
            this.phonon.getFromString(string, this.loadCallback.bind(this) );
        }

        $.get(folder+'/data.json', readJson.bind(this), "html" );
    }

    loadCallback() {
        /*
        Fuunction to be called once the file is loaded
        */
        this.setRepetitions(this.phonon.repetitions);
        this.update()
    }

    getStructure(nx,ny,nz) {
        let lat = this.phonon.lat;
        let apc = this.phonon.atom_pos_car;
        let atoms = [];

	    for (let ix=0;ix<nx;ix++) {
            for (let iy=0;iy<ny;iy++) {
                for (let iz=0;iz<nz;iz++) {
                    for (let i=0;i<this.phonon.natoms;i++) {

                        //postions of the atoms
                        let x = apc[i][0] + ix*lat[0][0] + iy*lat[1][0] + iz*lat[2][0];
                        let y = apc[i][1] + ix*lat[0][1] + iy*lat[1][1] + iz*lat[2][1];
                        let z = apc[i][2] + ix*lat[0][2] + iy*lat[1][2] + iz*lat[2][2];

                        atoms.push( [i,x,y,z] );
                    }
                }
            }
        }

        return atoms;
    }

    getBondingDistance() {
        /*
        replicate the atoms two times in each direction
        */
        let atoms = this.getStructure(2,2,2);

        let combinations = getCombinations( atoms );
        let min = 1e9;
        for (let i=0; i<combinations.length; i++ ) {
            let a = combinations[i][0];
            let b = combinations[i][1];

            let distance = dist(a.slice(1),b.slice(1));
            if (min > distance) {
                min = distance;
            }
        }
        return min;
    }

    getVibrations(nx,ny,nz) {
        /*
        Calculate the vibration patterns for all the atoms
        */
        let phonon = this.phonon;
        let veckn = phonon.vec[this.k][this.n];
        let vibrations = [];
        let kpt = phonon.kpoints[this.k];

        //additional phase if necessary
        let atom_phase = []
        if (phonon.addatomphase) {
            for (let i=0; i<phonon.natoms; i++) {
                let phase = kpt[0]*phonon.atom_pos_red[i][0] +
                        kpt[1]*phonon.atom_pos_red[i][1] +
                        kpt[2]*phonon.atom_pos_red[i][2];
                atom_phase.push(phase);
            }
        }
        else {
            for (let i=0; i<phonon.natoms; i++) {
                atom_phase.push(0);
            }
        }

        for (var ix=0; ix<nx; ix++) {
            for (var iy=0; iy<ny; iy++) {
                for (var iz=0; iz<nz; iz++) {

                    for (let i=0; i<phonon.natoms; i++) {
                        let sprod = kpt[0]*ix + kpt[1]*iy + kpt[2]*iz + atom_phase[i];
                        let phase = Complex.Polar(1.0,sprod*2.0*pi);

                        //Displacements of the atoms
                        let x = Complex(veckn[i][0][0],veckn[i][0][1]).mult(phase);
                        let y = Complex(veckn[i][1][0],veckn[i][1][1]).mult(phase);
                        let z = Complex(veckn[i][2][0],veckn[i][2][1]).mult(phase);

                        vibrations.push( [x,y,z] );
                    }
                }
            }
        }

        return vibrations;
    }

    setVibrations() {
        this.vibrations = this.getVibrations(this.nx,this.ny,this.nz);
    }

    updatePage() {
        /*
        lattice vectors table
        */

        for (let i=0; i<3; i++) {
            for (let j=0; j<3; j++) {
                //round lattice values
                $('#uc_'+i+j).html( this.phonon.lat[i][j].toPrecision(5) );
            }
        }

        //unit cell table
        $('#uc_natoms').html( this.phonon.natoms );
        $('#uc_atypes').html( this.phonon.formula );

        //atomic positions table
        let pos = this.phonon.atom_pos_red;
        $('#atompos').empty() //clean the atomic positions table
        for (let i=0; i<this.phonon.natoms; i++) {
            $('#atompos').append('

');
            $('#atompos tr:last').append(''+this.phonon.atom_types[i]+'	');
            for (let j=0; j<3; j++) {
                $('#atompos tr:last').append(''+pos[i][j].toFixed(4)+'	');
            }
        }

        //update title
        $('#t1').html(this.name);
    }

    update(dispersion=true) {
        /*
        Update all the aspects fo the webpage
        */

        //update structure
        this.getRepetitions();
        this.atoms = this.getStructure(this.nx,this.ny,this.nz);
        this.vibrations = this.getVibrations(this.nx,this.ny,this.nz);
        this.phonon.nndist = this.getBondingDistance();

        //update page
        this.updatePage();

        //update dispersion
        if (dispersion) { this.dispersion.update(this.phonon); }

        //update visualizer
        this.visualizer.update(this);
    }

    createPhonodbMenu(phonodb) {
        /*
        Create the phonondb menu
        */

        $("#div-phonodb").css('display', 'inline');
        let materials = jsyaml.load(phonodb);
        let materials_ref = {};
        $('#phonodb').empty();
        for (let i=0;i<materials.length;i++) {
            let material = materials[i];
            materials_ref[material['id']] = material;
            $('#phonodb').append('

');
            $('#phonodb li:last').append(""+material['name']+"");
        }

        this.materials = materials_ref;
    }

    updateMenu() {
        /*
        create menu with:
            local files (files distributed with the website)
            files from the phonodb database
        */

        //get a list of local materials
        $.getJSON('models.json', function(data) {
            let nmodels = data["nmodels"];
            let models  = data["models"];
            $('#mat').empty() //clean the atomic positions table

            for (let i=0; i<nmodels; i++) {
                $('#mat').append('

');
                $('#mat li:last').append(""+models[i]["name"]+"");
            }
        });

        //get a list of materials from phonodb
        $.get('phonondb.yaml', this.createPhonodbMenu.bind(this));
    }

    static getUrlVars() {
        /*
        get variables from the url
        from http://stackoverflow.com/questions/4656843/jquery-get-querystring-from-url

        currently the possible options are:
            json : load a json file from location
            yaml : load a yaml file from location
            name : change the display name of the material
        */
        let vars = {};
        let hash;

        if (location.search) {
            let hashes = location.search.slice(1).split('&');
            for(let i = 0; i < hashes.length; i++) {
                hash = hashes[i].split('=');
                vars[hash[0]] = hash[1];
            }
            return vars;
        }
        else {
            return null;
        }
    }

}

$(document).ready(function() {

    //visualizer
    v = new VibCrystal($('#vibcrystal'));
    //dispersion
    d = new PhononHighcharts($('#highcharts'));
    //phonon class
    p = new PhononWebpage(v,d);

    /*
    check if its Chrome 1+ taken from
    http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
    only show webm button for chrome
    */
    let isChrome = !!window.chrome && !!window.chrome.webstore;
    if (!isChrome) {
        $('#webmbutton')[0].style.visibility = 'hidden';
    }

    //get if the draw vectors option is activated
    v.arrows = $('#drawvectors')[0].checked;
    $('#file-input')[0].addEventListener('change', p.loadCustomFile.bind(p), false);
    $('#file-input')[0].addEventListener('click', function() { this.value = '';}, false);
    p.updateMenu();

    let url_vars = PhononWebpage.getUrlVars();
    if (url_vars) { p.loadURL(url_vars); }
    else          { p.loadLocal();       }

    // check if webgl is available
    if ( ! Detector.webgl ) {
        Detector.addGetWebGLMessage();
    }

    //jquery to make an action once you change the number of repetitions
    $(".input-rep").keyup(function(event){
        if(event.keyCode == 13) p.update(dispersion=false);
    });

});
<commit_msg>Fixed bug when loading local matrials<commit_after>const pi = 3.14159265359;
const bohr2ang = 0.529177249;

class PhononWebpage {

    constructor(visualizer, dispersion) {
        this.k = 0;
        this.n = 0;
        this.nx = 1;
        this.ny = 1;
        this.nz = 1;
        this.amplitude = 0.5;

        //select visualization
        this.visualizer = visualizer;

        //select dispersion
        this.dispersion = dispersion;

        //bind some functions (TODO: improve this)
        this.exportXSF    = exportXSF.bind(this);
        this.exportPOSCAR = exportPOSCAR.bind(this);
    }

    getRepetitions() {
        /*
        read the number of repetitions in each direction and update it
        */
        this.nx = $('#nx').val();
        this.ny = $('#ny').val();
        this.nz = $('#nz').val();
    }

    setRepetitions(repetitions) {
        /*
        set the number of repetitions on the interface
        */

        if (repetitions) {
            this.nx = repetitions[0];
            this.ny = repetitions[1];
            this.nz = repetitions[2];
        }

        $('#nx').val(this.nx);
        $('#ny').val(this.ny);
        $('#nz').val(this.nz);
    }

    loadCustomFile(event) {
        /*
        find the type of file and call the corresponding function to read it

        two formats available:
            phonopy files (band.yaml)
            internal .json format (*.json) description available in ./phononweb/phononweb.py
        */

        let yaml = null;
        let json = null;

        for (let i=0; i<event.target.files.length; i++) {
            let file = event.target.files[i];
            if (file.name.indexOf(".yaml") > -1) { yaml = file; }
            if (file.name.indexOf(".json") > -1) { json = file; }
        }

        this.name = "Custom file"
        if      (json) {
            this.phonon = new PhononJson();
            this.phonon.getFromFile(json, this.loadCallback.bind(this) );
        }
        else if (yaml) {
            this.phonon = new PhononYaml();
            this.phonon.getFromFile(yaml, this.loadCallback.bind(this) );
        }
        else {
            alert("Ivalid file");
        }
    }

    loadURL(url_vars) {
        /*
        load file from post request in the url

        two formats available:
            phonopy files (band.yaml)
            internal .json format (*.json) description available in ./phononweb/phononweb.py
        */

        function onReadJson(text) {
            this.phonon = new PhononJson();
            this.phonon.getFromString(text, this.loadCallback.bind(this) );
        }

        function onReadYaml(text) {
            this.phonon = new PhononYaml();
            this.phonon.getFromString(text, this.loadCallback.bind(this) );
        }

        if (!("yaml" in url_vars) && !("json" in url_vars)) {
            alert("Ivalid url");
        }

        this.name = "Custom file";
        for (let key in url_vars) {
            if ( key == "yaml" ) { $.get(url_vars[key], onReadYaml.bind(this)); }
            if ( key == "json" ) { $.get(url_vars[key], onReadJson.bind(this)); }
            if ( key == "name" ) { $('#t1').html(url_vars[key]);     }
        }

    }

    loadId(id) {
        /*
        load a material from phonodb
        http://phonondb.mtl.kyoto-u.ac.jp
        */

        this.loadURL( { yaml: this.materials[id]['url'] } );
        this.name = this.materials[id]['name'] +  " mp-"+id+"";
        this.update();
    }

    loadLocal(folder='graphene') {
        /*
        read structure from a local file distributed with the repository
        */

        let readJson = function(string) {
            this.phonon = new PhononJson();
            this.phonon.getFromString(string, this.loadCallback.bind(this) );
        }

        $.get(folder+'/data.json', readJson.bind(this), "html" );
    }

    loadCallback() {
        /*
        Fuunction to be called once the file is loaded
        */
        this.setRepetitions(this.phonon.repetitions);
        this.update()
    }

    getStructure(nx,ny,nz) {
        let lat = this.phonon.lat;
        let apc = this.phonon.atom_pos_car;
        let atoms = [];

	    for (let ix=0;ix<nx;ix++) {
            for (let iy=0;iy<ny;iy++) {
                for (let iz=0;iz<nz;iz++) {
                    for (let i=0;i<this.phonon.natoms;i++) {

                        //postions of the atoms
                        let x = apc[i][0] + ix*lat[0][0] + iy*lat[1][0] + iz*lat[2][0];
                        let y = apc[i][1] + ix*lat[0][1] + iy*lat[1][1] + iz*lat[2][1];
                        let z = apc[i][2] + ix*lat[0][2] + iy*lat[1][2] + iz*lat[2][2];

                        atoms.push( [i,x,y,z] );
                    }
                }
            }
        }

        return atoms;
    }

    getBondingDistance() {
        /*
        replicate the atoms two times in each direction
        */
        let atoms = this.getStructure(2,2,2);

        let combinations = getCombinations( atoms );
        let min = 1e9;
        for (let i=0; i<combinations.length; i++ ) {
            let a = combinations[i][0];
            let b = combinations[i][1];

            let distance = dist(a.slice(1),b.slice(1));
            if (min > distance) {
                min = distance;
            }
        }
        return min;
    }

    getVibrations(nx,ny,nz) {
        /*
        Calculate the vibration patterns for all the atoms
        */
        let phonon = this.phonon;
        let veckn = phonon.vec[this.k][this.n];
        let vibrations = [];
        let kpt = phonon.kpoints[this.k];

        //additional phase if necessary
        let atom_phase = []
        if (phonon.addatomphase) {
            for (let i=0; i<phonon.natoms; i++) {
                let phase = kpt[0]*phonon.atom_pos_red[i][0] +
                        kpt[1]*phonon.atom_pos_red[i][1] +
                        kpt[2]*phonon.atom_pos_red[i][2];
                atom_phase.push(phase);
            }
        }
        else {
            for (let i=0; i<phonon.natoms; i++) {
                atom_phase.push(0);
            }
        }

        for (var ix=0; ix<nx; ix++) {
            for (var iy=0; iy<ny; iy++) {
                for (var iz=0; iz<nz; iz++) {

                    for (let i=0; i<phonon.natoms; i++) {
                        let sprod = kpt[0]*ix + kpt[1]*iy + kpt[2]*iz + atom_phase[i];
                        let phase = Complex.Polar(1.0,sprod*2.0*pi);

                        //Displacements of the atoms
                        let x = Complex(veckn[i][0][0],veckn[i][0][1]).mult(phase);
                        let y = Complex(veckn[i][1][0],veckn[i][1][1]).mult(phase);
                        let z = Complex(veckn[i][2][0],veckn[i][2][1]).mult(phase);

                        vibrations.push( [x,y,z] );
                    }
                }
            }
        }

        return vibrations;
    }

    setVibrations() {
        this.vibrations = this.getVibrations(this.nx,this.ny,this.nz);
    }

    updatePage() {
        /*
        lattice vectors table
        */

        for (let i=0; i<3; i++) {
            for (let j=0; j<3; j++) {
                //round lattice values
                $('#uc_'+i+j).html( this.phonon.lat[i][j].toPrecision(5) );
            }
        }

        //unit cell table
        $('#uc_natoms').html( this.phonon.natoms );
        $('#uc_atypes').html( this.phonon.formula );

        //atomic positions table
        let pos = this.phonon.atom_pos_red;
        $('#atompos').empty() //clean the atomic positions table
        for (let i=0; i<this.phonon.natoms; i++) {
            $('#atompos').append('

');
            $('#atompos tr:last').append(''+this.phonon.atom_types[i]+'	');
            for (let j=0; j<3; j++) {
                $('#atompos tr:last').append(''+pos[i][j].toFixed(4)+'	');
            }
        }

        //update title
        $('#t1').html(this.name);
    }

    update(dispersion=true) {
        /*
        Update all the aspects fo the webpage
        */

        //update structure
        this.getRepetitions();
        this.atoms = this.getStructure(this.nx,this.ny,this.nz);
        this.vibrations = this.getVibrations(this.nx,this.ny,this.nz);
        this.phonon.nndist = this.getBondingDistance();

        //update page
        this.updatePage();

        //update dispersion
        if (dispersion) { this.dispersion.update(this.phonon); }

        //update visualizer
        this.visualizer.update(this);
    }

    createPhonodbMenu(phonodb) {
        /*
        Create the phonondb menu
        */

        $("#div-phonodb").css('display', 'inline');
        let materials = jsyaml.load(phonodb);
        let materials_ref = {};
        $('#phonodb').empty();
        for (let i=0;i<materials.length;i++) {
            let material = materials[i];
            materials_ref[material['id']] = material;
            $('#phonodb').append('

');
            $('#phonodb li:last').append(""+material['name']+"");
        }

        this.materials = materials_ref;
    }

    updateMenu() {
        /*
        create menu with:
            local files (files distributed with the website)
            files from the phonodb database
        */

        //get a list of local materials
        $.getJSON('models.json', function(data) {
            let nmodels = data["nmodels"];
            let models  = data["models"];
            $('#mat').empty() //clean the atomic positions table

            for (let i=0; i<nmodels; i++) {
                $('#mat').append('

');
                $('#mat li:last').append(""+models[i]["name"]+"");
            }
        });

        //get a list of materials from phonodb
        $.get('phonondb.yaml', this.createPhonodbMenu.bind(this));
    }

    static getUrlVars() {
        /*
        get variables from the url
        from http://stackoverflow.com/questions/4656843/jquery-get-querystring-from-url

        currently the possible options are:
            json : load a json file from location
            yaml : load a yaml file from location
            name : change the display name of the material
        */
        let vars = {};
        let hash;

        if (location.search) {
            let hashes = location.search.slice(1).split('&');
            for(let i = 0; i < hashes.length; i++) {
                hash = hashes[i].split('=');
                vars[hash[0]] = hash[1];
            }
            return vars;
        }
        else {
            return null;
        }
    }

}

$(document).ready(function() {

    //visualizer
    v = new VibCrystal($('#vibcrystal'));
    //dispersion
    d = new PhononHighcharts($('#highcharts'));
    //phonon class
    p = new PhononWebpage(v,d);

    /*
    check if its Chrome 1+ taken from
    http://stackoverflow.com/questions/9847580/how-to-detect-safari-chrome-ie-firefox-and-opera-browser
    only show webm button for chrome
    */
    let isChrome = !!window.chrome && !!window.chrome.webstore;
    if (!isChrome) {
        $('#webmbutton')[0].style.visibility = 'hidden';
    }

    //get if the draw vectors option is activated
    v.arrows = $('#drawvectors')[0].checked;
    $('#file-input')[0].addEventListener('change', p.loadCustomFile.bind(p), false);
    $('#file-input')[0].addEventListener('click', function() { this.value = '';}, false);
    p.updateMenu();

    let url_vars = PhononWebpage.getUrlVars();
    if (url_vars) { p.loadURL(url_vars); }
    else          { p.loadLocal();       }

    // check if webgl is available
    if ( ! Detector.webgl ) {
        Detector.addGetWebGLMessage();
    }

    //jquery to make an action once you change the number of repetitions
    $(".input-rep").keyup(function(event){
        if(event.keyCode == 13) p.update(dispersion=false);
    });

});
<|endoftext|>


<commit_before>if (!script) {
    window.addEventListener("LogEvent", function(e) {
        if (e.detail.includes("fired playing") || e.detail.includes("fired waiting")) {
            chrome.runtime.sendMessage({ state: "Pause" });
        } else if (e.detail.includes("fired pause")) {
            chrome.runtime.sendMessage({ state: "Play" });
        }
    }, false);

    function listenLog() {
        var originalLog = console.log;
        console.log = function(e) {
            originalLog(e);
            window.dispatchEvent(new CustomEvent("LogEvent", { detail: e }));
        }
    }

    var script = document.createElement('script');
    script.appendChild(document.createTextNode('(' + listenLog + ')();'));
    (document.body || document.head || document.documentElement).appendChild(script);
}<commit_msg>additional events for pause state<commit_after>if (!script) {
    window.addEventListener("LogEvent", function(e) {
        var evt = e.detail;
        if (evt.includes("fired playing") || evt.includes("fired waiting")) {
            chrome.runtime.sendMessage({ state: "Pause" });
        } else if (evt.includes("fired pause") || evt.includes("fired ended") || evt.includes("fired abort")) {
            chrome.runtime.sendMessage({ state: "Play" });
        }
    }, false);

    function listenLog() {
        var originalLog = console.log;
        console.log = function(e) {
            originalLog(e);
            window.dispatchEvent(new CustomEvent("LogEvent", { detail: e }));
        }
    }

    var script = document.createElement('script');
    script.appendChild(document.createTextNode('(' + listenLog + ')();'));
    (document.body || document.head || document.documentElement).appendChild(script);
}
<|endoftext|>