<commit_before>import url from 'url';
import traverson from 'traverson';
import pluralize from 'pluralize';
import hal_adapter from 'traverson-hal';
import debug_logger from 'debug';

let debug = debug_logger('rest-client-builder');

traverson.registerMediaType(hal_adapter.mediaType, hal_adapter);

function json(res) {
    try {
        return res && res.body ? JSON.parse(res.body) : {};
    }
    catch(e) {
        return {};
    }
}

class hal_client {
    constructor() {
        this._uri = null;
        this._api = null;
    }

    from(uri) {
        this._uri = uri;
        this._api = traverson
            .from(uri)
            .jsonHal()
            .withRequestOptions({
                headers: {
                    'Accept': 'application/hal+json',
                    'Content-Type': 'application/json'
                }
            });
         return this;
    }

    get uri() {
        return this._uri;
    }

    get api() {
        return this._api;
    }

    root() {
        return new Promise((resolve, reject) => {
            this.api
                .newRequest()
                .get((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    resource_at(href, params) {
        return new Promise((resolve, reject) => {
            this.api
                .newRequest()
                .from(url.resolve(this.api.getFrom(), href))
                .withTemplateParameters(params)
                .get((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }
}

class resource_proxy {
    constructor(client, name, parent, actions) {
        this._name = name;
        this._children = [];
        this._actions = actions || {
            find: true,
            create: true,
            update: true,
            patch: true,
            delete: true,
            find_by_id: true,
            delete_by_id: true,
            find_by_named_query: true
        };
        this._client = client;
        this._parent = parent || null;
        this._singular_name = pluralize.singular(name);
        this._id_name = `${this._singular_name}_id`;
    }

    get name() {
        return this._name;
    }

    get client() {
        return this._client;
    }

    get parent() {
        return this._parent;
    }

    get singular_name() {
        return this._singular_name;
    }

    _throw_if_disabled(action) {
        if (!this._actions[action])
            throw new Error(`This resource does not permit '${action}'.`);
    }

    find(params, options) {
        this._throw_if_disabled('find');
        for (let x in params) {
            if (Array.isArray(params[x]))
                params[x] = params[x].join(',');
        }
        let opts = Object.assign({}, { qs: params || {} }, options);
        return new Promise((resolve, reject) => {
            this.client
                .api
                .newRequest()
                .withRequestOptions(opts)
                .follow(this.name)
                .get((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    create(data, options) {
        this._throw_if_disabled('create');
        if (!data)
            throw new Error("The 'data' parameter is required.");
        return new Promise((resolve, reject) => {
            let request = this.client.api.newRequest();
            if (options)
                request = request.withRequestOptions(options);
            request
                .follow(this.name)
                .post(data, (error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    find_by_id(id, options) {
        this._throw_if_disabled('find_by_id');
        if (!id)
            throw new Error("The 'id' parameter is required.");
        let params = {};
        params[this._id_name] = id;
        return new Promise((resolve, reject) => {
            let request = this.client.api.newRequest();
            if (options)
                request = request.withRequestOptions(options);
            request
                .follow(this.name)
                .withTemplateParameters(params)
                .get((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    patch(id, data, options) {
        this._throw_if_disabled('patch');
        if (!id)
            throw new Error("The 'id' parameter is required.");
        if (!data)
            throw new Error("The 'data' parameter is required.");
        let params = {};
        params[this._id_name] = id;
        return new Promise((resolve, reject) => {
            let request = this.client.api.newRequest();
            if (options)
                request = request.withRequestOptions(options);
            request
                .follow(this.name)
                .withTemplateParameters(params)
                .patch(data, (error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    delete(filters, options) {
        this._throw_if_disabled('delete');
        let params = {};
        let opts = Object.assign({}, { qs: filters || {} }, options);
        return new Promise((resolve, reject) => {
            this.client
                .api
                .newRequest()
                .withRequestOptions(opts)
                .follow(this.name)
                .withTemplateParameters(params)
                .del((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    delete_by_id(id, options) {
        this._throw_if_disabled('delete_by_id');
        if (!id)
            throw new Error("The 'id' parameter is required.");
        let params = {};
        params[this._id_name] = id;
        return new Promise((resolve, reject) => {
            let request = this.client.api.newRequest();
            if (options)
                request = request.withRequestOptions(options);
            request
                .follow(this.name)
                .withTemplateParameters(params)
                .del((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    update(id, data, options) {
        this._throw_if_disabled('update');
        if (!id)
            throw new Error("The 'id' parameter is required.");
        if (!data)
            throw new Error("The 'data' parameter is required.");
        let params = {};
        params[this._id_name] = id;
        return new Promise((resolve, reject) => {
            let request = this.client.api.newRequest();
            if (options)
                request = request.withRequestOptions(options);
            request
                .follow(this.name)
                .withTemplateParameters(params)
                .put(data, (error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    find_by_named_query(name, options) {
        this._throw_if_disabled('find_by_named_query');
        if (!name)
            throw new Error("The 'name' parameter is required.");
        let params = {};
        params[this._id_name] = name;
        return new Promise((resolve, reject) => {
            let request = this.client.api.newRequest();
            if (options)
                request = request.withRequestOptions(options);
            request
                .follow(this.name)
                .withTemplateParameters(params)
                .get((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }
}

class resource_builder {
    constructor(name, parent) {
        this._name = name;
        this._actions = {
            find: true,
            create: true,
            update: true,
            patch: true,
            delete: true,
            find_by_id: true,
            delete_by_id: true,
            find_by_named_query: true
        };
        this._children = [];
        this._description = null;
        this._parent = parent || null;
    }

    get name() {
        return this._name;
    }

    get parent() {
        return this._parent;
    }

    resource(name) {
        let b = new resource_builder(name, this);
        this._children.push(b);
        return b;
    }

    description(desc) {
        this._description = desc;
        return this;
    }

    disable_find() {
        this._actions.find = false;
        return this;
    }

    disable_create() {
        this._actions.create = false;
        return this;
    }

    disable_update() {
        this._actions.update = false;
        return this;
    }

    disable_patch() {
        this._actions.patch = false;
        return this;
    }

    disable_delete() {
        this._actions.delete = false;
        return this;
    }

    disable_find_by_id() {
        this._actions.find_by_id = false;
        return this;
    }

    disable_delete_by_id() {
        this._actions.delete_by_id = false;
        return this;
    }

    disable_find_by_named_query() {
        this._actions.find_by_named_query = false;
        return this;
    }
}

class root_resource_builder {
    constructor() {
        this._children = [];
    }

    hal() {
        let h = new hal_client();

        function make_client(parent, resources) {
            if (!parent || !resources || resources.length === 0) return;
            resources.forEach(x => {
                let proxy = new resource_proxy(h, x.name, parent, x._actions);
                make_client(proxy, x._children);
                parent[x.name] = proxy;
            });
        }

        make_client(h, this._children);

        return h;
    }

    resource(name) {
        let b = new resource_builder(name, this);
        this._children.push(b);
        return b;
    }
}

export class rest_client_builder {
    constructor() {
    }

    root() {
        return new root_resource_builder();
    }
}
<commit_msg>updated: refactor how parent/child relationships work in the client builder.<commit_after>import url from 'url';
import traverson from 'traverson';
import pluralize from 'pluralize';
import hal_adapter from 'traverson-hal';
import debug_logger from 'debug';

let debug = debug_logger('rest-client-builder');

traverson.registerMediaType(hal_adapter.mediaType, hal_adapter);

function json(res) {
    try {
        return res && res.body ? JSON.parse(res.body) : {};
    }
    catch(e) {
        return {};
    }
}

function add_child_accessors(client, parent, children) {
    if (!children)
        return;
    children.forEach(x => {
        parent[x.name] = () => {
            let proxy = new resource_proxy(client, x.name, parent, x._actions);
            add_child_accessors(client, proxy, x._children);
            return proxy;
        };
        parent[pluralize.singular(x.name)] = id => {
            if (!id)
                throw new Error("The 'id' parameter is required.");
            let proxy = new resource_proxy(client, x.name, parent, x._actions, id);
            add_child_accessors(client, proxy, x._children);
            return proxy;
        };
    });
}

class hal_client {
    constructor() {
        this._uri = null;
        this._api = null;
    }

    from(uri) {
        this._uri = uri;
        this._api = traverson
            .from(uri)
            .jsonHal()
            .withRequestOptions({
                headers: {
                    'Accept': 'application/hal+json',
                    'Content-Type': 'application/json'
                }
            });
         return this;
    }

    get uri() {
        return this._uri;
    }

    get api() {
        return this._api;
    }

    root() {
        return new Promise((resolve, reject) => {
            this.api
                .newRequest()
                .get((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    resource_at(href, params) {
        return new Promise((resolve, reject) => {
            this.api
                .newRequest()
                .from(url.resolve(this.api.getFrom(), href))
                .withTemplateParameters(params)
                .get((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }
}

class resource_proxy {
    constructor(client, name, parent, actions, parent_id) {
        this._name = name;
        this._children = [];
        this._actions = actions || {
            find: true,
            create: true,
            update: true,
            patch: true,
            delete: true,
            find_by_id: true,
            delete_by_id: true,
            find_by_named_query: true
        };
        this._client = client;
        this._parent = parent || null;
        this._parent_id = parent_id || null;
        this._singular_name = pluralize.singular(name);
        this._id_name = `${this._singular_name}_id`;
    }

    get name() {
        return this._name;
    }

    get client() {
        return this._client;
    }

    get parent() {
        return this._parent;
    }

    get parent_id() {
        return this._parent_id;
    }

    get singular_name() {
        return this._singular_name;
    }

    _throw_if_disabled(action) {
        if (!this._actions[action])
            throw new Error(`This resource does not permit '${action}'.`);
    }

    find(params, options) {
        this._throw_if_disabled('find');
        for (let x in params) {
            if (Array.isArray(params[x]))
                params[x] = params[x].join(',');
        }
        let opts = Object.assign({}, { qs: params || {} }, options);
        return new Promise((resolve, reject) => {
            this.client
                .api
                .newRequest()
                .withRequestOptions(opts)
                .follow(this.name)
                .get((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    create(data, options) {
        this._throw_if_disabled('create');
        if (!data)
            throw new Error("The 'data' parameter is required.");
        return new Promise((resolve, reject) => {
            let request = this.client.api.newRequest();
            if (options)
                request = request.withRequestOptions(options);
            request
                .follow(this.name)
                .post(data, (error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    find_by_id(id, options) {
        this._throw_if_disabled('find_by_id');
        if (!id)
            throw new Error("The 'id' parameter is required.");
        let params = {};
        params[this._id_name] = id;
        return new Promise((resolve, reject) => {
            let request = this.client.api.newRequest();
            if (options)
                request = request.withRequestOptions(options);
            request
                .follow(this.name)
                .withTemplateParameters(params)
                .get((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    patch(id, data, options) {
        this._throw_if_disabled('patch');
        if (!id)
            throw new Error("The 'id' parameter is required.");
        if (!data)
            throw new Error("The 'data' parameter is required.");
        let params = {};
        params[this._id_name] = id;
        return new Promise((resolve, reject) => {
            let request = this.client.api.newRequest();
            if (options)
                request = request.withRequestOptions(options);
            request
                .follow(this.name)
                .withTemplateParameters(params)
                .patch(data, (error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    delete(filters, options) {
        this._throw_if_disabled('delete');
        let params = {};
        let opts = Object.assign({}, { qs: filters || {} }, options);
        return new Promise((resolve, reject) => {
            this.client
                .api
                .newRequest()
                .withRequestOptions(opts)
                .follow(this.name)
                .withTemplateParameters(params)
                .del((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    delete_by_id(id, options) {
        this._throw_if_disabled('delete_by_id');
        if (!id)
            throw new Error("The 'id' parameter is required.");
        let params = {};
        params[this._id_name] = id;
        return new Promise((resolve, reject) => {
            let request = this.client.api.newRequest();
            if (options)
                request = request.withRequestOptions(options);
            request
                .follow(this.name)
                .withTemplateParameters(params)
                .del((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    update(id, data, options) {
        this._throw_if_disabled('update');
        if (!id)
            throw new Error("The 'id' parameter is required.");
        if (!data)
            throw new Error("The 'data' parameter is required.");
        let params = {};
        params[this._id_name] = id;
        return new Promise((resolve, reject) => {
            let request = this.client.api.newRequest();
            if (options)
                request = request.withRequestOptions(options);
            request
                .follow(this.name)
                .withTemplateParameters(params)
                .put(data, (error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }

    find_by_named_query(name, options) {
        this._throw_if_disabled('find_by_named_query');
        if (!name)
            throw new Error("The 'name' parameter is required.");
        let params = {};
        params[this._id_name] = name;
        return new Promise((resolve, reject) => {
            let request = this.client.api.newRequest();
            if (options)
                request = request.withRequestOptions(options);
            request
                .follow(this.name)
                .withTemplateParameters(params)
                .get((error, response) => error ? reject(error) : resolve({ response, resource: json(response) }));
        });
    }
}

class resource_builder {
    constructor(name, parent) {
        this._name = name;
        this._actions = {
            find: true,
            create: true,
            update: true,
            patch: true,
            delete: true,
            find_by_id: true,
            delete_by_id: true,
            find_by_named_query: true
        };
        this._children = [];
        this._description = null;
        this._parent = parent || null;
    }

    get name() {
        return this._name;
    }

    get parent() {
        return this._parent;
    }

    resource(name) {
        let b = new resource_builder(name, this);
        this._children.push(b);
        return b;
    }

    description(desc) {
        this._description = desc;
        return this;
    }

    disable_find() {
        this._actions.find = false;
        return this;
    }

    disable_create() {
        this._actions.create = false;
        return this;
    }

    disable_update() {
        this._actions.update = false;
        return this;
    }

    disable_patch() {
        this._actions.patch = false;
        return this;
    }

    disable_delete() {
        this._actions.delete = false;
        return this;
    }

    disable_find_by_id() {
        this._actions.find_by_id = false;
        return this;
    }

    disable_delete_by_id() {
        this._actions.delete_by_id = false;
        return this;
    }

    disable_find_by_named_query() {
        this._actions.find_by_named_query = false;
        return this;
    }
}

class root_resource_builder {
    constructor() {
        this._children = [];
    }

    hal() {
        let client = new hal_client();
        add_child_accessors(client, client, this._children);
        return client;
    }

    resource(name) {
        let b = new resource_builder(name, this);
        this._children.push(b);
        return b;
    }
}

export class rest_client_builder {
    constructor() {
    }

    root() {
        return new root_resource_builder();
    }
}
<|endoftext|>


<commit_before>(function() {
  'use strict';

  var SoundCloudSetter = function(audio) {
    var self = this;
    var client_id = SOUNDCLOUD_API_KEY;

    self.streamUrl = "";
    self.artwork_url = "";
    self.audio = audio;

    this.loadSoundCloud = function(track_url, successCallback, errorCallback) {

      SC.initialize({
        client_id: client_id
      });

      SC.resolve(track_url)
        .then(function(data){
        self.streamUrl = data.stream_url + '?client_id=' + client_id;
        self.artwork_url = data.artwork_url;
        successCallback();

      }).catch(function(error){

        errorCallback(error);
      });
    };

    this.directLoadSoundCloud = function(direction) {

      if (this.audio.paused) {
        this.audio.play();
      } else {
        this.audio.pause();
      }
    }
  };

  var SoundCloudAudioSource = function(audio){
    var self = this
      , audioCtx = new (window.AudioContext || window.webkitAudioContext)
      , source = audioCtx.createMediaElementSource(audio);

    self.analyser = audioCtx.createAnalyser();
    self.analyser.fftSize = 256;
    audio.crossOrigin = "anonymous";
    source.connect(self.analyser);
    self.analyser.connect(audioCtx.destination);

    this.bufferLength = self.analyser.frequencyBinCount;

    this.dataArray = new Uint8Array(this.bufferLength);

    this.playStream = function(streamUrl) {
      audio.setAttribute('src', streamUrl);
      audio.play();
    }

  };

  var ControlGroup = function() {

    var ctrGroup = document.querySelector('.ctrgroup');

    this.toggle = function() {

      if (ctrGroup.className.indexOf('ctrgroup--hidden') === 9) {
        ctrGroup.className = ctrGroup.className.split(' ')[0];
      } else {
        ctrGroup.className = ctrGroup.className + ' ctrgroup--hidden';
      }

    };

  };

  var Visualizer = function() {
    var fgCanvas
      , fgCtx
      , bgCanvas
      , bgCtx
      , albumImg
      , canvas
      , audioSource
      , gradientColor = {
          0: ['#89FFFD', '#EF32D9'],
          1: ['#00DBDE', '#FC00FF'],
          2: ['#7BC6CC', '#BE93C5'],
          3 :['#E55D87', '#5FC3E4']
        };

    var drawBg = function(){
      bgCtx.clearRect(0, 0, bgCanvas.width, bgCanvas.height);
      bgCtx.beginPath();
      bgCtx.rect(0, 0, bgCanvas.width, bgCanvas.height);

      var r = Math.floor(Math.random() * (3 + 1))
        , gradient = bgCtx.createLinearGradient(0,0,1500,0);

      gradient.addColorStop(0, gradientColor[r][0]); // #00DBDE'rgb(0, 219, 222)'
      gradient.addColorStop(1, gradientColor[r][1]); // #fc00ff'rgb(252, 0, 255)'

      bgCtx.fillStyle = gradient;
      bgCtx.fill();
    };

    this.resizeCanvas = function() {
      if (fgCanvas) {

        // resize the foreground canvas
        fgCanvas.width = window.innerWidth;
        fgCanvas.height = window.innerHeight;

        // resize the bg canvas
        bgCanvas.width = window.innerWidth;
        bgCanvas.height = window.innerHeight;

        drawBg();

      }
    };

    var draw = function() {
      var barHeight
        , barData
        , barWidth = (fgCanvas.width / audioSource.bufferLength) * 2.5
        , x = 0;

      audioSource.analyser.getByteFrequencyData(audioSource.dataArray);

      fgCtx.clearRect(-fgCanvas.width, -fgCanvas.height, fgCanvas.width*2, fgCanvas.height *2);

      for (var i = 0, l = audioSource.bufferLength; i < l; i++) {
        barData = audioSource.dataArray[i];
        barHeight = barData * 3;

        fgCtx.fillStyle = 'rgba(' + (barData+200) + ', '+ (barData+200)+',' + (barData+200) + ', 0.4'+')';
        fgCtx.fillRect(x, fgCanvas.height-barHeight/2 + 100, barWidth, barHeight/100);

        fgCtx.fillRect(x, fgCanvas.height-barHeight/2, barWidth, barHeight/2);
        fgCtx.beginPath();
        fgCtx.arc(x, fgCanvas.height-barHeight/2 - 90, barWidth /3 , 0, 2 * Math.PI, false);

        fgCtx.fill();

        x += barWidth + 1;
      }

      requestAnimationFrame(draw);
    };

    this.drawAlbumImg = function() {
      albumImg.setAttribute('src', soundCloud.artwork_url);

    };
    this.clearBackEffect = function() {
      clearInterval(drawBg);
    };

    this.init = function(option) {

      audioSource = option.audioSource;
      canvas = document.querySelector(option.visualPanel);

      fgCanvas = document.createElement('canvas');
      fgCanvas.setAttribute('style', 'position: absolute; z-index: 10');
      fgCtx = fgCanvas.getContext("2d");
      canvas.appendChild(fgCanvas);

      albumImg = document.createElement('img');
      albumImg.setAttribute('style', 'position: absolute; z-index: 20; top: 10px; right: 10px; opacity: 0.5');
      canvas.appendChild(albumImg);

      bgCanvas = document.createElement('canvas');
      bgCtx = bgCanvas.getContext("2d");
      canvas.appendChild(bgCanvas);

      this.resizeCanvas();
      draw();
      setInterval(drawBg, 1000 / 2);
      window.addEventListener('resize', this.resizeCanvas, false);
    }

  };

  var play = function(trackurl) {
    soundCloud.loadSoundCloud(trackurl,
      function() {
        audioSource.playStream(soundCloud.streamUrl);
        visualPanel.style.display = 'block';
        visualizer.drawAlbumImg();
        setTimeout(ctrGroup.toggle, 3000); // auto-hide the control panel
      },
      function(error) {
        console.log(error);
      });
    };

    var audio = document.querySelector('.ctrgroup__player__audio')
      , form = document.querySelector('.ctrgroup__player__form')
      , toggleButton = document.querySelector('.ctrgroup__togglebtn')
      , visualPanel = document.querySelector('.visualPanel')
      , defaulPanel = document.querySelector('.defaulPanel');

    var soundCloud = new SoundCloudSetter(audio)
      , audioSource = new SoundCloudAudioSource(audio)
      , ctrGroup = new ControlGroup()
      , visualizer = new Visualizer();

    visualizer.init({
      visualPanel : '.visualPanel',
      audioSource : audioSource
    });

    ctrGroup.toggle();
    visualPanel.style.display = 'none';

    form.addEventListener('submit', function(e) {
      e.preventDefault();
      defaulPanel.style.display = 'none';
      var trackUrl = document.querySelector('.ctrgroup__player__form__input').value;
      play(trackUrl);
    });

    toggleButton.addEventListener('click', function(e) {
      e.preventDefault();
      ctrGroup.toggle();
    });

    audio.addEventListener("ended", function(){
      visualPanel.style.display = 'none';
    });

  }());
<commit_msg>play blank mp3 before waiting for promise<commit_after>(function() {
  'use strict';

  var SoundCloudSetter = function(audio) {
    var self = this;
    var client_id = SOUNDCLOUD_API_KEY;

    self.streamUrl = "";
    self.artwork_url = "";
    self.audio = audio;

    this.loadSoundCloud = function(track_url, successCallback, errorCallback) {

      SC.initialize({
        client_id: client_id
      });

      SC.resolve(track_url)
        .then(function(data){
        self.streamUrl = data.stream_url + '?client_id=' + client_id;
        self.artwork_url = data.artwork_url;
        successCallback();

      }).catch(function(error){

        errorCallback(error);
      });
    };

    this.directLoadSoundCloud = function(direction) {

      if (this.audio.paused) {
        this.audio.play();
      } else {
        this.audio.pause();
      }
    }
  };

  var SoundCloudAudioSource = function(audio){
    var self = this
      , audioCtx = new (window.AudioContext || window.webkitAudioContext)
      , source = audioCtx.createMediaElementSource(audio);

    self.analyser = audioCtx.createAnalyser();
    self.analyser.fftSize = 256;
    audio.crossOrigin = "anonymous";
    source.connect(self.analyser);
    self.analyser.connect(audioCtx.destination);

    this.bufferLength = self.analyser.frequencyBinCount;

    this.dataArray = new Uint8Array(this.bufferLength);

    this.playStream = function(streamUrl) {
      audio.setAttribute('src', streamUrl);
      audio.play();
    }

  };

  var ControlGroup = function() {

    var ctrGroup = document.querySelector('.ctrgroup');

    this.toggle = function() {

      if (ctrGroup.className.indexOf('ctrgroup--hidden') === 9) {
        ctrGroup.className = ctrGroup.className.split(' ')[0];
      } else {
        ctrGroup.className = ctrGroup.className + ' ctrgroup--hidden';
      }

    };

  };

  var Visualizer = function() {
    var fgCanvas
      , fgCtx
      , bgCanvas
      , bgCtx
      , albumImg
      , canvas
      , audioSource
      , gradientColor = {
          0: ['#89FFFD', '#EF32D9'],
          1: ['#00DBDE', '#FC00FF'],
          2: ['#7BC6CC', '#BE93C5'],
          3 :['#E55D87', '#5FC3E4']
        };

    var drawBg = function(){
      bgCtx.clearRect(0, 0, bgCanvas.width, bgCanvas.height);
      bgCtx.beginPath();
      bgCtx.rect(0, 0, bgCanvas.width, bgCanvas.height);

      var r = Math.floor(Math.random() * (3 + 1))
        , gradient = bgCtx.createLinearGradient(0,0,1500,0);

      gradient.addColorStop(0, gradientColor[r][0]); // #00DBDE'rgb(0, 219, 222)'
      gradient.addColorStop(1, gradientColor[r][1]); // #fc00ff'rgb(252, 0, 255)'

      bgCtx.fillStyle = gradient;
      bgCtx.fill();
    };

    this.resizeCanvas = function() {
      if (fgCanvas) {

        // resize the foreground canvas
        fgCanvas.width = window.innerWidth;
        fgCanvas.height = window.innerHeight;

        // resize the bg canvas
        bgCanvas.width = window.innerWidth;
        bgCanvas.height = window.innerHeight;

        drawBg();

      }
    };

    var draw = function() {
      var barHeight
        , barData
        , barWidth = (fgCanvas.width / audioSource.bufferLength) * 2.5
        , x = 0;

      audioSource.analyser.getByteFrequencyData(audioSource.dataArray);

      fgCtx.clearRect(-fgCanvas.width, -fgCanvas.height, fgCanvas.width*2, fgCanvas.height *2);

      for (var i = 0, l = audioSource.bufferLength; i < l; i++) {
        barData = audioSource.dataArray[i];
        barHeight = barData * 3;

        fgCtx.fillStyle = 'rgba(' + (barData+200) + ', '+ (barData+200)+',' + (barData+200) + ', 0.4'+')';
        fgCtx.fillRect(x, fgCanvas.height-barHeight/2 + 100, barWidth, barHeight/100);

        fgCtx.fillRect(x, fgCanvas.height-barHeight/2, barWidth, barHeight/2);
        fgCtx.beginPath();
        fgCtx.arc(x, fgCanvas.height-barHeight/2 - 90, barWidth /3 , 0, 2 * Math.PI, false);

        fgCtx.fill();

        x += barWidth + 1;
      }

      requestAnimationFrame(draw);
    };

    this.drawAlbumImg = function() {
      albumImg.setAttribute('src', soundCloud.artwork_url);

    };
    this.clearBackEffect = function() {
      clearInterval(drawBg);
    };

    this.init = function(option) {

      audioSource = option.audioSource;
      canvas = document.querySelector(option.visualPanel);

      fgCanvas = document.createElement('canvas');
      fgCanvas.setAttribute('style', 'position: absolute; z-index: 10');
      fgCtx = fgCanvas.getContext("2d");
      canvas.appendChild(fgCanvas);

      albumImg = document.createElement('img');
      albumImg.setAttribute('style', 'position: absolute; z-index: 20; top: 10px; right: 10px; opacity: 0.5');
      canvas.appendChild(albumImg);

      bgCanvas = document.createElement('canvas');
      bgCtx = bgCanvas.getContext("2d");
      canvas.appendChild(bgCanvas);

      this.resizeCanvas();
      draw();
      setInterval(drawBg, 1000 / 2);
      window.addEventListener('resize', this.resizeCanvas, false);
    }

  };

  var play = function(trackurl) {
    audio.src = 'data:audio/mpeg;base64,/+MYxAAAAANIAUAAAASEEB/jwOFM/0MM/90b/+RhST//w4NFwOjf///PZu////9lns5GFDv//l9GlUIEEIAAAgIg8Ir/JGq3/+MYxDsLIj5QMYcoAP0dv9HIjUcH//yYSg+CIbkGP//8w0bLVjUP///3Z0x5QCAv/yLjwtGKTEFNRTMuOTeqqqqqqqqqqqqq/+MYxEkNmdJkUYc4AKqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqq';
    audio.play();
    soundCloud.loadSoundCloud(trackurl,
      function() {
        audioSource.playStream(soundCloud.streamUrl);
        visualPanel.style.display = 'block';
        visualizer.drawAlbumImg();
        setTimeout(ctrGroup.toggle, 3000); // auto-hide the control panel
      },
      function(error) {
        console.log(error);
      });
    };

    var audio = document.querySelector('.ctrgroup__player__audio')
      , form = document.querySelector('.ctrgroup__player__form')
      , toggleButton = document.querySelector('.ctrgroup__togglebtn')
      , visualPanel = document.querySelector('.visualPanel')
      , defaulPanel = document.querySelector('.defaulPanel');

    var soundCloud = new SoundCloudSetter(audio)
      , audioSource = new SoundCloudAudioSource(audio)
      , ctrGroup = new ControlGroup()
      , visualizer = new Visualizer();

    visualizer.init({
      visualPanel : '.visualPanel',
      audioSource : audioSource
    });

    ctrGroup.toggle();
    visualPanel.style.display = 'none';

    form.addEventListener('submit', function(e) {
      e.preventDefault();
      defaulPanel.style.display = 'none';
      var trackUrl = document.querySelector('.ctrgroup__player__form__input').value;
      play(trackUrl);
    });

    toggleButton.addEventListener('click', function(e) {
      e.preventDefault();
      ctrGroup.toggle();
    });

    audio.addEventListener("ended", function(){
      visualPanel.style.display = 'none';
    });

  }());
<|endoftext|>


<commit_before>'use strict';

function logError(gutil, msg) {
    gutil.log(gutil.colors.red(msg));
}

function logWarning(gutil, msg) {
    gutil.log(gutil.colors.yellow(msg));
}

function logInfo(gutil, msg) {
    gutil.log(msg);
}

function logInfoHighlighted(gutil, msg) {
    gutil.log(gutil.colors.cyan(msg));
}

function logSuccess(gutil, msg) {
    gutil.log(gutil.colors.green(msg));
}

function logDebug(gutil, msg) {
    gutil.log(gutil.colors.grey(msg));
}

function urlBldr(url) {
	return {
	  build: function() {
	    return url;
	  },
	  add: function(val) {
	    return urlBldr(url + val);
	  },
	  addIf: function(bool, val) {
	    return bool ? this.add(val) : urlBldr(url);
	  },
	  query: function(nm, val) {
	      return url.indexOf('?') > -1 ? this.add('&' + nm + '=' + val) : this.add('?' + nm + '=' + val);
	  },
	  queryIf: function(bool, nm, val) {
	    return bool ? this.query(nm, val) : urlBldr(url);
	  }
	};
}

function htmlDecode(str) {
	return String(str).replace(/&/g, '&')
		.replace(/</g, '<').replace(/>/g, '>')
		.replace(/"/g, '"').replace(/#/g, '#')
		.replace(/(/g, '(').replace(/)/g, ')');
}

function logRequestError(gutil, action, err) {
    logError(gutil, 'Error making request to ' + action + ' plugin.');
    if (err.message.toLowerCase().indexOf("ssl")) {
        logError(gutil, 'Possible SSL configuration issue.');
        logError(gutil, 'Check to make sure you have the correct serverUrl and pluginUploadProtocol' +
        ' properties set in server.conf.json and that SSL support has been configured correctly on your community.');
    }

    logError(gutil, err);
}

function logErrorResponseStatusCode(gutil, res) {
    logError(gutil, 'server returned status code ' + res.statusCode);
    if (403 === res.statusCode) {
        logError(gutil, 'Either request is not being made over SSL or SSL is not configured property on the community.');
        logError(gutil, 'Check server.conf.json to make sure pluginUploadProtocol is set to https.');
    } else if (400 === res.statusCode) {
        logError(gutil, 'Check to make sure the protocol in the url set as the value of the serverUrl property '
        + 'matches set as the value of the pluginUploadProtocol property (in server.conf.json)');
    }
}

function logHardFailures(serviceResponse, gutil, scriptName) {
	if (serviceResponse.hasOwnProperty('hard-failures')) {
		var errorMessage = 'Error: [' + serviceResponse.status + '] ' + serviceResponse.message;
		var hardFailures = serviceResponse['hard-failures'];

		if (!Array.isArray(hardFailures)) {
			hardFailures = new Array(hardFailures);
		}

		hardFailures.forEach(function(hardFailure) {
			errorMessage = errorMessage + '\n' +
			'Failure Code: ' + hardFailure.code + '\n' +
			'Failure Rule: ' + hardFailure.ruleFailed + '\n' +
			'Failure Message: ' + htmlDecode(hardFailure.message) + '\n' +
			'Failure Details: ' + htmlDecode(hardFailure.details) + '\n';
			var errorSuggestion = hardFailure.suggestion;
			if (typeof errorSuggestion === 'string') {
				errorMessage += 'Suggestion: ' + errorSuggestion + '\n';
			}
		});

		var pluginError = new gutil.PluginError(scriptName, gutil.colors.red(errorMessage), { showStack: false });
		logWarning(gutil, pluginError);
	}
}

function logSoftFailures(serviceResponse, gutil) {
	if (serviceResponse.hasOwnProperty('soft-failures')) {
		var warnMessage = 'Warning: [' + serviceResponse.status + '] ' + serviceResponse.message;
		var softFailures = serviceResponse['soft-failures'];

		if (!Array.isArray(softFailures)) {
			softFailures = new Array(softFailures);
		}

		softFailures.forEach(function(softFailure) {
			warnMessage += '\n' +
			'Warn Code: ' + softFailure.code + '\n' +
			'Warn Rule: ' + softFailure.ruleFailed + '\n' +
			'Warn Message: ' + htmlDecode(softFailure.message) + '\n' +
			'Warn Details: ' + htmlDecode(softFailure.details) + '\n';
			var warnSuggestion = softFailure.suggestion;
			if (typeof warnSuggestion === 'string') {
				warnMessage += 'Suggestion: ' + warnSuggestion + '\n';
			}
		});

        logWarning(gutil, warnMessage);
	}
}

module.exports = {
    urlBldr: urlBldr,
    logError: logError,
    logWarning: logWarning,
    logInfo: logInfo,
    logInfoHighlighted: logInfoHighlighted,
    logSuccess: logSuccess,
    logDebug: logDebug,
    logRequestError: logRequestError,
    logErrorResponseStatusCode: logErrorResponseStatusCode,
    logHardFailures: logHardFailures,
    logSoftFailures: logSoftFailures
};<commit_msg>LIA-43668 log status and message even if no hard validation failures if there is a failure message<commit_after>'use strict';

function logError(gutil, msg) {
    gutil.log(gutil.colors.red(msg));
}

function logWarning(gutil, msg) {
    gutil.log(gutil.colors.yellow(msg));
}

function logInfo(gutil, msg) {
    gutil.log(msg);
}

function logInfoHighlighted(gutil, msg) {
    gutil.log(gutil.colors.cyan(msg));
}

function logSuccess(gutil, msg) {
    gutil.log(gutil.colors.green(msg));
}

function logDebug(gutil, msg) {
    gutil.log(gutil.colors.grey(msg));
}

function urlBldr(url) {
	return {
	  build: function() {
	    return url;
	  },
	  add: function(val) {
	    return urlBldr(url + val);
	  },
	  addIf: function(bool, val) {
	    return bool ? this.add(val) : urlBldr(url);
	  },
	  query: function(nm, val) {
	      return url.indexOf('?') > -1 ? this.add('&' + nm + '=' + val) : this.add('?' + nm + '=' + val);
	  },
	  queryIf: function(bool, nm, val) {
	    return bool ? this.query(nm, val) : urlBldr(url);
	  }
	};
}

function htmlDecode(str) {
	return String(str).replace(/&/g, '&')
		.replace(/</g, '<').replace(/>/g, '>')
		.replace(/"/g, '"').replace(/#/g, '#')
		.replace(/(/g, '(').replace(/)/g, ')');
}

function logRequestError(gutil, action, err) {
    logError(gutil, 'Error making request to ' + action + ' plugin.');
    if (err.message.toLowerCase().indexOf("ssl")) {
        logError(gutil, 'Possible SSL configuration issue.');
        logError(gutil, 'Check to make sure you have the correct serverUrl and pluginUploadProtocol' +
        ' properties set in server.conf.json and that SSL support has been configured correctly on your community.');
    }

    logError(gutil, err);
}

function logErrorResponseStatusCode(gutil, res) {
    logError(gutil, 'server returned status code ' + res.statusCode);
    if (403 === res.statusCode) {
        logError(gutil, 'Either request is not being made over SSL or SSL is not configured property on the community.');
        logError(gutil, 'Check server.conf.json to make sure pluginUploadProtocol is set to https.');
    } else if (400 === res.statusCode) {
        logError(gutil, 'Check to make sure the protocol in the url set as the value of the serverUrl property '
        + 'matches set as the value of the pluginUploadProtocol property (in server.conf.json)');
    }
}

function logHardFailures(serviceResponse, gutil, scriptName) {
    if (serviceResponse.hasOwnProperty('status')) {
        if (serviceResponse.status.indexOf('_FAIL') > -1) {
            var errorMessage = 'Error: [' + serviceResponse.status + '] ' + htmlDecode(serviceResponse.message);

            if (serviceResponse.hasOwnProperty('hard-failures')) {
                var hardFailures = serviceResponse['hard-failures'];

                if (!Array.isArray(hardFailures)) {
                    hardFailures = new Array(hardFailures);
                }

                hardFailures.forEach(function (hardFailure) {
                    errorMessage = errorMessage + '\n' +
                    'Failure Code: ' + hardFailure.code + '\n' +
                    'Failure Rule: ' + hardFailure.ruleFailed + '\n' +
                    'Failure Message: ' + htmlDecode(hardFailure.message) + '\n' +
                    'Failure Details: ' + htmlDecode(hardFailure.details) + '\n';
                    var errorSuggestion = hardFailure.suggestion;
                    if (typeof errorSuggestion === 'string') {
                        errorMessage += 'Suggestion: ' + errorSuggestion + '\n';
                    }
                });
            }

            var pluginError = new gutil.PluginError(scriptName, gutil.colors.red(errorMessage), {showStack: false});
            logWarning(gutil, pluginError);
        }
    }
 }

function logSoftFailures(serviceResponse, gutil) {
	if (serviceResponse.hasOwnProperty('soft-failures')) {
		var warnMessage = 'Warning: [' + serviceResponse.status + '] ' + serviceResponse.message;
		var softFailures = serviceResponse['soft-failures'];

		if (!Array.isArray(softFailures)) {
			softFailures = new Array(softFailures);
		}

		softFailures.forEach(function(softFailure) {
			warnMessage += '\n' +
			'Warn Code: ' + softFailure.code + '\n' +
			'Warn Rule: ' + softFailure.ruleFailed + '\n' +
			'Warn Message: ' + htmlDecode(softFailure.message) + '\n' +
			'Warn Details: ' + htmlDecode(softFailure.details) + '\n';
			var warnSuggestion = softFailure.suggestion;
			if (typeof warnSuggestion === 'string') {
				warnMessage += 'Suggestion: ' + warnSuggestion + '\n';
			}
		});

        logWarning(gutil, warnMessage);
	}
}

module.exports = {
    urlBldr: urlBldr,
    logError: logError,
    logWarning: logWarning,
    logInfo: logInfo,
    logInfoHighlighted: logInfoHighlighted,
    logSuccess: logSuccess,
    logDebug: logDebug,
    logRequestError: logRequestError,
    logErrorResponseStatusCode: logErrorResponseStatusCode,
    logHardFailures: logHardFailures,
    logSoftFailures: logSoftFailures
};<|endoftext|>


<commit_before>//
// Sidebar
//

var Sidebar = function() {
  this.selector = '#sidebar';
};

Sidebar.prototype.refresh = function(context, revision) {
  this.show();
  var self = this;
  context.load('revs.json', {cache: false}).
          render('revision_sidebar.mustache').
          replace(this.selector).then(function() {
            var rows = $("#sidebar tr");
            rows.click(function() {
              context.redirect($(this).find("a").attr("href"));
            })

            if(revision) {
              Sammy.log("selected revision:" + revision);
              self.rowForRevision(revision).addClass("active");
            }
          });
};

Sidebar.prototype.rowForRevision = function(revision) {
  return $(this.selector + " tr[data-revision=" + revision + "]");
};

Sidebar.prototype.hide = function() {
  $("#menu-item-builds").removeClass('active');
  $(this.selector).hide();
};

Sidebar.prototype.show = function() {
  $("#menu-item-builds").addClass('active');

  var e = $(this.selector);
  if (!e.is(":visible"))
    e.show();
};

//
// Builds
//

var Builds = function() {
  this.selector = '#content';

};
Builds.currentView = 'list';

Builds.prototype.load = function(context, params) {
  this.show();

  context.load("/builds/" + params.revision + ".json")
      .render("builds.mustache")
      .replace(this.selector)
      .then(function() {
        $("#list").tablesorter();

        if(params.view == "list") {
          Builds.currentView = "list";

          $("#matrix-tab").removeClass("active")
          $("#list-tab").addClass("active")

          $("#matrix").hide();
          $("#list").show();
        } else {
          Builds.currentView = "matrix";

          $("#list-tab").removeClass("active")
          $("#matrix-tab").addClass("active")

          $("#list").hide();
          $("#matrix").show();
        }

      });
};

Builds.prototype.hide = function() {
  $(this.selector).hide();
  $("#menu-item-builds").removeClass('active');
};

Builds.prototype.show = function() {
  $(this.selector).show();
};

var Graphs = function() {
  this.selector = '#graphs';
};

Graphs.prototype.load = function(context) {
  this.show();

  var self = this;
  context.load('/graphs.json').then(function(data) {
    self.renderGraph("#area-graph",data);
  });
};

Graphs.prototype.hide = function() {
  $("#menu-item-graphs").removeClass('active');
  $(this.selector).hide();
};

Graphs.prototype.show = function() {
  $("#menu-item-graphs").addClass('active');
  $(this.selector).show();
};

Graphs.prototype.renderGraph = function(selector, data) {
  this.chart = new Highcharts.Chart({
    chart: {
        renderTo: document.querySelector(selector),
        type: 'areaspline'
    },
    title: {
        text: 'Builds'
    },
    credits: { enabled: false },
    subtitle: {
        text: ''
    },
    xAxis: {
        categories: data.categories,
        tickmarkPlacement: 'off',
        title: {
            enabled: false
        },
        labels: {
          formatter: function() { return 'r' + this.value + ''; }
        }

    },
    yAxis: {
        title: {
            text: 'Count'
        }
    },
    tooltip: {
        formatter: function() {
          // console.log(this);
            return 'r'+
                this.x +': '+ Highcharts.numberFormat(this.y, 0, ',') + ' build' + (this.y != 1 ? 's' : '');
        }
    },
    plotOptions: {
        area: {
            stacking: 'normal',
            lineColor: '#666666',
            lineWidth: 1,
            marker: {
                lineWidth: 1,
                lineColor: '#666666'
            }
        }
    },
    series: data.series
  });
};

var app = Sammy('#main', function() {
  this.use('Mustache');

  this.pages = {};
  this.pages.sidebar = new Sidebar();
  this.pages.builds = new Builds();
  this.pages.graphs = new Graphs();

  $(document).ajaxStart(function() {
    $("#loading").show();
  });

  $(document).ajaxStop(function() {
    $("#loading").hide();
  });

  this.get('#/', function(context) {
    app.pages.graphs.hide();
    app.pages.builds.show();
    app.pages.sidebar.refresh(this);
  });

  this.get('#/revision/:revision/:view', function(context) {
    app.pages.sidebar.refresh(this, this.params.revision);
    app.pages.builds.load(this, this.params)
  });

  this.get('#/revision/:revision', function(context) {
    this.redirect("#/revision/" + this.params.revision + "/" + Builds.currentView)
  });

  this.get('#/graphs', function(context) {
    app.pages.sidebar.hide();
    app.pages.builds.hide();
    app.pages.graphs.load(this);
  })
});

$(document).ready(function() {
  app.run('#/');
});
<commit_msg>areaspline -> area<commit_after>//
// Sidebar
//

var Sidebar = function() {
  this.selector = '#sidebar';
};

Sidebar.prototype.refresh = function(context, revision) {
  this.show();
  var self = this;
  context.load('revs.json', {cache: false}).
          render('revision_sidebar.mustache').
          replace(this.selector).then(function() {
            var rows = $("#sidebar tr");
            rows.click(function() {
              context.redirect($(this).find("a").attr("href"));
            })

            if(revision) {
              Sammy.log("selected revision:" + revision);
              self.rowForRevision(revision).addClass("active");
            }
          });
};

Sidebar.prototype.rowForRevision = function(revision) {
  return $(this.selector + " tr[data-revision=" + revision + "]");
};

Sidebar.prototype.hide = function() {
  $("#menu-item-builds").removeClass('active');
  $(this.selector).hide();
};

Sidebar.prototype.show = function() {
  $("#menu-item-builds").addClass('active');

  var e = $(this.selector);
  if (!e.is(":visible"))
    e.show();
};

//
// Builds
//

var Builds = function() {
  this.selector = '#content';

};
Builds.currentView = 'list';

Builds.prototype.load = function(context, params) {
  this.show();

  context.load("/builds/" + params.revision + ".json")
      .render("builds.mustache")
      .replace(this.selector)
      .then(function() {
        $("#list").tablesorter();

        if(params.view == "list") {
          Builds.currentView = "list";

          $("#matrix-tab").removeClass("active")
          $("#list-tab").addClass("active")

          $("#matrix").hide();
          $("#list").show();
        } else {
          Builds.currentView = "matrix";

          $("#list-tab").removeClass("active")
          $("#matrix-tab").addClass("active")

          $("#list").hide();
          $("#matrix").show();
        }

      });
};

Builds.prototype.hide = function() {
  $(this.selector).hide();
  $("#menu-item-builds").removeClass('active');
};

Builds.prototype.show = function() {
  $(this.selector).show();
};

var Graphs = function() {
  this.selector = '#graphs';
};

Graphs.prototype.load = function(context) {
  this.show();

  var self = this;
  context.load('/graphs.json').then(function(data) {
    self.renderGraph("#area-graph",data);
  });
};

Graphs.prototype.hide = function() {
  $("#menu-item-graphs").removeClass('active');
  $(this.selector).hide();
};

Graphs.prototype.show = function() {
  $("#menu-item-graphs").addClass('active');
  $(this.selector).show();
};

Graphs.prototype.renderGraph = function(selector, data) {
  this.chart = new Highcharts.Chart({
    chart: {
        renderTo: document.querySelector(selector),
        type: 'area'
    },
    title: {
        text: 'Builds'
    },
    credits: { enabled: false },
    subtitle: {
        text: ''
    },
    xAxis: {
        categories: data.categories,
        tickmarkPlacement: 'off',
        title: {
            enabled: false
        },
        labels: {
          formatter: function() { return 'r' + this.value + ''; }
        }

    },
    yAxis: {
        title: {
            text: 'Count'
        }
    },
    tooltip: {
        formatter: function() {
          // console.log(this);
            return 'r'+
                this.x +': '+ Highcharts.numberFormat(this.y, 0, ',') + ' build' + (this.y != 1 ? 's' : '');
        }
    },
    plotOptions: {
        area: {
            stacking: 'normal',
            lineColor: '#666666',
            lineWidth: 1,
            marker: {
                lineWidth: 1,
                lineColor: '#666666'
            }
        }
    },
    series: data.series
  });
};

var app = Sammy('#main', function() {
  this.use('Mustache');

  this.pages = {};
  this.pages.sidebar = new Sidebar();
  this.pages.builds = new Builds();
  this.pages.graphs = new Graphs();

  $(document).ajaxStart(function() {
    $("#loading").show();
  });

  $(document).ajaxStop(function() {
    $("#loading").hide();
  });

  this.get('#/', function(context) {
    app.pages.graphs.hide();
    app.pages.builds.show();
    app.pages.sidebar.refresh(this);
  });

  this.get('#/revision/:revision/:view', function(context) {
    app.pages.sidebar.refresh(this, this.params.revision);
    app.pages.builds.load(this, this.params)
  });

  this.get('#/revision/:revision', function(context) {
    this.redirect("#/revision/" + this.params.revision + "/" + Builds.currentView)
  });

  this.get('#/graphs', function(context) {
    app.pages.sidebar.hide();
    app.pages.builds.hide();
    app.pages.graphs.load(this);
  })
});

$(document).ready(function() {
  app.run('#/');
});
<|endoftext|>


<commit_before>const PROTO_VER_1_10 = require('minecraft-data')('1.10.2').version.version

module.exports = inject

function inject (bot, options) {
  const CHAT_LENGTH_LIMIT = options.chatLengthLimit || ((bot.protocolVersion > PROTO_VER_1_10) ? 256 : 100)
  const USERNAME_REGEX = '(?:\\(.+\\)|\\[.+\\]|.)*?(\\w+)'

  const ChatMessage = require('prismarine-chat')(bot.version)
  // add an array, containing objects such as {pattern:/regex pattern/, chatType:"string", description:"string"}
  // chat.pattern.type will emit an event for bot.on() of the same type, eg chatType = whisper will trigger bot.on('whisper')
  bot.chatPatterns = [
    {
      pattern: new RegExp(`^${USERNAME_REGEX} whispers(?: to you)?:? (.*)$`),
      type: 'whisper',
      description: 'Vanilla whisper'
    },
    {
      pattern: new RegExp(`^\\[${USERNAME_REGEX} -> \\w+\\s?\\] (.*)$`),
      type: 'whisper',
      description: 'Essentials whisper'
    },
    {
      pattern: new RegExp(`^${USERNAME_REGEX}\\s?[>:\\-»\\]\\)~]+\\s(.*)$`),
      type: 'chat',
      description: 'Universal chat'
    }
  ]

  bot.chatAddPattern = (patternValue, typeValue, descriptionValue) => {
    // description is not required but recommended
    if (typeof descriptionValue === 'undefined') {
      descriptionValue = 'None'
    }
    bot.chatPatterns.push({
      pattern: patternValue,
      type: typeValue,
      description: descriptionValue
    })
  }

  bot._client.on('chat', (packet) => {
    function checkForChatPatterns (msg) {
      const stringMsg = msg.toString()
      let matchAny = false
      // iterate through each object in chat.patterns array and test if .pattern matches
      for (const { pattern, type } of bot.chatPatterns) {
        // Chat pattern matches server messages so drop them
        if (stringMsg.startsWith('[Server:')) break

        const match = stringMsg.match(pattern)
        if (match) {
          matchAny = true
          bot.emit(type, ...match.slice(1), msg.translate, msg)
        }
      }

      if (matchAny === false) {
        bot.emit('unmachedMessage', stringMsg, msg)
      }
    }

    var msg
    try {
      msg = new ChatMessage(JSON.parse(packet.message))
    } catch (e) {
      msg = new ChatMessage(packet.message)
    }
    bot.emit('message', msg)
    checkForChatPatterns(msg)

    // Position 2 is the action bar
    if (packet.position === 2) bot.emit('actionBar', msg)
  })

  function chatWithHeader (header, message) {
    const lengthLimit = CHAT_LENGTH_LIMIT - header.length
    message.split('\n').forEach((subMessage) => {
      if (!subMessage) return
      let i
      let smallMsg
      for (i = 0; i < subMessage.length; i += lengthLimit) {
        smallMsg = header + subMessage.substring(i, i + lengthLimit)
        bot._client.write('chat', { message: smallMsg })
      }
    })
  }

  function tabComplete (text, cb, assumeCommand = false, sendBlockInSight = true) {
    let position

    if (sendBlockInSight) {
      const block = bot.blockInSight()

      if (block) {
        position = block.position
      }
    }

    bot._client.once('tab_complete', (packet) => {
      cb(undefined, packet.matches)
    })

    bot._client.write('tab_complete', {
      text,
      assumeCommand,
      lookedAtBlock: position
    })
  }

  bot.whisper = (username, message) => {
    chatWithHeader(`/tell ${username} `, message)
  }
  bot.chat = (message) => {
    chatWithHeader('', message)
  }

  bot.tabComplete = tabComplete
}
<commit_msg>Add type validation to bot.chat (#1369)<commit_after>const PROTO_VER_1_10 = require('minecraft-data')('1.10.2').version.version

module.exports = inject

function inject (bot, options) {
  const CHAT_LENGTH_LIMIT = options.chatLengthLimit || ((bot.protocolVersion > PROTO_VER_1_10) ? 256 : 100)
  const USERNAME_REGEX = '(?:\\(.+\\)|\\[.+\\]|.)*?(\\w+)'

  const ChatMessage = require('prismarine-chat')(bot.version)
  // add an array, containing objects such as {pattern:/regex pattern/, chatType:"string", description:"string"}
  // chat.pattern.type will emit an event for bot.on() of the same type, eg chatType = whisper will trigger bot.on('whisper')
  bot.chatPatterns = [
    {
      pattern: new RegExp(`^${USERNAME_REGEX} whispers(?: to you)?:? (.*)$`),
      type: 'whisper',
      description: 'Vanilla whisper'
    },
    {
      pattern: new RegExp(`^\\[${USERNAME_REGEX} -> \\w+\\s?\\] (.*)$`),
      type: 'whisper',
      description: 'Essentials whisper'
    },
    {
      pattern: new RegExp(`^${USERNAME_REGEX}\\s?[>:\\-»\\]\\)~]+\\s(.*)$`),
      type: 'chat',
      description: 'Universal chat'
    }
  ]

  bot.chatAddPattern = (patternValue, typeValue, descriptionValue) => {
    // description is not required but recommended
    if (typeof descriptionValue === 'undefined') {
      descriptionValue = 'None'
    }
    bot.chatPatterns.push({
      pattern: patternValue,
      type: typeValue,
      description: descriptionValue
    })
  }

  bot._client.on('chat', (packet) => {
    function checkForChatPatterns (msg) {
      const stringMsg = msg.toString()
      let matchAny = false
      // iterate through each object in chat.patterns array and test if .pattern matches
      for (const { pattern, type } of bot.chatPatterns) {
        // Chat pattern matches server messages so drop them
        if (stringMsg.startsWith('[Server:')) break

        const match = stringMsg.match(pattern)
        if (match) {
          matchAny = true
          bot.emit(type, ...match.slice(1), msg.translate, msg)
        }
      }

      if (matchAny === false) {
        bot.emit('unmachedMessage', stringMsg, msg)
      }
    }

    var msg
    try {
      msg = new ChatMessage(JSON.parse(packet.message))
    } catch (e) {
      msg = new ChatMessage(packet.message)
    }
    bot.emit('message', msg)
    checkForChatPatterns(msg)

    // Position 2 is the action bar
    if (packet.position === 2) bot.emit('actionBar', msg)
  })

  function chatWithHeader (header, message) {
    if (typeof message !== 'string') {
      throw new Error('Incorrect type! Should be a string.')
    }

    const lengthLimit = CHAT_LENGTH_LIMIT - header.length
    message.split('\n').forEach((subMessage) => {
      if (!subMessage) return
      let i
      let smallMsg
      for (i = 0; i < subMessage.length; i += lengthLimit) {
        smallMsg = header + subMessage.substring(i, i + lengthLimit)
        bot._client.write('chat', { message: smallMsg })
      }
    })
  }

  function tabComplete (text, cb, assumeCommand = false, sendBlockInSight = true) {
    let position

    if (sendBlockInSight) {
      const block = bot.blockInSight()

      if (block) {
        position = block.position
      }
    }

    bot._client.once('tab_complete', (packet) => {
      cb(undefined, packet.matches)
    })

    bot._client.write('tab_complete', {
      text,
      assumeCommand,
      lookedAtBlock: position
    })
  }

  bot.whisper = (username, message) => {
    chatWithHeader(`/tell ${username} `, message)
  }
  bot.chat = (message) => {
    chatWithHeader('', message)
  }

  bot.tabComplete = tabComplete
}
<|endoftext|>


<commit_before>/**
 * Sheet object, represent each element as single sheet
 * @param  {string}     identifier :unique key for accessing sheet object internally
 * @param  {domElement} element    :dom element as scope for sheet to work with
 * @param  {object}     config     : configuration object
 * @return {void}
 */
function sheet(identifier, element, config){
    this.identifier   = identifier;
    this.el           = $(element);
    this.lang         = 'en';
    this.cells        = {};
    this.variables    = {};
    this.config       = $.extend({}, defaultConfig, config);
    this.counter      = 1;
    this.relatedSheet = false;
    this.elementId    = this.el.attr('id');
    this.dependant    = {};
    this.dependencies = {};
    this.calculated   = false;
    this.calculating  = false;
    this.activeCell   = null;
    this.totalCell    = 0;
    this.affectedCell = [];

    this.init();
};

sheet.fx = sheet.prototype;<commit_msg>add docblock to sheet variable<commit_after>/**
 * Sheet object, represent each element as single sheet
 * @param  {string}     identifier :unique key for accessing sheet object internally
 * @param  {domElement} element    :dom element as scope for sheet to work with
 * @param  {object}     config     : configuration object
 * @return {void}
 */
function sheet(identifier, element, config){
    /** @type {string} The sheet unique identifier */
    this.identifier   = identifier;

    /** @type {object} jQuery dom element */
    this.el           = $(element);

    /** @type {string} Default language setting */
    this.lang         = 'en';

    /** @type {object} Cells registry containing all available cell */
    this.cells        = {};

    /** @type {object} Variable registry, containing all defined variable */
    this.variables    = {};

    /** @type {object} Sheet configuration object */
    this.config       = $.extend({}, defaultConfig, config);

    /** @type {number} Cell counter, count total cell in the sheet */
    this.counter      = 1;

    /** @type {boolean} Indicate if the sheet has relation with other sheet */
    this.relatedSheet = false;

    /** @type {string} The id of the element where the sheet is initialized and bound */
    this.elementId    = this.el.attr('id');

    /** @type {object} Registry containing sheets that depend on this sheet */
    this.dependant    = {};

    /** @type {object} Registry containing sheets that this sheet depend on*/
    this.dependencies = {};

    /** @type {boolean} Indicate the current sheet calculation is done */
    this.calculated   = false;

    /** @type {boolean} Indicate the current sheet calculation is in progress */
    this.calculating  = false;

    /** @type {object} Current cell object being calculated */
    this.activeCell   = null;
    this.totalCell    = 0;
    this.affectedCell = [];

    this.init();
};

sheet.fx = sheet.prototype;<|endoftext|>


<commit_before>'use strict';
/**
 * JS
 */

angular.module('jukebox', [
  'ngRoute',
  'ngResource',
  'ngSanitize',
  'ngAnimate',
  'ngTouch',
  'mgcrea.ngStrap'
])

.config(['$routeProvider', '$locationProvider',
function ($routeProvider,   $locationProvider) {
  $locationProvider.hashPrefix('!');
  $routeProvider.
    when('/playing', {templateUrl: 'playing',   controller: 'PlayingCtrl'}).
    when('/history', {templateUrl: 'history',   controller: 'HistoryCtrl'}).
    otherwise({redirectTo: '/playing'});
}])
.config(['$alertProvider', function ($alertProvider) {
  angular.extend($alertProvider.defaults, {
    placement: 'top-right',
    type: 'info',
    container: 'body',
    template: 'alert-info',
    duration: 2,
  });
}])
.config(['$compileProvider', function ($compileProvider) {
  $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|javascript):/);
}]);
<commit_msg>Added: comment about the "unsafe" `javascript:` links for the bookmarklet<commit_after>'use strict';
/**
 * JS
 */

angular.module('jukebox', [
  'ngRoute',
  'ngResource',
  'ngSanitize',
  'ngAnimate',
  'ngTouch',
  'mgcrea.ngStrap'
])

.config(['$routeProvider', '$locationProvider',
function ($routeProvider,   $locationProvider) {
  $locationProvider.hashPrefix('!');
  $routeProvider.
    when('/playing', {templateUrl: 'playing',   controller: 'PlayingCtrl'}).
    when('/history', {templateUrl: 'history',   controller: 'HistoryCtrl'}).
    otherwise({redirectTo: '/playing'});
}])
.config(['$alertProvider', function ($alertProvider) {
  angular.extend($alertProvider.defaults, {
    placement: 'top-right',
    type: 'info',
    container: 'body',
    template: 'alert-info',
    duration: 2,
  });
}])
.config(['$compileProvider', function ($compileProvider) {
  /*
    This is required to allow `javascript:` links (bookmarklet).
    They are now disabled by default and appear as 'unsafe'.
    cf. http://stackoverflow.com/questions/15637133/unsafe-link-in-angular
  */
  $compileProvider.aHrefSanitizationWhitelist(/^\s*(https?|ftp|mailto|javascript):/);
}]);
<|endoftext|>


<commit_before>'use strict';

var _ = require('lodash');
var moment = require('moment');
var times = require('../times');

var ALL_STATUS_FIELDS = ['reservoir', 'battery', 'clock', 'status', 'device'];

function init (ctx) {
  var translate = ctx.language.translate;
  var timeago = require('./timeago')(ctx);
  var openaps = require('./openaps')(ctx);
  var levels = ctx.levels;

  var pump = {
    name: 'pump'
    , label: 'Pump'
    , pluginType: 'pill-status'
  };

  pump.getPrefs = function getPrefs (sbx) {

    function cleanList (value) {
      return decodeURIComponent(value || '').toLowerCase().split(' ');
    }

    function isEmpty (list) {
      return _.isEmpty(list) || _.isEmpty(list[0]);
    }

    var fields = cleanList(sbx.extendedSettings.fields);
    fields = isEmpty(fields) ? ['reservoir'] : fields;

    var retroFields = cleanList(sbx.extendedSettings.retroFields);
    retroFields = isEmpty(retroFields) ? ['reservoir', 'battery'] : retroFields;

    var profile = sbx.data.profile;
    var warnBattQuietNight = sbx.extendedSettings.warnBattQuietNight;

    if (warnBattQuietNight && (!profile || !profile.hasData() || !profile.getTimezone())) {
      console.warn('PUMP_WARN_BATT_QUIET_NIGHT requires a treatment profile with time zone set to obtain user time zone');
      warnBattQuietNight = false;
    }

    return {
      fields: fields
      , retroFields: retroFields
      , warnClock: sbx.extendedSettings.warnClock || 30
      , urgentClock: sbx.extendedSettings.urgentClock || 60
      , warnRes: sbx.extendedSettings.warnRes || 10
      , urgentRes: sbx.extendedSettings.urgentRes || 5
      , warnBattV: sbx.extendedSettings.warnBattV || 1.35
      , urgentBattV: sbx.extendedSettings.urgentBattV || 1.3
      , warnBattP: sbx.extendedSettings.warnBattP || 30
      , urgentBattP: sbx.extendedSettings.urgentBattP || 20
      , warnOnSuspend: sbx.extendedSettings.warnOnSuspend || false
      , enableAlerts: sbx.extendedSettings.enableAlerts || false
      , warnBattQuietNight: warnBattQuietNight || false
      , dayStart: sbx.settings.dayStart
      , dayEnd: sbx.settings.dayEnd
    };
  };

  pump.setProperties = function setProperties (sbx) {
    sbx.offerProperty('pump', function setPump ( ) {

      var prefs = pump.getPrefs(sbx);
      var recentMills = sbx.time - times.mins(prefs.urgentClock * 2).msecs;

      var filtered = _.filter(sbx.data.devicestatus, function (status) {
        return ('pump' in status) && sbx.entryMills(status) <= sbx.time && sbx.entryMills(status) >= recentMills;
      });

      var pumpStatus = null;

      _.forEach(filtered, function each (status) {
        status.clockMills = status.pump && status.pump.clock ? moment(status.pump.clock).valueOf() : status.mills;
        if (!pumpStatus || status.clockMills > pumpStatus.clockMills) {
          pumpStatus = status;
        }
      });

      pumpStatus = pumpStatus || { };
      pumpStatus.data = prepareData(pumpStatus, prefs, sbx);

      return pumpStatus;
    });
  };

  pump.checkNotifications = function checkNotifications (sbx) {
    var prefs = pump.getPrefs(sbx);

    if (!prefs.enableAlerts) { return; }

    pump.warnOnSuspend = prefs.warnOnSuspend;

    var data = prepareData(sbx.properties.pump, prefs, sbx);

    if (data.level >= levels.WARN) {
      sbx.notifications.requestNotify({
        level: data.level
        , title: data.title
        , message: data.message
        , pushoverSound: 'echo'
        , group: 'Pump'
        , plugin: pump
      });
    }
  };

  pump.updateVisualisation = function updateVisualisation (sbx) {
    var prop = sbx.properties.pump;

    var prefs = pump.getPrefs(sbx);
    var result = prepareData(prop, prefs, sbx);

    var values = [ ];
    var info = [ ];

    var selectedFields = sbx.data.inRetroMode ? prefs.retroFields : prefs.fields;

    _.forEach(ALL_STATUS_FIELDS, function eachField (fieldName) {
      var field = result[fieldName];
      if (field) {
        var selected = _.indexOf(selectedFields, fieldName) > -1;
        if (selected) {
          values.push(field.display);
        } else {
          info.push({label: field.label, value: field.display});
        }
      }
    });

    if (result.extended) {
      info.push({label: '------------', value: ''});
      _.forOwn(result.extended, function(value, key) {
         info.push({ label: key, value: value });
      });
    }

    sbx.pluginBase.updatePillText(pump, {
      value: values.join(' ')
      , info: info
      , label: translate('Pump')
      , pillClass: statusClass(result.level)
    });
  };

  function virtAsstReservoirHandler (next, slots, sbx) {
    var reservoir = _.get(sbx, 'properties.pump.pump.reservoir');
    if (reservoir || reservoir === 0) {
      var response = translate('virtAsstReservoir', {
        params: [
            reservoir
        ]
      });
      next(translate('virtAsstTitlePumpReservoir'), response);
    } else {
      next(translate('virtAsstTitlePumpReservoir'), translate('virtAsstUnknown'));
    }
  }

  function virtAsstBatteryHandler (next, slots, sbx) {
    var battery = _.get(sbx, 'properties.pump.data.battery');
    if (battery) {
      var response = translate('virtAsstPumpBattery', {
              params: [
                  battery.value,
                  battery.unit
              ]
          });
      next(translate('virtAsstTitlePumpBattery'), response);
    } else {
      next(translate('virtAsstTitlePumpBattery'), translate('virtAsstUnknown'));
    }
  }

  pump.virtAsst = {
    intentHandlers:[
      {
        // backwards compatibility
        intent: 'InsulinRemaining',
        intentHandler: virtAsstReservoirHandler
      }
      , {
        // backwards compatibility
        intent: 'PumpBattery',
        intentHandler: virtAsstBatteryHandler
      }
      , {
        intent: 'MetricNow'
        , metrics: ['pump reservoir']
        , intentHandler: virtAsstReservoirHandler
      }
      , {
        intent: 'MetricNow'
        , metrics: ['pump battery']
        , intentHandler: virtAsstBatteryHandler
      }
    ]
  };

  function statusClass (level) {
    var cls = 'current';

    if (level === levels.WARN) {
      cls = 'warn';
    } else if (level === levels.URGENT) {
      cls = 'urgent';
    }

    return cls;
  }

  function updateClock (prefs, result, sbx) {
    if (result.clock) {
      result.clock.label = 'Last Clock';
      result.clock.display = timeFormat(result.clock.value, sbx);

      var urgent = moment(sbx.time).subtract(prefs.urgentClock, 'minutes');
      var warn = moment(sbx.time).subtract(prefs.warnClock, 'minutes');

      if (urgent.isAfter(result.clock.value)) {
        result.clock.level = levels.URGENT;
        result.clock.message = 'URGENT: Pump data stale';
      } else if (warn.isAfter(result.clock.value)) {
        result.clock.level = levels.WARN;
        result.clock.message = 'Warning, Pump data stale';
      } else {
        result.clock.level = levels.NONE;
      }
    }
  }

  function updateReservoir (prefs, result) {
    if (result.reservoir) {
      result.reservoir.label = 'Reservoir';
      result.reservoir.display = result.reservoir.value.toPrecision(3) + 'U';
      if (result.reservoir.value < prefs.urgentRes) {
        result.reservoir.level = levels.URGENT;
        result.reservoir.message = 'URGENT: Pump Reservoir Low';
      } else if (result.reservoir.value < prefs.warnRes) {
        result.reservoir.level = levels.WARN;
        result.reservoir.message = 'Warning, Pump Reservoir Low';
      } else {
        result.reservoir.level = levels.NONE;
      }
    } else if (result.manufacturer === 'Insulet') {
      result.reservoir = {
        label: 'Reservoir', display: '50+ U'
      }
    }
    if (result.reservoir_display_override) {
      result.reservoir.display = result.reservoir_display_override;
    }
    if (result.reservoir_level_override) {
      result.reservoir.level = result.reservoir_level_override;
    }
  }

  function updateBattery (type, prefs, result, batteryWarn) {
    if (result.battery) {
      result.battery.label = 'Battery';
      result.battery.display = result.battery.value + type;
      var urgent = type === 'v' ? prefs.urgentBattV : prefs.urgentBattP;
      var warn = type === 'v' ? prefs.warnBattV : prefs.warnBattP;

      if (result.battery.value < urgent && batteryWarn) {
        result.battery.level = levels.URGENT;
        result.battery.message = 'URGENT: Pump Battery Low';
      } else if (result.battery.value < warn && batteryWarn) {
        result.battery.level = levels.WARN;
        result.battery.message = 'Warning, Pump Battery Low';
      } else {
        result.battery.level = levels.NONE;
      }
    }
  }

  function buildMessage (result) {
    if (result.level > levels.NONE) {
      var message = [];

      if (result.battery) {
        message.push('Pump Battery: ' + result.battery.display);
      }

      if (result.reservoir) {
        message.push('Pump Reservoir: ' + result.reservoir.display);
      }

      result.message = message.join('\n');
    }
  }

  function updateStatus(pump, result) {
    if (pump.status) {
      var status = pump.status.status || 'normal';
      if (pump.status.bolusing) {
        status = 'bolusing';
      } else if (pump.status.suspended) {
        status = 'suspended';
        if (pump.warnOnSuspend && pump.status.suspended) {
          result.status.level = levels.WARN;
          result.status.message = 'Pump Suspended';
        }
      }
      result.status = { value: status, display: status, label: translate('Status') };
    }
  }

  function prepareData (prop, prefs, sbx) {
    var pump = (prop && prop.pump) || { };
    var time = (sbx.data.profile && sbx.data.profile.getTimezone()) ? moment(sbx.time).tz(sbx.data.profile.getTimezone()) : moment(sbx.time);
    var now = time.hours() + time.minutes() / 60.0 + time.seconds() / 3600.0;
    var batteryWarn = !(prefs.warnBattQuietNight && (now < prefs.dayStart || now > prefs.dayEnd));
    var result = {
      level: levels.NONE
      , clock: pump.clock ? { value: moment(pump.clock) } : null
      , reservoir: pump.reservoir || pump.reservoir === 0 ? { value: pump.reservoir } : null
      , reservoir_display_override: pump.reservoir_display_override || null
      , manufacturer: pump.manufacturer
      , model: pump.model
      , extended: pump.extended || null
    };

    updateClock(prefs, result, sbx);
    updateReservoir(prefs, result);
    updateStatus(pump, result);

    if (pump.battery && pump.battery.percent) {
      result.battery = { value: pump.battery.percent, unit: 'percent' };
      updateBattery('%', prefs, result, batteryWarn);
    } else if (pump.battery && pump.battery.voltage) {
      result.battery = { value: pump.battery.voltage, unit: 'volts'};
      updateBattery('v', prefs, result, batteryWarn);
    }

    result.device = { label: translate('Device'), display: prop.device };

    result.title = 'Pump Status';
    result.level = levels.NONE;

    //TODO: A new Pump Offline marker?  Something generic?  Use something new instead of a treatment?
    if (openaps.findOfflineMarker(sbx)) {
      console.info('OpenAPS known offline, not checking for alerts');
    } else {
      _.forEach(ALL_STATUS_FIELDS, function eachField(fieldName) {
        var field = result[fieldName];
        if (field && field.level > result.level) {
          result.level = field.level;
          result.title = field.message;
        }
      });
    }

    buildMessage(result);

    return result;
  }

  function timeFormat (m, sbx) {

    var when;
    if (m && sbx.data.inRetroMode) {
      when = m.format('LT');
    } else if (m) {
      when = formatAgo(m, sbx.time);
    } else {
      when = 'unknown';
    }

    return when;
  }

  function formatAgo (m, nowMills) {
    var ago = timeago.calcDisplay({mills: m.valueOf()}, nowMills);
    return translate('%1' + ago.shortLabel + (ago.shortLabel.length === 1 ? ' ago' : ''), { params: [(ago.value ? ago.value : '')]});
  }

  return pump;
}

module.exports = init;
<commit_msg>reservoir level override<commit_after>'use strict';

var _ = require('lodash');
var moment = require('moment');
var times = require('../times');

var ALL_STATUS_FIELDS = ['reservoir', 'battery', 'clock', 'status', 'device'];

function init (ctx) {
  var translate = ctx.language.translate;
  var timeago = require('./timeago')(ctx);
  var openaps = require('./openaps')(ctx);
  var levels = ctx.levels;

  var pump = {
    name: 'pump'
    , label: 'Pump'
    , pluginType: 'pill-status'
  };

  pump.getPrefs = function getPrefs (sbx) {

    function cleanList (value) {
      return decodeURIComponent(value || '').toLowerCase().split(' ');
    }

    function isEmpty (list) {
      return _.isEmpty(list) || _.isEmpty(list[0]);
    }

    var fields = cleanList(sbx.extendedSettings.fields);
    fields = isEmpty(fields) ? ['reservoir'] : fields;

    var retroFields = cleanList(sbx.extendedSettings.retroFields);
    retroFields = isEmpty(retroFields) ? ['reservoir', 'battery'] : retroFields;

    var profile = sbx.data.profile;
    var warnBattQuietNight = sbx.extendedSettings.warnBattQuietNight;

    if (warnBattQuietNight && (!profile || !profile.hasData() || !profile.getTimezone())) {
      console.warn('PUMP_WARN_BATT_QUIET_NIGHT requires a treatment profile with time zone set to obtain user time zone');
      warnBattQuietNight = false;
    }

    return {
      fields: fields
      , retroFields: retroFields
      , warnClock: sbx.extendedSettings.warnClock || 30
      , urgentClock: sbx.extendedSettings.urgentClock || 60
      , warnRes: sbx.extendedSettings.warnRes || 10
      , urgentRes: sbx.extendedSettings.urgentRes || 5
      , warnBattV: sbx.extendedSettings.warnBattV || 1.35
      , urgentBattV: sbx.extendedSettings.urgentBattV || 1.3
      , warnBattP: sbx.extendedSettings.warnBattP || 30
      , urgentBattP: sbx.extendedSettings.urgentBattP || 20
      , warnOnSuspend: sbx.extendedSettings.warnOnSuspend || false
      , enableAlerts: sbx.extendedSettings.enableAlerts || false
      , warnBattQuietNight: warnBattQuietNight || false
      , dayStart: sbx.settings.dayStart
      , dayEnd: sbx.settings.dayEnd
    };
  };

  pump.setProperties = function setProperties (sbx) {
    sbx.offerProperty('pump', function setPump ( ) {

      var prefs = pump.getPrefs(sbx);
      var recentMills = sbx.time - times.mins(prefs.urgentClock * 2).msecs;

      var filtered = _.filter(sbx.data.devicestatus, function (status) {
        return ('pump' in status) && sbx.entryMills(status) <= sbx.time && sbx.entryMills(status) >= recentMills;
      });

      var pumpStatus = null;

      _.forEach(filtered, function each (status) {
        status.clockMills = status.pump && status.pump.clock ? moment(status.pump.clock).valueOf() : status.mills;
        if (!pumpStatus || status.clockMills > pumpStatus.clockMills) {
          pumpStatus = status;
        }
      });

      pumpStatus = pumpStatus || { };
      pumpStatus.data = prepareData(pumpStatus, prefs, sbx);

      return pumpStatus;
    });
  };

  pump.checkNotifications = function checkNotifications (sbx) {
    var prefs = pump.getPrefs(sbx);

    if (!prefs.enableAlerts) { return; }

    pump.warnOnSuspend = prefs.warnOnSuspend;

    var data = prepareData(sbx.properties.pump, prefs, sbx);

    if (data.level >= levels.WARN) {
      sbx.notifications.requestNotify({
        level: data.level
        , title: data.title
        , message: data.message
        , pushoverSound: 'echo'
        , group: 'Pump'
        , plugin: pump
      });
    }
  };

  pump.updateVisualisation = function updateVisualisation (sbx) {
    var prop = sbx.properties.pump;

    var prefs = pump.getPrefs(sbx);
    var result = prepareData(prop, prefs, sbx);

    var values = [ ];
    var info = [ ];

    var selectedFields = sbx.data.inRetroMode ? prefs.retroFields : prefs.fields;

    _.forEach(ALL_STATUS_FIELDS, function eachField (fieldName) {
      var field = result[fieldName];
      if (field) {
        var selected = _.indexOf(selectedFields, fieldName) > -1;
        if (selected) {
          values.push(field.display);
        } else {
          info.push({label: field.label, value: field.display});
        }
      }
    });

    if (result.extended) {
      info.push({label: '------------', value: ''});
      _.forOwn(result.extended, function(value, key) {
         info.push({ label: key, value: value });
      });
    }

    sbx.pluginBase.updatePillText(pump, {
      value: values.join(' ')
      , info: info
      , label: translate('Pump')
      , pillClass: statusClass(result.level)
    });
  };

  function virtAsstReservoirHandler (next, slots, sbx) {
    var reservoir = _.get(sbx, 'properties.pump.pump.reservoir');
    if (reservoir || reservoir === 0) {
      var response = translate('virtAsstReservoir', {
        params: [
            reservoir
        ]
      });
      next(translate('virtAsstTitlePumpReservoir'), response);
    } else {
      next(translate('virtAsstTitlePumpReservoir'), translate('virtAsstUnknown'));
    }
  }

  function virtAsstBatteryHandler (next, slots, sbx) {
    var battery = _.get(sbx, 'properties.pump.data.battery');
    if (battery) {
      var response = translate('virtAsstPumpBattery', {
              params: [
                  battery.value,
                  battery.unit
              ]
          });
      next(translate('virtAsstTitlePumpBattery'), response);
    } else {
      next(translate('virtAsstTitlePumpBattery'), translate('virtAsstUnknown'));
    }
  }

  pump.virtAsst = {
    intentHandlers:[
      {
        // backwards compatibility
        intent: 'InsulinRemaining',
        intentHandler: virtAsstReservoirHandler
      }
      , {
        // backwards compatibility
        intent: 'PumpBattery',
        intentHandler: virtAsstBatteryHandler
      }
      , {
        intent: 'MetricNow'
        , metrics: ['pump reservoir']
        , intentHandler: virtAsstReservoirHandler
      }
      , {
        intent: 'MetricNow'
        , metrics: ['pump battery']
        , intentHandler: virtAsstBatteryHandler
      }
    ]
  };

  function statusClass (level) {
    var cls = 'current';

    if (level === levels.WARN) {
      cls = 'warn';
    } else if (level === levels.URGENT) {
      cls = 'urgent';
    }

    return cls;
  }

  function updateClock (prefs, result, sbx) {
    if (result.clock) {
      result.clock.label = 'Last Clock';
      result.clock.display = timeFormat(result.clock.value, sbx);

      var urgent = moment(sbx.time).subtract(prefs.urgentClock, 'minutes');
      var warn = moment(sbx.time).subtract(prefs.warnClock, 'minutes');

      if (urgent.isAfter(result.clock.value)) {
        result.clock.level = levels.URGENT;
        result.clock.message = 'URGENT: Pump data stale';
      } else if (warn.isAfter(result.clock.value)) {
        result.clock.level = levels.WARN;
        result.clock.message = 'Warning, Pump data stale';
      } else {
        result.clock.level = levels.NONE;
      }
    }
  }

  function updateReservoir (prefs, result) {
    if (result.reservoir) {
      result.reservoir.label = 'Reservoir';
      result.reservoir.display = result.reservoir.value.toPrecision(3) + 'U';
      if (result.reservoir.value < prefs.urgentRes) {
        result.reservoir.level = levels.URGENT;
        result.reservoir.message = 'URGENT: Pump Reservoir Low';
      } else if (result.reservoir.value < prefs.warnRes) {
        result.reservoir.level = levels.WARN;
        result.reservoir.message = 'Warning, Pump Reservoir Low';
      } else {
        result.reservoir.level = levels.NONE;
      }
    } else if (result.manufacturer === 'Insulet') {
      result.reservoir = {
        label: 'Reservoir', display: '50+ U'
      }
    }
    if (result.reservoir_display_override) {
      result.reservoir.display = result.reservoir_display_override;
    }
    if (result.reservoir_level_override) {
      result.reservoir.level = result.reservoir_level_override;
    }
    console.log("pump result = ", result);
  }

  function updateBattery (type, prefs, result, batteryWarn) {
    if (result.battery) {
      result.battery.label = 'Battery';
      result.battery.display = result.battery.value + type;
      var urgent = type === 'v' ? prefs.urgentBattV : prefs.urgentBattP;
      var warn = type === 'v' ? prefs.warnBattV : prefs.warnBattP;

      if (result.battery.value < urgent && batteryWarn) {
        result.battery.level = levels.URGENT;
        result.battery.message = 'URGENT: Pump Battery Low';
      } else if (result.battery.value < warn && batteryWarn) {
        result.battery.level = levels.WARN;
        result.battery.message = 'Warning, Pump Battery Low';
      } else {
        result.battery.level = levels.NONE;
      }
    }
  }

  function buildMessage (result) {
    if (result.level > levels.NONE) {
      var message = [];

      if (result.battery) {
        message.push('Pump Battery: ' + result.battery.display);
      }

      if (result.reservoir) {
        message.push('Pump Reservoir: ' + result.reservoir.display);
      }

      result.message = message.join('\n');
    }
  }

  function updateStatus(pump, result) {
    if (pump.status) {
      var status = pump.status.status || 'normal';
      if (pump.status.bolusing) {
        status = 'bolusing';
      } else if (pump.status.suspended) {
        status = 'suspended';
        if (pump.warnOnSuspend && pump.status.suspended) {
          result.status.level = levels.WARN;
          result.status.message = 'Pump Suspended';
        }
      }
      result.status = { value: status, display: status, label: translate('Status') };
    }
  }

  function prepareData (prop, prefs, sbx) {
    var pump = (prop && prop.pump) || { };
    var time = (sbx.data.profile && sbx.data.profile.getTimezone()) ? moment(sbx.time).tz(sbx.data.profile.getTimezone()) : moment(sbx.time);
    var now = time.hours() + time.minutes() / 60.0 + time.seconds() / 3600.0;
    var batteryWarn = !(prefs.warnBattQuietNight && (now < prefs.dayStart || now > prefs.dayEnd));
    var result = {
      level: levels.NONE
      , clock: pump.clock ? { value: moment(pump.clock) } : null
      , reservoir: pump.reservoir || pump.reservoir === 0 ? { value: pump.reservoir } : null
      , reservoir_display_override: pump.reservoir_display_override || null
      , manufacturer: pump.manufacturer
      , model: pump.model
      , extended: pump.extended || null
    };

    updateClock(prefs, result, sbx);
    updateReservoir(prefs, result);
    updateStatus(pump, result);

    if (pump.battery && pump.battery.percent) {
      result.battery = { value: pump.battery.percent, unit: 'percent' };
      updateBattery('%', prefs, result, batteryWarn);
    } else if (pump.battery && pump.battery.voltage) {
      result.battery = { value: pump.battery.voltage, unit: 'volts'};
      updateBattery('v', prefs, result, batteryWarn);
    }

    result.device = { label: translate('Device'), display: prop.device };

    result.title = 'Pump Status';
    result.level = levels.NONE;

    //TODO: A new Pump Offline marker?  Something generic?  Use something new instead of a treatment?
    if (openaps.findOfflineMarker(sbx)) {
      console.info('OpenAPS known offline, not checking for alerts');
    } else {
      _.forEach(ALL_STATUS_FIELDS, function eachField(fieldName) {
        var field = result[fieldName];
        if (field && field.level > result.level) {
          result.level = field.level;
          result.title = field.message;
        }
      });
    }

    buildMessage(result);

    return result;
  }

  function timeFormat (m, sbx) {

    var when;
    if (m && sbx.data.inRetroMode) {
      when = m.format('LT');
    } else if (m) {
      when = formatAgo(m, sbx.time);
    } else {
      when = 'unknown';
    }

    return when;
  }

  function formatAgo (m, nowMills) {
    var ago = timeago.calcDisplay({mills: m.valueOf()}, nowMills);
    return translate('%1' + ago.shortLabel + (ago.shortLabel.length === 1 ? ' ago' : ''), { params: [(ago.value ? ago.value : '')]});
  }

  return pump;
}

module.exports = init;
<|endoftext|>