<commit_before>const bane = require("bane");
const WebSocket = require("isomorphic-ws");

class Mopidy {
  constructor(settings) {
    if (!(this instanceof Mopidy)) {
      return new Mopidy(settings);
    }
    this._console = Mopidy._getConsole(settings || {});
    this._settings = this._configure(settings || {});
    this._backoffDelay = this._settings.backoffDelayMin;
    this._pendingRequests = {};
    this._webSocket = null;
    bane.createEventEmitter(this);
    this._delegateEvents();
    if (this._settings.autoConnect) {
      this.connect();
    }
  }

  _configure(settings) {
    const newSettings = { ...settings };
    const protocol =
      typeof document !== "undefined" && document.location.protocol === "https:"
        ? "wss://"
        : "ws://";
    const currentHost =
      (typeof document !== "undefined" && document.location.host) ||
      "localhost";
    newSettings.webSocketUrl =
      settings.webSocketUrl || `${protocol}${currentHost}/mopidy/ws`;
    if (settings.autoConnect !== false) {
      newSettings.autoConnect = true;
    }
    newSettings.backoffDelayMin = settings.backoffDelayMin || 1000;
    newSettings.backoffDelayMax = settings.backoffDelayMax || 64000;
    if (typeof settings.callingConvention === "undefined") {
      this._console.warn(
        "Mopidy.js is using the default calling convention. The " +
          "default will change in the future. You should explicitly " +
          "specify which calling convention you use."
      );
    }
    newSettings.callingConvention =
      settings.callingConvention || "by-position-only";
    return newSettings;
  }

  _delegateEvents() {
    // Remove existing event handlers
    this.off("websocket:close");
    this.off("websocket:error");
    this.off("websocket:incomingMessage");
    this.off("websocket:open");
    this.off("state:offline");
    // Register basic set of event handlers
    this.on("websocket:close", this._cleanup);
    this.on("websocket:error", this._handleWebSocketError);
    this.on("websocket:incomingMessage", this._handleMessage);
    this.on("websocket:open", this._resetBackoffDelay);
    this.on("websocket:open", this._getApiSpec);
    this.on("state:offline", this._reconnect);
  }

  connect() {
    if (this._webSocket) {
      if (this._webSocket.readyState === Mopidy.WebSocket.OPEN) {
        return;
      }
      this._webSocket.close();
    }

    this._webSocket =
      this._settings.webSocket ||
      new Mopidy.WebSocket(this._settings.webSocketUrl);

    this._webSocket.onclose = close => {
      this.emit("websocket:close", close);
    };
    this._webSocket.onerror = error => {
      this.emit("websocket:error", error);
    };
    this._webSocket.onopen = () => {
      this.emit("websocket:open");
    };
    this._webSocket.onmessage = message => {
      this.emit("websocket:incomingMessage", message);
    };
  }

  _cleanup(closeEvent) {
    Object.keys(this._pendingRequests).forEach(requestId => {
      const resolver = this._pendingRequests[requestId];
      delete this._pendingRequests[requestId];
      const error = new Mopidy.ConnectionError("WebSocket closed");
      error.closeEvent = closeEvent;
      resolver.reject(error);
    });
    this.emit("state:offline");
  }

  _reconnect() {
    this.emit("reconnectionPending", {
      timeToAttempt: this._backoffDelay,
    });
    setTimeout(() => {
      this.emit("reconnecting");
      this.connect();
    }, this._backoffDelay);
    this._backoffDelay = this._backoffDelay * 2;
    if (this._backoffDelay > this._settings.backoffDelayMax) {
      this._backoffDelay = this._settings.backoffDelayMax;
    }
  }

  _resetBackoffDelay() {
    this._backoffDelay = this._settings.backoffDelayMin;
  }

  close() {
    this.off("state:offline", this._reconnect);
    this._webSocket.close();
  }

  _handleWebSocketError(error) {
    this._console.warn("WebSocket error:", error.stack || error);
  }

  _send(message) {
    switch (this._webSocket.readyState) {
      case Mopidy.WebSocket.CONNECTING:
        return Promise.reject(
          new Mopidy.ConnectionError("WebSocket is still connecting")
        );
      case Mopidy.WebSocket.CLOSING:
        return Promise.reject(
          new Mopidy.ConnectionError("WebSocket is closing")
        );
      case Mopidy.WebSocket.CLOSED:
        return Promise.reject(
          new Mopidy.ConnectionError("WebSocket is closed")
        );
      default:
    }
    const jsonRpcMessage = {
      ...message,
      jsonrpc: "2.0",
      id: this._nextRequestId(),
    };
    return new Promise((resolve, reject) => {
      this._pendingRequests[jsonRpcMessage.id] = { resolve, reject };
      this._webSocket.send(JSON.stringify(jsonRpcMessage));
      this.emit("websocket:outgoingMessage", jsonRpcMessage);
    });
  }

  _handleMessage(message) {
    try {
      const data = JSON.parse(message.data);
      if (Object.hasOwnProperty.call(data, "id")) {
        this._handleResponse(data);
      } else if (Object.hasOwnProperty.call(data, "event")) {
        this._handleEvent(data);
      } else {
        this._console.warn(
          `Unknown message type received. Message was: ${message.data}`
        );
      }
    } catch (error) {
      if (error instanceof SyntaxError) {
        this._console.warn(
          `WebSocket message parsing failed. Message was: ${message.data}`
        );
      } else {
        throw error;
      }
    }
  }

  _handleResponse(responseMessage) {
    if (
      !Object.hasOwnProperty.call(this._pendingRequests, responseMessage.id)
    ) {
      this._console.warn(
        "Unexpected response received. Message was:",
        responseMessage
      );
      return;
    }
    let error;
    const resolver = this._pendingRequests[responseMessage.id];
    delete this._pendingRequests[responseMessage.id];
    if (Object.hasOwnProperty.call(responseMessage, "result")) {
      resolver.resolve(responseMessage.result);
    } else if (Object.hasOwnProperty.call(responseMessage, "error")) {
      error = new Mopidy.ServerError(responseMessage.error.message);
      error.code = responseMessage.error.code;
      error.data = responseMessage.error.data;
      resolver.reject(error);
      this._console.warn("Server returned error:", responseMessage.error);
    } else {
      error = new Error("Response without 'result' or 'error' received");
      error.data = { response: responseMessage };
      resolver.reject(error);
      this._console.warn(
        "Response without 'result' or 'error' received. Message was:",
        responseMessage
      );
    }
  }

  _handleEvent(eventMessage) {
    const type = eventMessage.event;
    const data = eventMessage;
    delete data.event;
    this.emit(`event:${Mopidy._snakeToCamel(type)}`, data);
  }

  _getApiSpec() {
    return this._send({ method: "core.describe" })
      .then(this._createApi.bind(this))
      .catch(this._handleWebSocketError);
  }

  _createApi(methods) {
    const byPositionOrByName =
      this._settings.callingConvention === "by-position-or-by-name";

    const caller = method => (...args) => {
      const message = { method };
      if (args.length === 0) {
        return this._send(message);
      }
      if (!byPositionOrByName) {
        message.params = Array.prototype.slice.call(args);
        return this._send(message);
      }
      if (args.length > 1) {
        return Promise.reject(
          new Error(
            "Expected zero arguments, a single array, or a single object."
          )
        );
      }
      if (!Array.isArray(args[0]) && args[0] !== Object(args[0])) {
        return Promise.reject(new TypeError("Expected an array or an object."));
      }
      [message.params] = args;
      return this._send(message);
    };

    const getPath = fullName => {
      let path = fullName.split(".");
      if (path.length >= 1 && path[0] === "core") {
        path = path.slice(1);
      }
      return path;
    };

    const createObjects = objPath => {
      let parentObj = this;
      objPath.forEach(objName => {
        const camelObjName = Mopidy._snakeToCamel(objName);
        parentObj[camelObjName] = parentObj[camelObjName] || {};
        parentObj = parentObj[camelObjName];
      });
      return parentObj;
    };

    const createMethod = fullMethodName => {
      const methodPath = getPath(fullMethodName);
      const methodName = Mopidy._snakeToCamel(methodPath.slice(-1)[0]);
      const object = createObjects(methodPath.slice(0, -1));
      object[methodName] = caller(fullMethodName);
      object[methodName].description = methods[fullMethodName].description;
      object[methodName].params = methods[fullMethodName].params;
    };

    Object.keys(methods).forEach(createMethod);

    this.emit("state:online");
  }

  static _getConsole(settings) {
    if (typeof settings.console !== "undefined") {
      return settings.console;
    }
    const con = (typeof console !== "undefined" && console) || {};
    con.log = con.log || (() => {});
    con.warn = con.warn || (() => {});
    con.error = con.error || (() => {});
    return con;
  }

  static _snakeToCamel(name) {
    return name.replace(/(_[a-z])/g, match =>
      match.toUpperCase().replace("_", "")
    );
  }
}

class ConnectionError extends Error {
  constructor(message) {
    super(message);
    this.name = "ConnectionError";
  }
}
Mopidy.ConnectionError = ConnectionError;

class ServerError extends Error {
  constructor(message) {
    super(message);
    this.name = "ServerError";
  }
}
Mopidy.ServerError = ServerError;

Mopidy.WebSocket = WebSocket;

Mopidy.prototype._nextRequestId = (() => {
  let lastUsed = -1;
  return () => {
    lastUsed += 1;
    return lastUsed;
  };
})();

module.exports = Mopidy;
<commit_msg>Remove dead code<commit_after>const bane = require("bane");
const WebSocket = require("isomorphic-ws");

class Mopidy {
  constructor(settings) {
    this._console = Mopidy._getConsole(settings || {});
    this._settings = this._configure(settings || {});
    this._backoffDelay = this._settings.backoffDelayMin;
    this._pendingRequests = {};
    this._webSocket = null;
    bane.createEventEmitter(this);
    this._delegateEvents();
    if (this._settings.autoConnect) {
      this.connect();
    }
  }

  _configure(settings) {
    const newSettings = { ...settings };
    const protocol =
      typeof document !== "undefined" && document.location.protocol === "https:"
        ? "wss://"
        : "ws://";
    const currentHost =
      (typeof document !== "undefined" && document.location.host) ||
      "localhost";
    newSettings.webSocketUrl =
      settings.webSocketUrl || `${protocol}${currentHost}/mopidy/ws`;
    if (settings.autoConnect !== false) {
      newSettings.autoConnect = true;
    }
    newSettings.backoffDelayMin = settings.backoffDelayMin || 1000;
    newSettings.backoffDelayMax = settings.backoffDelayMax || 64000;
    if (typeof settings.callingConvention === "undefined") {
      this._console.warn(
        "Mopidy.js is using the default calling convention. The " +
          "default will change in the future. You should explicitly " +
          "specify which calling convention you use."
      );
    }
    newSettings.callingConvention =
      settings.callingConvention || "by-position-only";
    return newSettings;
  }

  _delegateEvents() {
    // Remove existing event handlers
    this.off("websocket:close");
    this.off("websocket:error");
    this.off("websocket:incomingMessage");
    this.off("websocket:open");
    this.off("state:offline");
    // Register basic set of event handlers
    this.on("websocket:close", this._cleanup);
    this.on("websocket:error", this._handleWebSocketError);
    this.on("websocket:incomingMessage", this._handleMessage);
    this.on("websocket:open", this._resetBackoffDelay);
    this.on("websocket:open", this._getApiSpec);
    this.on("state:offline", this._reconnect);
  }

  connect() {
    if (this._webSocket) {
      if (this._webSocket.readyState === Mopidy.WebSocket.OPEN) {
        return;
      }
      this._webSocket.close();
    }

    this._webSocket =
      this._settings.webSocket ||
      new Mopidy.WebSocket(this._settings.webSocketUrl);

    this._webSocket.onclose = close => {
      this.emit("websocket:close", close);
    };
    this._webSocket.onerror = error => {
      this.emit("websocket:error", error);
    };
    this._webSocket.onopen = () => {
      this.emit("websocket:open");
    };
    this._webSocket.onmessage = message => {
      this.emit("websocket:incomingMessage", message);
    };
  }

  _cleanup(closeEvent) {
    Object.keys(this._pendingRequests).forEach(requestId => {
      const resolver = this._pendingRequests[requestId];
      delete this._pendingRequests[requestId];
      const error = new Mopidy.ConnectionError("WebSocket closed");
      error.closeEvent = closeEvent;
      resolver.reject(error);
    });
    this.emit("state:offline");
  }

  _reconnect() {
    this.emit("reconnectionPending", {
      timeToAttempt: this._backoffDelay,
    });
    setTimeout(() => {
      this.emit("reconnecting");
      this.connect();
    }, this._backoffDelay);
    this._backoffDelay = this._backoffDelay * 2;
    if (this._backoffDelay > this._settings.backoffDelayMax) {
      this._backoffDelay = this._settings.backoffDelayMax;
    }
  }

  _resetBackoffDelay() {
    this._backoffDelay = this._settings.backoffDelayMin;
  }

  close() {
    this.off("state:offline", this._reconnect);
    this._webSocket.close();
  }

  _handleWebSocketError(error) {
    this._console.warn("WebSocket error:", error.stack || error);
  }

  _send(message) {
    switch (this._webSocket.readyState) {
      case Mopidy.WebSocket.CONNECTING:
        return Promise.reject(
          new Mopidy.ConnectionError("WebSocket is still connecting")
        );
      case Mopidy.WebSocket.CLOSING:
        return Promise.reject(
          new Mopidy.ConnectionError("WebSocket is closing")
        );
      case Mopidy.WebSocket.CLOSED:
        return Promise.reject(
          new Mopidy.ConnectionError("WebSocket is closed")
        );
      default:
    }
    const jsonRpcMessage = {
      ...message,
      jsonrpc: "2.0",
      id: this._nextRequestId(),
    };
    return new Promise((resolve, reject) => {
      this._pendingRequests[jsonRpcMessage.id] = { resolve, reject };
      this._webSocket.send(JSON.stringify(jsonRpcMessage));
      this.emit("websocket:outgoingMessage", jsonRpcMessage);
    });
  }

  _handleMessage(message) {
    try {
      const data = JSON.parse(message.data);
      if (Object.hasOwnProperty.call(data, "id")) {
        this._handleResponse(data);
      } else if (Object.hasOwnProperty.call(data, "event")) {
        this._handleEvent(data);
      } else {
        this._console.warn(
          `Unknown message type received. Message was: ${message.data}`
        );
      }
    } catch (error) {
      if (error instanceof SyntaxError) {
        this._console.warn(
          `WebSocket message parsing failed. Message was: ${message.data}`
        );
      } else {
        throw error;
      }
    }
  }

  _handleResponse(responseMessage) {
    if (
      !Object.hasOwnProperty.call(this._pendingRequests, responseMessage.id)
    ) {
      this._console.warn(
        "Unexpected response received. Message was:",
        responseMessage
      );
      return;
    }
    let error;
    const resolver = this._pendingRequests[responseMessage.id];
    delete this._pendingRequests[responseMessage.id];
    if (Object.hasOwnProperty.call(responseMessage, "result")) {
      resolver.resolve(responseMessage.result);
    } else if (Object.hasOwnProperty.call(responseMessage, "error")) {
      error = new Mopidy.ServerError(responseMessage.error.message);
      error.code = responseMessage.error.code;
      error.data = responseMessage.error.data;
      resolver.reject(error);
      this._console.warn("Server returned error:", responseMessage.error);
    } else {
      error = new Error("Response without 'result' or 'error' received");
      error.data = { response: responseMessage };
      resolver.reject(error);
      this._console.warn(
        "Response without 'result' or 'error' received. Message was:",
        responseMessage
      );
    }
  }

  _handleEvent(eventMessage) {
    const type = eventMessage.event;
    const data = eventMessage;
    delete data.event;
    this.emit(`event:${Mopidy._snakeToCamel(type)}`, data);
  }

  _getApiSpec() {
    return this._send({ method: "core.describe" })
      .then(this._createApi.bind(this))
      .catch(this._handleWebSocketError);
  }

  _createApi(methods) {
    const byPositionOrByName =
      this._settings.callingConvention === "by-position-or-by-name";

    const caller = method => (...args) => {
      const message = { method };
      if (args.length === 0) {
        return this._send(message);
      }
      if (!byPositionOrByName) {
        message.params = Array.prototype.slice.call(args);
        return this._send(message);
      }
      if (args.length > 1) {
        return Promise.reject(
          new Error(
            "Expected zero arguments, a single array, or a single object."
          )
        );
      }
      if (!Array.isArray(args[0]) && args[0] !== Object(args[0])) {
        return Promise.reject(new TypeError("Expected an array or an object."));
      }
      [message.params] = args;
      return this._send(message);
    };

    const getPath = fullName => {
      let path = fullName.split(".");
      if (path.length >= 1 && path[0] === "core") {
        path = path.slice(1);
      }
      return path;
    };

    const createObjects = objPath => {
      let parentObj = this;
      objPath.forEach(objName => {
        const camelObjName = Mopidy._snakeToCamel(objName);
        parentObj[camelObjName] = parentObj[camelObjName] || {};
        parentObj = parentObj[camelObjName];
      });
      return parentObj;
    };

    const createMethod = fullMethodName => {
      const methodPath = getPath(fullMethodName);
      const methodName = Mopidy._snakeToCamel(methodPath.slice(-1)[0]);
      const object = createObjects(methodPath.slice(0, -1));
      object[methodName] = caller(fullMethodName);
      object[methodName].description = methods[fullMethodName].description;
      object[methodName].params = methods[fullMethodName].params;
    };

    Object.keys(methods).forEach(createMethod);

    this.emit("state:online");
  }

  static _getConsole(settings) {
    if (typeof settings.console !== "undefined") {
      return settings.console;
    }
    const con = (typeof console !== "undefined" && console) || {};
    con.log = con.log || (() => {});
    con.warn = con.warn || (() => {});
    con.error = con.error || (() => {});
    return con;
  }

  static _snakeToCamel(name) {
    return name.replace(/(_[a-z])/g, match =>
      match.toUpperCase().replace("_", "")
    );
  }
}

class ConnectionError extends Error {
  constructor(message) {
    super(message);
    this.name = "ConnectionError";
  }
}
Mopidy.ConnectionError = ConnectionError;

class ServerError extends Error {
  constructor(message) {
    super(message);
    this.name = "ServerError";
  }
}
Mopidy.ServerError = ServerError;

Mopidy.WebSocket = WebSocket;

Mopidy.prototype._nextRequestId = (() => {
  let lastUsed = -1;
  return () => {
    lastUsed += 1;
    return lastUsed;
  };
})();

module.exports = Mopidy;
<|endoftext|>


<commit_before>TrelloPowerUp.initialize({
  'board-buttons': function(t, options){
    return [{
    icon: './images/button-pic.jpg',
    text: 'My Button',
    callback: function(t){
      $(document).ready(function(){
              $(".open-card-composer js-open-card-composer").remove();
    }}];
  }
});
<commit_msg>remove header<commit_after>TrelloPowerUp.initialize({
  'board-buttons': function(t, options){
    return [{
    icon: './images/button-pic.jpg',
    text: 'My Button',
    callback: function(t){
      var elem = document.getElementById("header");
      elem.parentNode.removeChild(elem);
    }}];
  }
});
<|endoftext|>


<commit_before>var Coffee = {
  api: 'http://draug.online.ntnu.no/coffee.txt',
  msgConnectionError: 'Frakoblet fra kaffeknappen',

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

    // Receives the status for the coffee pot
    var self = this;
    $.ajax({
      url: self.api,
      success: function(data) {

        // Split into pot number and age of last pot
        var pieces = data.split("\n");
        var pots = pieces[0];
        var age = pieces[1];

        // Calulate the age of the last pot better
        var time = String(age.match(/\d+:\d+:\d+/)).split(':');
        var now = new Date();
        var then = new Date(now.getFullYear(), now.getMonth(), now.getDate(), time[0], time[1]);
        var one_minute = 1000 * 60;
        // Calculate difference between the two dates, and convert to minutes
        var diff = Math.floor(( then.getTime() - now.getTime() ) / one_minute);
        // Create a proper time string from all the minutes
        var ageString;
        if (-1 <= diff && diff <= 0) {
          ageString = 'Kaffen ble nettopp laget';
        }
        else if (1 <= diff && diff <= 59) {
          ageString = 'Kaffen ble laget for '+diff+' min siden';
        }
        else if (60 <= diff) {
          ageString = 'Kaffen ble laget kl '+time[0]+':'+time[1]+'';
        }

        // Add 'kanner' to pots-string
        pots = ''+(pots=='0'?'Ingen kanner':pots=='1'?'1 kanne':pots+' kanner')+'';

        // Make a string worthy of returning
        var coffee = ageString+'
'+pots+' i dag'

        callback(coffee);
      },
      error: function(jqXHR, text, err) {
        if (DEBUG) console.log('ERROR: Failed to get coffee pot status.');
        callback(self.msgConnectionError);
      },
    });
  },

  getPots: 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;
    $.ajax({
      url: self.api,
      success: function(data) {
        // Find amount of pots today, convert to number
        var pieces = data.split(/:(.+)/);
        var pots = Number(pieces[0]);
        callback(pots);
      },
      error: function(jqXHR, text, err) {
        if (DEBUG) console.log('ERROR: Failed to get coffee pot status.');
        callback(-1);
      },
    });
  },

  showNotification: function(pots) {
    var notification = webkitNotifications.createHTMLNotification('subscription.html');
    notification.show(); // HTML5-style
  },

}
<commit_msg>Coffee error handling and handling yesterdays coffee<commit_after>var Coffee = {
  api: 'http://draug.online.ntnu.no/coffee.txt',
  msgConnectionError: 'Frakoblet fra kaffeknappen',

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

    // Receives the status for the coffee pot
    var self = this;
    $.ajax({
      url: self.api,
      success: function(data) {

        // Test data
        // if (DEBUG) {
        //   data = "200\n27. February 21:20:371";
        //   // data = "what";
        // }

        try {
          // Split into pot number and age of last pot
          var pieces = data.split("\n");
          var pots = pieces[0];
          var age = pieces[1];

          // Get now
          var now = new Date();

          // Check if the coffee pots were made today
          var coffeeDate = new Date(age.match(/\d+\. [a-zA-Z]+/));
          if (coffeeDate.getDate() == now.getDate()) {

            // Calulate the age of the last pot better
            var coffeeTime = String(age.match(/\d+:\d+:\d+/)).split(':');
            var then = new Date(now.getFullYear(), now.getMonth(), now.getDate(), coffeeTime[0], coffeeTime[1]);
            var one_minute = 1000 * 60;

            // Calculate difference between the two dates, and convert to minutes
            var diff = Math.floor(( now.getTime() - then.getTime() ) / one_minute);

            // Create a proper time string from all the minutes
            var ageString;
            if (-1 <= diff && diff <= 9) {
              ageString = 'Kaffen ble nettopp laget';
            }
            else if (10 <= diff && diff <= 59) {
              ageString = 'Kaffen ble laget for '+diff+' min siden';
            }
            else if (60 <= diff) {
              ageString = 'Kaffen ble laget kl '+coffeeTime[0]+':'+coffeeTime[1];
            }
            else {
              ageString = 'Har noen laget kaffe i fremtiden?!'
            }

            // Add 'kanner' to pots-string
            pots = (pots=='0'?'Ingen kanner':pots=='1'?'1 kanne':pots+' kanner') + ' i dag';

            // Make a complete string
            var coffee = ageString+'
'+pots
            callback(coffee);
          }
          else {
            // Coffee was not made today
            var coffee = 'Kaffen har ikke blitt satt på
Ingen kanner i dag';
            callback(coffee);
          }
        } catch (err) {
          // Coffee format is wrong
          var coffee = 'Feil i kaffeformat...
Det er sikkert kaffe!';
          callback(coffee);
        }
      },
      error: function(jqXHR, text, err) {
        if (DEBUG) console.log('ERROR: Failed to get coffee pot status.');
        callback(self.msgConnectionError);
      },
    });
  },

  getPots: 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;
    $.ajax({
      url: self.api,
      success: function(data) {
        // Find amount of pots today, convert to number
        var pieces = data.split(/:(.+)/);
        var pots = Number(pieces[0]);
        callback(pots);
      },
      error: function(jqXHR, text, err) {
        if (DEBUG) console.log('ERROR: Failed to get coffee pot status.');
        callback(-1);
      },
    });
  },

  showNotification: function(pots) {
    var notification = webkitNotifications.createHTMLNotification('subscription.html');
    notification.show(); // HTML5-style
  },

}
<|endoftext|>


<commit_before>'use strict';

/**
 * @ngdoc service
 * @name $log
 * @requires $window
 *
 * @description
 * Simple service for logging. Default implementation safely writes the message
 * into the browser's console (if present).
 *
 * The main purpose of this service is to simplify debugging and troubleshooting.
 *
 * The default is to log `debug` messages. You can use
 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
 *
 * @example
   <example module="logExample">
     <file name="script.js">
       angular.module('logExample', [])
         .controller('LogController', ['$scope', '$log', function($scope, $log) {
           $scope.$log = $log;
           $scope.message = 'Hello World!';
         }]);
     </file>
     <file name="index.html">

Reload this page with open console, enter text and hit the log button...

         Message:
         <input type="text" ng-model="message"/>
         <button ng-click="$log.log(message)">log</button>
         <button ng-click="$log.warn(message)">warn</button>
         <button ng-click="$log.info(message)">info</button>
         <button ng-click="$log.error(message)">error</button>

     </file>
   </example>
 */

/**
 * @ngdoc provider
 * @name $logProvider
 * @description
 * Use the `$logProvider` to configure how the application logs messages
 */
function $LogProvider() {
  var debug = true,
      self = this;

  /**
   * @ngdoc method
   * @name $logProvider#debugEnabled
   * @description
   * @param {boolean=} flag enable or disable debug level messages
   * @returns {*} current value if used as getter or itself (chaining) if used as setter
   */
  this.debugEnabled = function(flag) {
    if (isDefined(flag)) {
      debug = flag;
    return this;
    } else {
      return debug;
    }
  };

  this.$get = ['$window', function($window) {
    return {
      /**
       * @ngdoc method
       * @name $log#log
       *
       * @description
       * Write a log message
       */
      log: consoleLog('log'),

      /**
       * @ngdoc method
       * @name $log#info
       *
       * @description
       * Write an information message
       */
      info: consoleLog('info'),

      /**
       * @ngdoc method
       * @name $log#warn
       *
       * @description
       * Write a warning message
       */
      warn: consoleLog('warn'),

      /**
       * @ngdoc method
       * @name $log#error
       *
       * @description
       * Write an error message
       */
      error: consoleLog('error'),

      /**
       * @ngdoc method
       * @name $log#debug
       *
       * @description
       * Write a debug message
       */
      debug: (function() {
        var fn = consoleLog('debug');

        return function() {
          if (debug) {
            fn.apply(self, arguments);
          }
        };
      }())
    };

    function formatError(arg) {
      if (arg instanceof Error) {
        if (arg.stack) {
          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
              ? 'Error: ' + arg.message + '\n' + arg.stack
              : arg.stack;
        } else if (arg.sourceURL) {
          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
        }
      }
      return arg;
    }

    function consoleLog(type) {
      var console = $window.console || {},
          logFn = console[type] || console.log || noop,
          hasApply = false;

      // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
      // The reason behind this is that console.log has type "object" in IE8...
      try {
        hasApply = !!logFn.apply;
      } catch (e) {}

      if (hasApply) {
        return function() {
          var args = [];
          forEach(arguments, function(arg) {
            args.push(formatError(arg));
          });
          return logFn.apply(console, args);
        };
      }

      // we are IE which either doesn't have window.console => this is noop and we do nothing,
      // or we are IE where console.log doesn't have apply so we log at least first 2 args
      return function(arg1, arg2) {
        logFn(arg1, arg2 == null ? '' : arg2);
      };
    }
  }];
}
<commit_msg>docs($log): Add debug button to example<commit_after>'use strict';

/**
 * @ngdoc service
 * @name $log
 * @requires $window
 *
 * @description
 * Simple service for logging. Default implementation safely writes the message
 * into the browser's console (if present).
 *
 * The main purpose of this service is to simplify debugging and troubleshooting.
 *
 * The default is to log `debug` messages. You can use
 * {@link ng.$logProvider ng.$logProvider#debugEnabled} to change this.
 *
 * @example
   <example module="logExample">
     <file name="script.js">
       angular.module('logExample', [])
         .controller('LogController', ['$scope', '$log', function($scope, $log) {
           $scope.$log = $log;
           $scope.message = 'Hello World!';
         }]);
     </file>
     <file name="index.html">

Reload this page with open console, enter text and hit the log button...

         Message:
         <input type="text" ng-model="message"/>
         <button ng-click="$log.log(message)">log</button>
         <button ng-click="$log.warn(message)">warn</button>
         <button ng-click="$log.info(message)">info</button>
         <button ng-click="$log.error(message)">error</button>
         <button ng-click="$log.debug(message)">debug</button>

     </file>
   </example>
 */

/**
 * @ngdoc provider
 * @name $logProvider
 * @description
 * Use the `$logProvider` to configure how the application logs messages
 */
function $LogProvider() {
  var debug = true,
      self = this;

  /**
   * @ngdoc method
   * @name $logProvider#debugEnabled
   * @description
   * @param {boolean=} flag enable or disable debug level messages
   * @returns {*} current value if used as getter or itself (chaining) if used as setter
   */
  this.debugEnabled = function(flag) {
    if (isDefined(flag)) {
      debug = flag;
    return this;
    } else {
      return debug;
    }
  };

  this.$get = ['$window', function($window) {
    return {
      /**
       * @ngdoc method
       * @name $log#log
       *
       * @description
       * Write a log message
       */
      log: consoleLog('log'),

      /**
       * @ngdoc method
       * @name $log#info
       *
       * @description
       * Write an information message
       */
      info: consoleLog('info'),

      /**
       * @ngdoc method
       * @name $log#warn
       *
       * @description
       * Write a warning message
       */
      warn: consoleLog('warn'),

      /**
       * @ngdoc method
       * @name $log#error
       *
       * @description
       * Write an error message
       */
      error: consoleLog('error'),

      /**
       * @ngdoc method
       * @name $log#debug
       *
       * @description
       * Write a debug message
       */
      debug: (function() {
        var fn = consoleLog('debug');

        return function() {
          if (debug) {
            fn.apply(self, arguments);
          }
        };
      }())
    };

    function formatError(arg) {
      if (arg instanceof Error) {
        if (arg.stack) {
          arg = (arg.message && arg.stack.indexOf(arg.message) === -1)
              ? 'Error: ' + arg.message + '\n' + arg.stack
              : arg.stack;
        } else if (arg.sourceURL) {
          arg = arg.message + '\n' + arg.sourceURL + ':' + arg.line;
        }
      }
      return arg;
    }

    function consoleLog(type) {
      var console = $window.console || {},
          logFn = console[type] || console.log || noop,
          hasApply = false;

      // Note: reading logFn.apply throws an error in IE11 in IE8 document mode.
      // The reason behind this is that console.log has type "object" in IE8...
      try {
        hasApply = !!logFn.apply;
      } catch (e) {}

      if (hasApply) {
        return function() {
          var args = [];
          forEach(arguments, function(arg) {
            args.push(formatError(arg));
          });
          return logFn.apply(console, args);
        };
      }

      // we are IE which either doesn't have window.console => this is noop and we do nothing,
      // or we are IE where console.log doesn't have apply so we log at least first 2 args
      return function(arg1, arg2) {
        logFn(arg1, arg2 == null ? '' : arg2);
      };
    }
  }];
}
<|endoftext|>


<commit_before>var md = new Remarkable({
  linkify: true
});

// Invoked when the user clicks the radio buttons.
function filterDidChange(radio) {
  localStorage.setItem('filter', radio.value);
  $('#container .filterable-node').hide();
  $('.tag-' + radio.value).show();
}

var partyMoji = String.fromCodePoint(0x1F389);

function sortRepos(repos) {
 return repos.sort(function(a, b) {
      if (a.name < b.name) return -1;
      if (a.name > b.name) return 1;
      return 0;
  });
}

function numberWithCommas(x) {
  // http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
  return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

// Create a material lite card.
function createCard(titleNode, descriptionNode, actionsNode) {
  var card = document.createElement('div');
  card.className = 'mdl-cell mdl-color--white mdl-grid mdl-card mdl-shadow--2dp';
  titleNode.className = 'mdl-card__title';
  actionsNode.className = 'mdl-card__supporting-text';
  descriptionNode.className = 'mdl-card__actions mdl-card--border';
  card.appendChild(titleNode);
  card.appendChild(actionsNode);
  card.appendChild(descriptionNode);
  return card;
}

function shortNameForRepoName(repoName) {
  return repoName.replace(/^material-motion-/, '').replace(/-android$/, '');
}

function preprocessRepo(repo) {
  repo['shortName'] = shortNameForRepoName(repo.name);

  var filterClass = 'tag-other';
  if (repo.name.match(/-android$/)) {
    filterClass = 'tag-android';
  } else if (repo.name.match(/-(objc|swift)$/)) {
    filterClass = 'tag-appleos';
  } else if (repo.name.match(/-(web|js)$/)) {
    filterClass = 'tag-web';
  }
  repo['filterClass'] = filterClass;
  return repo;
}

function preprocessIssue(issue) {
  var re = /repos\/(.+?)\/(.+?)$/;
  var m;

  if ((m = re.exec(issue.repository_url)) !== null) {
    if (m.index === re.lastIndex) {
      re.lastIndex++;
    }

    issue['repo_html_url'] = "https://github.com/" + m[1] + "/" + m[2];
    issue['repo'] = m[2];
  }

  issue['repoShortName'] = shortNameForRepoName(issue.repo);

  var filterClass = 'tag-other';
  if (issue.repo.match(/-android$/)) {
    filterClass = 'tag-android';
  } else if (issue.repo.match(/-(objc|swift)$/)) {
    filterClass = 'tag-appleos';
  } else if (issue.repo.match(/-(web|js)$/)) {
    filterClass = 'tag-web';
  }
  issue['filterClass'] = filterClass;
  return issue;
}

function shouldHideFilterableNode(object) {
  return 'tag-' + localStorage.getItem('filter') != object.filterClass;
}

function didCreateFilterableNode(object, node) {
  node.className += " filterable-node " + object.filterClass;
  if (shouldHideFilterableNode(object)) {
    $(node).hide();
  }
}

// Cached github request.
function requestGitHubAPI(path, data, callback) {
  if (typeof callback === 'undefined') {
    callback = data;
    data = undefined;
  }
  var cacheKey = path;
  if (data) {
    cacheKey += "::" + JSON.stringify(data);
  }
  var storage = localStorage.getItem(cacheKey);
  var storageTimestamp = localStorage.getItem(cacheKey + '.timestamp');
  if (storage && storageTimestamp && Date.now() - storageTimestamp < 5 * 60 * 1000 ) {
    callback.call(null, JSON.parse(storage));
    return;
  }
  $.ajax({
    type: 'get',
    url: 'https://api.github.com' + path,
    data: data,
    complete: function(xhr) {
      localStorage.setItem(cacheKey, JSON.stringify(xhr.responseJSON));
      localStorage.setItem(cacheKey + '.timestamp', Date.now());
      callback.call(null, xhr.responseJSON);
    }
  });
}

$(function() {
  // Add event listeners to each radio button.
  var radioButtons = document.filter.filterby;
  for(var i = 0; i < radioButtons.length; i++) {
    radioButtons[i].onclick = function() {
      filterDidChange(this)
    };
  }

  $(document).ready(function() {
    var filter = localStorage.getItem('filter');
    if (filter) {
      var radio = document.getElementById('filter-' + filter);
      radio.click();
      filterDidChange(radio);
    } else {
      var radio = document.getElementById('filter-android');
      radio.click();
      filterDidChange(radio);
    }

    document.getElementById('clear').onclick = function(event) {
      event.stopPropagation();
      localStorage.clear();
      window.location.reload();
    }
  });
});
<commit_msg>Embed the filter into the url.<commit_after>var md = new Remarkable({
  linkify: true
});

// http://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript
function getParameterByName(name) {
  url = window.location.href;
  name = name.replace(/[\[\]]/g, "\\$&");
  var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"),
      results = regex.exec(url);
  if (!results) return null;
  if (!results[2]) return '';
  return decodeURIComponent(results[2].replace(/\+/g, " "));
}

// Invoked when the user clicks the radio buttons.
function filterDidChange(radio) {
  localStorage.setItem('filter', radio.value);
  $('#container .filterable-node').hide();
  $('.tag-' + radio.value).show();

  var href = window.location.href.replace(/\?.+/, '');
  window.history.replaceState(null, null, href + "?filterby=" + radio.value);
}

var partyMoji = String.fromCodePoint(0x1F389);

function sortRepos(repos) {
 return repos.sort(function(a, b) {
      if (a.name < b.name) return -1;
      if (a.name > b.name) return 1;
      return 0;
  });
}

function numberWithCommas(x) {
  // http://stackoverflow.com/questions/2901102/how-to-print-a-number-with-commas-as-thousands-separators-in-javascript
  return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}

// Create a material lite card.
function createCard(titleNode, descriptionNode, actionsNode) {
  var card = document.createElement('div');
  card.className = 'mdl-cell mdl-color--white mdl-grid mdl-card mdl-shadow--2dp';
  titleNode.className = 'mdl-card__title';
  actionsNode.className = 'mdl-card__supporting-text';
  descriptionNode.className = 'mdl-card__actions mdl-card--border';
  card.appendChild(titleNode);
  card.appendChild(actionsNode);
  card.appendChild(descriptionNode);
  return card;
}

function shortNameForRepoName(repoName) {
  return repoName.replace(/^material-motion-/, '').replace(/-android$/, '');
}

function preprocessRepo(repo) {
  repo['shortName'] = shortNameForRepoName(repo.name);

  var filterClass = 'tag-other';
  if (repo.name.match(/-android$/)) {
    filterClass = 'tag-android';
  } else if (repo.name.match(/-(objc|swift)$/)) {
    filterClass = 'tag-appleos';
  } else if (repo.name.match(/-(web|js)$/)) {
    filterClass = 'tag-web';
  }
  repo['filterClass'] = filterClass;
  return repo;
}

function preprocessIssue(issue) {
  var re = /repos\/(.+?)\/(.+?)$/;
  var m;

  if ((m = re.exec(issue.repository_url)) !== null) {
    if (m.index === re.lastIndex) {
      re.lastIndex++;
    }

    issue['repo_html_url'] = "https://github.com/" + m[1] + "/" + m[2];
    issue['repo'] = m[2];
  }

  issue['repoShortName'] = shortNameForRepoName(issue.repo);

  var filterClass = 'tag-other';
  if (issue.repo.match(/-android$/)) {
    filterClass = 'tag-android';
  } else if (issue.repo.match(/-(objc|swift)$/)) {
    filterClass = 'tag-appleos';
  } else if (issue.repo.match(/-(web|js)$/)) {
    filterClass = 'tag-web';
  }
  issue['filterClass'] = filterClass;
  return issue;
}

function shouldHideFilterableNode(object) {
  return 'tag-' + localStorage.getItem('filter') != object.filterClass;
}

function didCreateFilterableNode(object, node) {
  node.className += " filterable-node " + object.filterClass;
  if (shouldHideFilterableNode(object)) {
    $(node).hide();
  }
}

// Cached github request.
function requestGitHubAPI(path, data, callback) {
  if (typeof callback === 'undefined') {
    callback = data;
    data = undefined;
  }
  var cacheKey = path;
  if (data) {
    cacheKey += "::" + JSON.stringify(data);
  }
  var storage = localStorage.getItem(cacheKey);
  var storageTimestamp = localStorage.getItem(cacheKey + '.timestamp');
  if (storage && storageTimestamp && Date.now() - storageTimestamp < 5 * 60 * 1000 ) {
    callback.call(null, JSON.parse(storage));
    return;
  }
  $.ajax({
    type: 'get',
    url: 'https://api.github.com' + path,
    data: data,
    complete: function(xhr) {
      localStorage.setItem(cacheKey, JSON.stringify(xhr.responseJSON));
      localStorage.setItem(cacheKey + '.timestamp', Date.now());
      callback.call(null, xhr.responseJSON);
    }
  });
}

$(function() {
  // Add event listeners to each radio button.
  var radioButtons = document.filter.filterby;
  for(var i = 0; i < radioButtons.length; i++) {
    radioButtons[i].onclick = function() {
      filterDidChange(this)
    };
  }

  $(document).ready(function() {
    var filter = getParameterByName('filterby') || localStorage.getItem('filter');
    if (filter) {
      var radio = document.getElementById('filter-' + filter);
      radio.click();
      filterDidChange(radio);
    } else {
      var radio = document.getElementById('filter-android');
      radio.click();
      filterDidChange(radio);
    }

    document.getElementById('clear').onclick = function(event) {
      event.stopPropagation();
      localStorage.clear();
      window.location.reload();
    }
  });
});
<|endoftext|>


<commit_before>import Super from './super';
import * as u from './utils';

///////////////////////
///   THE NIPPLE    ///
///////////////////////

function Nipple (collection, options) {
    this.identifier = options.identifier;
    this.position = options.position;
    this.frontPosition = options.frontPosition;
    this.collection = collection;

    // Defaults
    this.defaults = {
        size: 100,
        threshold: 0.1,
        color: 'white',
        fadeTime: 250,
        dataOnly: false,
        restJoystick: true,
        restOpacity: 0.5,
        mode: 'dynamic',
        zone: document.body,
        lockX: false,
        lockY: false
    };

    this.config(options);

    // Overwrites
    if (this.options.mode === 'dynamic') {
        this.options.restOpacity = 0;
    }

    this.id = Nipple.id;
    Nipple.id += 1;
    this.buildEl()
        .stylize();

    // Nipple's API.
    this.instance = {
        el: this.ui.el,
        on: this.on.bind(this),
        off: this.off.bind(this),
        show: this.show.bind(this),
        hide: this.hide.bind(this),
        add: this.addToDom.bind(this),
        remove: this.removeFromDom.bind(this),
        destroy: this.destroy.bind(this),
        resetDirection: this.resetDirection.bind(this),
        computeDirection: this.computeDirection.bind(this),
        trigger: this.trigger.bind(this),
        position: this.position,
        frontPosition: this.frontPosition,
        ui: this.ui,
        identifier: this.identifier,
        id: this.id,
        options: this.options
    };

    return this.instance;
}

Nipple.prototype = new Super();
Nipple.constructor = Nipple;
Nipple.id = 0;

// Build the dom element of the Nipple instance.
Nipple.prototype.buildEl = function (options) {
    this.ui = {};

    if (this.options.dataOnly) {
        return this;
    }

    this.ui.el = document.createElement('div');
    this.ui.back = document.createElement('div');
    this.ui.front = document.createElement('div');

    this.ui.el.className = 'nipple collection_' + this.collection.id;
    this.ui.back.className = 'back';
    this.ui.front.className = 'front';

    this.ui.el.setAttribute('id', 'nipple_' + this.collection.id +
        '_' + this.id);

    this.ui.el.appendChild(this.ui.back);
    this.ui.el.appendChild(this.ui.front);

    return this;
};

// Apply CSS to the Nipple instance.
Nipple.prototype.stylize = function () {
    if (this.options.dataOnly) {
        return this;
    }
    var animTime = this.options.fadeTime + 'ms';
    var borderStyle = u.getVendorStyle('borderRadius', '50%');
    var transitStyle = u.getTransitionStyle('transition', 'opacity', animTime);
    var styles = {};
    styles.el = {
        position: 'absolute',
        opacity: this.options.restOpacity,
        display: 'block',
        'zIndex': 999
    };

    styles.back = {
        position: 'absolute',
        display: 'block',
        width: this.options.size + 'px',
        height: this.options.size + 'px',
        marginLeft: -this.options.size / 2 + 'px',
        marginTop: -this.options.size / 2 + 'px',
        background: this.options.color,
        'opacity': '.5'
    };

    styles.front = {
        width: this.options.size / 2 + 'px',
        height: this.options.size / 2 + 'px',
        position: 'absolute',
        display: 'block',
        marginLeft: -this.options.size / 4 + 'px',
        marginTop: -this.options.size / 4 + 'px',
        background: this.options.color,
        'opacity': '.5'
    };

    u.extend(styles.el, transitStyle);
    u.extend(styles.back, borderStyle);
    u.extend(styles.front, borderStyle);

    this.applyStyles(styles);

    return this;
};

Nipple.prototype.applyStyles = function (styles) {
    // Apply styles
    for (var i in this.ui) {
        if (this.ui.hasOwnProperty(i)) {
            for (var j in styles[i]) {
                this.ui[i].style[j] = styles[i][j];
            }
        }
    }

    return this;
};

// Inject the Nipple instance into DOM.
Nipple.prototype.addToDom = function () {
    // We're not adding it if we're dataOnly or already in dom.
    if (this.options.dataOnly || document.body.contains(this.ui.el)) {
        return this;
    }
    this.options.zone.appendChild(this.ui.el);
    return this;
};

// Remove the Nipple instance from DOM.
Nipple.prototype.removeFromDom = function () {
    if (this.options.dataOnly || !document.body.contains(this.ui.el)) {
        return this;
    }
    this.options.zone.removeChild(this.ui.el);
    return this;
};

// Entirely destroy this nipple
Nipple.prototype.destroy = function () {
    clearTimeout(this.removeTimeout);
    clearTimeout(this.showTimeout);
    clearTimeout(this.restTimeout);
    this.trigger('destroyed', this.instance);
    this.removeFromDom();
    this.off();
};

// Fade in the Nipple instance.
Nipple.prototype.show = function (cb) {
    var self = this;

    if (self.options.dataOnly) {
        return self;
    }

    clearTimeout(self.removeTimeout);
    clearTimeout(self.showTimeout);
    clearTimeout(self.restTimeout);

    self.addToDom();

    self.restCallback();

    setTimeout(function () {
        self.ui.el.style.opacity = 1;
    }, 0);

    self.showTimeout = setTimeout(function () {
        self.trigger('shown', self.instance);
        if (typeof cb === 'function') {
            cb.call(this);
        }
    }, self.options.fadeTime);

    return self;
};

// Fade out the Nipple instance.
Nipple.prototype.hide = function (cb) {
    var self = this;

    if (self.options.dataOnly) {
        return self;
    }

    self.ui.el.style.opacity = self.options.restOpacity;

    clearTimeout(self.removeTimeout);
    clearTimeout(self.showTimeout);
    clearTimeout(self.restTimeout);

    self.removeTimeout = setTimeout(
        function () {
            var display = self.options.mode === 'dynamic' ? 'none' : 'block';
            self.ui.el.style.display = display;
            if (typeof cb === 'function') {
                cb.call(self);
            }

            self.trigger('hidden', self.instance);
        },
        self.options.fadeTime
    );
    if (self.options.restJoystick) {
        self.restPosition();
    }

    return self;
};

Nipple.prototype.restPosition = function (cb) {
    var self = this;
    self.frontPosition = {
        x: 0,
        y: 0
    };
    var animTime = self.options.fadeTime + 'ms';

    var transitStyle = {};
    transitStyle.front = u.getTransitionStyle('transition',
        ['top', 'left'], animTime);

    var styles = {front: {}};
    styles.front = {
        left: self.frontPosition.x + 'px',
        top: self.frontPosition.y + 'px'
    };

    self.applyStyles(transitStyle);
    self.applyStyles(styles);

    self.restTimeout = setTimeout(
        function () {
            if (typeof cb === 'function') {
                cb.call(self);
            }
            self.restCallback();
        },
        self.options.fadeTime
    );
};

Nipple.prototype.restCallback = function () {
    var self = this;
    var transitStyle = {};
    transitStyle.front = u.getTransitionStyle('transition', 'none', '');
    self.applyStyles(transitStyle);
    self.trigger('rested', self.instance);
};

Nipple.prototype.resetDirection = function () {
    // Fully rebuild the object to let the iteration possible.
    this.direction = {
        x: false,
        y: false,
        angle: false
    };
};

Nipple.prototype.computeDirection = function (obj) {
    var rAngle = obj.angle.radian;
    var angle45 = Math.PI / 4;
    var angle90 = Math.PI / 2;
    var direction, directionX, directionY;

    // Angular direction
    //     \  UP /
    //      \   /
    // LEFT       RIGHT
    //      /   \
    //     /DOWN \
    //
    if (
        rAngle > angle45 &&
        rAngle < (angle45 * 3) &&
        !obj.lockX
    ) {
        direction = 'up';
    } else if (
        rAngle > -angle45 &&
        rAngle <= angle45 &&
        !obj.lockY
    ) {
        direction = 'left';
    } else if (
        rAngle > (-angle45 * 3) &&
        rAngle <= -angle45 &&
        !obj.lockX
    ) {
        direction = 'down';
    } else if (!obj.lockY) {
        direction = 'right';
    }

    // Plain direction
    //    UP                 |
    // _______               | RIGHT
    //                  LEFT |
    //   DOWN                |
    if (!obj.lockY) {
        if (rAngle > -angle90 && rAngle < angle90) {
            directionX = 'left';
        } else {
            directionX = 'right';
        }
    }

    if (!obj.lockX) {
        if (rAngle > 0) {
            directionY = 'up';
        } else {
            directionY = 'down';
        }
    }

    if (obj.force > this.options.threshold) {
        var oldDirection = {};
        var i;
        for (i in this.direction) {
            if (this.direction.hasOwnProperty(i)) {
                oldDirection[i] = this.direction[i];
            }
        }

        var same = {};

        this.direction = {
            x: directionX,
            y: directionY,
            angle: direction
        };

        obj.direction = this.direction;

        for (i in oldDirection) {
            if (oldDirection[i] === this.direction[i]) {
                same[i] = true;
            }
        }

        // If all 3 directions are the same, we don't trigger anything.
        if (same.x && same.y && same.angle) {
            return obj;
        }

        if (!same.x || !same.y) {
            this.trigger('plain', obj);
        }

        if (!same.x) {
            this.trigger('plain:' + directionX, obj);
        }

        if (!same.y) {
            this.trigger('plain:' + directionY, obj);
        }

        if (!same.angle) {
            this.trigger('dir dir:' + direction, obj);
        }
    }
    return obj;
};

export default Nipple;
<commit_msg>fix: reset direction when below the threshold<commit_after>import Super from './super';
import * as u from './utils';

///////////////////////
///   THE NIPPLE    ///
///////////////////////

function Nipple (collection, options) {
    this.identifier = options.identifier;
    this.position = options.position;
    this.frontPosition = options.frontPosition;
    this.collection = collection;

    // Defaults
    this.defaults = {
        size: 100,
        threshold: 0.1,
        color: 'white',
        fadeTime: 250,
        dataOnly: false,
        restJoystick: true,
        restOpacity: 0.5,
        mode: 'dynamic',
        zone: document.body,
        lockX: false,
        lockY: false
    };

    this.config(options);

    // Overwrites
    if (this.options.mode === 'dynamic') {
        this.options.restOpacity = 0;
    }

    this.id = Nipple.id;
    Nipple.id += 1;
    this.buildEl()
        .stylize();

    // Nipple's API.
    this.instance = {
        el: this.ui.el,
        on: this.on.bind(this),
        off: this.off.bind(this),
        show: this.show.bind(this),
        hide: this.hide.bind(this),
        add: this.addToDom.bind(this),
        remove: this.removeFromDom.bind(this),
        destroy: this.destroy.bind(this),
        resetDirection: this.resetDirection.bind(this),
        computeDirection: this.computeDirection.bind(this),
        trigger: this.trigger.bind(this),
        position: this.position,
        frontPosition: this.frontPosition,
        ui: this.ui,
        identifier: this.identifier,
        id: this.id,
        options: this.options
    };

    return this.instance;
}

Nipple.prototype = new Super();
Nipple.constructor = Nipple;
Nipple.id = 0;

// Build the dom element of the Nipple instance.
Nipple.prototype.buildEl = function (options) {
    this.ui = {};

    if (this.options.dataOnly) {
        return this;
    }

    this.ui.el = document.createElement('div');
    this.ui.back = document.createElement('div');
    this.ui.front = document.createElement('div');

    this.ui.el.className = 'nipple collection_' + this.collection.id;
    this.ui.back.className = 'back';
    this.ui.front.className = 'front';

    this.ui.el.setAttribute('id', 'nipple_' + this.collection.id +
        '_' + this.id);

    this.ui.el.appendChild(this.ui.back);
    this.ui.el.appendChild(this.ui.front);

    return this;
};

// Apply CSS to the Nipple instance.
Nipple.prototype.stylize = function () {
    if (this.options.dataOnly) {
        return this;
    }
    var animTime = this.options.fadeTime + 'ms';
    var borderStyle = u.getVendorStyle('borderRadius', '50%');
    var transitStyle = u.getTransitionStyle('transition', 'opacity', animTime);
    var styles = {};
    styles.el = {
        position: 'absolute',
        opacity: this.options.restOpacity,
        display: 'block',
        'zIndex': 999
    };

    styles.back = {
        position: 'absolute',
        display: 'block',
        width: this.options.size + 'px',
        height: this.options.size + 'px',
        marginLeft: -this.options.size / 2 + 'px',
        marginTop: -this.options.size / 2 + 'px',
        background: this.options.color,
        'opacity': '.5'
    };

    styles.front = {
        width: this.options.size / 2 + 'px',
        height: this.options.size / 2 + 'px',
        position: 'absolute',
        display: 'block',
        marginLeft: -this.options.size / 4 + 'px',
        marginTop: -this.options.size / 4 + 'px',
        background: this.options.color,
        'opacity': '.5'
    };

    u.extend(styles.el, transitStyle);
    u.extend(styles.back, borderStyle);
    u.extend(styles.front, borderStyle);

    this.applyStyles(styles);

    return this;
};

Nipple.prototype.applyStyles = function (styles) {
    // Apply styles
    for (var i in this.ui) {
        if (this.ui.hasOwnProperty(i)) {
            for (var j in styles[i]) {
                this.ui[i].style[j] = styles[i][j];
            }
        }
    }

    return this;
};

// Inject the Nipple instance into DOM.
Nipple.prototype.addToDom = function () {
    // We're not adding it if we're dataOnly or already in dom.
    if (this.options.dataOnly || document.body.contains(this.ui.el)) {
        return this;
    }
    this.options.zone.appendChild(this.ui.el);
    return this;
};

// Remove the Nipple instance from DOM.
Nipple.prototype.removeFromDom = function () {
    if (this.options.dataOnly || !document.body.contains(this.ui.el)) {
        return this;
    }
    this.options.zone.removeChild(this.ui.el);
    return this;
};

// Entirely destroy this nipple
Nipple.prototype.destroy = function () {
    clearTimeout(this.removeTimeout);
    clearTimeout(this.showTimeout);
    clearTimeout(this.restTimeout);
    this.trigger('destroyed', this.instance);
    this.removeFromDom();
    this.off();
};

// Fade in the Nipple instance.
Nipple.prototype.show = function (cb) {
    var self = this;

    if (self.options.dataOnly) {
        return self;
    }

    clearTimeout(self.removeTimeout);
    clearTimeout(self.showTimeout);
    clearTimeout(self.restTimeout);

    self.addToDom();

    self.restCallback();

    setTimeout(function () {
        self.ui.el.style.opacity = 1;
    }, 0);

    self.showTimeout = setTimeout(function () {
        self.trigger('shown', self.instance);
        if (typeof cb === 'function') {
            cb.call(this);
        }
    }, self.options.fadeTime);

    return self;
};

// Fade out the Nipple instance.
Nipple.prototype.hide = function (cb) {
    var self = this;

    if (self.options.dataOnly) {
        return self;
    }

    self.ui.el.style.opacity = self.options.restOpacity;

    clearTimeout(self.removeTimeout);
    clearTimeout(self.showTimeout);
    clearTimeout(self.restTimeout);

    self.removeTimeout = setTimeout(
        function () {
            var display = self.options.mode === 'dynamic' ? 'none' : 'block';
            self.ui.el.style.display = display;
            if (typeof cb === 'function') {
                cb.call(self);
            }

            self.trigger('hidden', self.instance);
        },
        self.options.fadeTime
    );
    if (self.options.restJoystick) {
        self.restPosition();
    }

    return self;
};

Nipple.prototype.restPosition = function (cb) {
    var self = this;
    self.frontPosition = {
        x: 0,
        y: 0
    };
    var animTime = self.options.fadeTime + 'ms';

    var transitStyle = {};
    transitStyle.front = u.getTransitionStyle('transition',
        ['top', 'left'], animTime);

    var styles = {front: {}};
    styles.front = {
        left: self.frontPosition.x + 'px',
        top: self.frontPosition.y + 'px'
    };

    self.applyStyles(transitStyle);
    self.applyStyles(styles);

    self.restTimeout = setTimeout(
        function () {
            if (typeof cb === 'function') {
                cb.call(self);
            }
            self.restCallback();
        },
        self.options.fadeTime
    );
};

Nipple.prototype.restCallback = function () {
    var self = this;
    var transitStyle = {};
    transitStyle.front = u.getTransitionStyle('transition', 'none', '');
    self.applyStyles(transitStyle);
    self.trigger('rested', self.instance);
};

Nipple.prototype.resetDirection = function () {
    // Fully rebuild the object to let the iteration possible.
    this.direction = {
        x: false,
        y: false,
        angle: false
    };
};

Nipple.prototype.computeDirection = function (obj) {
    var rAngle = obj.angle.radian;
    var angle45 = Math.PI / 4;
    var angle90 = Math.PI / 2;
    var direction, directionX, directionY;

    // Angular direction
    //     \  UP /
    //      \   /
    // LEFT       RIGHT
    //      /   \
    //     /DOWN \
    //
    if (
        rAngle > angle45 &&
        rAngle < (angle45 * 3) &&
        !obj.lockX
    ) {
        direction = 'up';
    } else if (
        rAngle > -angle45 &&
        rAngle <= angle45 &&
        !obj.lockY
    ) {
        direction = 'left';
    } else if (
        rAngle > (-angle45 * 3) &&
        rAngle <= -angle45 &&
        !obj.lockX
    ) {
        direction = 'down';
    } else if (!obj.lockY) {
        direction = 'right';
    }

    // Plain direction
    //    UP                 |
    // _______               | RIGHT
    //                  LEFT |
    //   DOWN                |
    if (!obj.lockY) {
        if (rAngle > -angle90 && rAngle < angle90) {
            directionX = 'left';
        } else {
            directionX = 'right';
        }
    }

    if (!obj.lockX) {
        if (rAngle > 0) {
            directionY = 'up';
        } else {
            directionY = 'down';
        }
    }

    if (obj.force > this.options.threshold) {
        var oldDirection = {};
        var i;
        for (i in this.direction) {
            if (this.direction.hasOwnProperty(i)) {
                oldDirection[i] = this.direction[i];
            }
        }

        var same = {};

        this.direction = {
            x: directionX,
            y: directionY,
            angle: direction
        };

        obj.direction = this.direction;

        for (i in oldDirection) {
            if (oldDirection[i] === this.direction[i]) {
                same[i] = true;
            }
        }

        // If all 3 directions are the same, we don't trigger anything.
        if (same.x && same.y && same.angle) {
            return obj;
        }

        if (!same.x || !same.y) {
            this.trigger('plain', obj);
        }

        if (!same.x) {
            this.trigger('plain:' + directionX, obj);
        }

        if (!same.y) {
            this.trigger('plain:' + directionY, obj);
        }

        if (!same.angle) {
            this.trigger('dir dir:' + direction, obj);
        }
    } else {
        this.resetDirection();
    }

    return obj;
};

export default Nipple;
<|endoftext|>


<commit_before>"use strict";

/* note, always put `common.js` first when including scripts */

var HTTP_UNAUTHORIZED = 401;
var HTTP_OK = 200;
var CSRF_REGEX = /name\='csrfmiddlewaretoken' value\='.*'/; //regex to find the csrf token
var LOGGED_IN_REGEX = /var username \= ".*"/;
var baseUrl = "http://localhost:8000";

var PROMPT_TEMPLATE_CACHE = {};

///////////////////URL BUILDERS///////////////////
function getCSRFLoginUrl() {
    return baseUrl + "/accounts/login/";
}

function getLoginUrl() {
    return baseUrl + "/api/v1/auth/login/";
}

function getLogoutUrl() {
    return baseUrl + "/api/v1/auth/logout/";
}

function getUserUrl(username) {
    return baseUrl + "/users/" + username;
}

/*
 * Given the response text of a webpage
 * try to parse the CSRF token out.
 */
function parseCSRFToken(data) {
    var match = data.match(CSRF_REGEX);
    if (match) {
        match = match[0];
        return match.slice(match.indexOf("value=") + 7, match.length - 1); // grab the csrf token
    } else {
        return null;
    }
}

/*
 * Given the repsonse text of a webpage
 * try to parse the username out
 */
function parseUsername(data) {
    var match = data.match(LOGGED_IN_REGEX);
    if (match) {
        match = match[0];
        var username = match.slice("var username = ".length + 1, match.length - 1);
        if (username === "") {
            return null;
        } else {
            return username;
        }
    } else {
        return null;
    }
}

function logErrors(jqXHR, textStatus, errorThrown) {
    console.log(jqXHR);
    console.log(textStatus);
    console.log(errorThrown);
}

/*
 * Parse urls
 * https://gist.github.com/jlong/2428561
 */
function getUrlParser(url) {
    var parser = document.createElement("a");
    parser.href = url;
    return parser;
}

function URI(url) {
    var parser = getUrlParser(url);
    this.protocol = parser.protocol; // => "http:"
    this.hostname = parser.hostname; // => "example.com"
    this.port = parser.port; // => "3000"
    this.pathname = parser.pathname; // => "/pathname/"
    this.search = parser.search; // => "?search=test"
    this.hash = parser.hash; // => "#hash"
    this.host = parser.host; // => "example.com:3000"
    parser.remove();
}

/////// TEMPLATING /////////////
function createMentionTag(data) {
    return data.replace(/(^|\W+)\@([\w\-]+)/gm, "$1@$2");
}

Object.size = function(obj) {
    var size = 0,
        key;
    for (key in obj) {
        if (obj.hasOwnProperty(key)) {
            size++;
        }
    }
    return size;
};

// List of HTML entities for escaping.
var unescapeMap = {
    "&": "&",
    "<": "<",
    ">": ">",
    """: '"',
    "'": "'",
    "`": "`"
};

// Functions for escaping and unescaping strings to/from HTML interpolation.
var createEscaper = function(map) {
    var escaper = function(match) {
        return map[match];
    };
    // Regexes for identifying a key that needs to be escaped
    var source = "(?:" + _.keys(map).join("|") + ")";
    var testRegexp = new RegExp(source);
    var replaceRegexp = new RegExp(source, "g");
    return function(string) {
        string = string === null ? "" : "" + string;
        return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
    };
};

var _unescape = createEscaper(unescapeMap);

function initTemplates(templatePage, templateCache) {
    var url = chrome.extension.getURL("../html/" + templatePage);
    var templates = $($.ajax({
        type: "GET",
        url: url,
        dataType: "html",
        async: false
    }).responseText);
    for (var i = 0; i < templates.length; i++) {
        var el = $(templates[i]);
        var id = el.attr("id");
        if (id !== undefined) {
            templateCache["#" + id] = el;
        }
    }
}

function getPromptTemplate(templateId, templateArgs) {
    return getTemplate(templateId, templateArgs, "prompt.html", PROMPT_TEMPLATE_CACHE);
}

function getTemplate(templateId, templateArgs, templatePage, templateCache) {
    templateArgs = templateArgs || {};
    if (!Object.size(templateCache)) {
        initTemplates(templatePage, templateCache);
    }
    var template = _unescape(templateCache[templateId].html());
    return $(_.template(template, templateArgs));
}

function hexToRgb(hex) {
  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result ? {
      r: Math.min(parseInt(result[1], 16) + 30, 255),
      g: Math.min(parseInt(result[2], 16) + 30, 255),
      b: Math.min(parseInt(result[3], 16) + 30, 255)
  } : null;
}

function muteColor(colorString) {
  var rgbString = '';

  if (colorString) {
    var rgb = hexToRgb(colorString);
    rgbString = "rgb(" + rgb.r + "," + rgb.g + "," + rgb.b + ")";
  }

  return rgbString;
}

function getHostName(url) {
    var match = url.match(/:\/\/(www[0-9]?\.)?(.[^/:]+)/i);
    if (match != null && match.length > 2 && typeof match[2] === 'string' && match[2].length > 0) {
        return match[2];
    }
    else {
        return null;
    }
}

function isInHighlightBlacklist(url) {
    for (let site of highlightBlacklist) {
        var re = new RegExp("(https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+" + site + "\.[^\s]{2,}|www\." + site + "\.[^\s]{2,}|https?:\/\/(?:www\.|(?!www))" + site + "\.[^\s]{2,}|www\." + site + "]\.[^\s]{2,})|https?:\/\/(?:www\.|(?!www))[a-zA-Z0-9][a-zA-Z0-9-]+\." + site + "\.[^\s]{2,}");
        if (re.test(url)) {
            return true;
        }
    }
    return false;
}

// Pulled from Alexa 100
var highlightBlacklist = new Set([
    "facebook",
    "twitter",
    "medium",
    "gmail",
    "google",
    "messenger",
    "stackoverflow",
    "youtube",
    "baidu",
    "reddit",
    "amazon",
    "instagram",
    "live",
    "linkedin",
    "netflix",
    "imgur",
    "ebay",
    "bing",
    "pinterest",
    "github",
    "dropbox",
    "craigslist",
    "soundcloud",
    "spotify",
]);
<commit_msg>Fix regex for blacklisting sites on Pano<commit_after>"use strict";

/* note, always put `common.js` first when including scripts */

var HTTP_UNAUTHORIZED = 401;
var HTTP_OK = 200;
var CSRF_REGEX = /name\='csrfmiddlewaretoken' value\='.*'/; //regex to find the csrf token
var LOGGED_IN_REGEX = /var username \= ".*"/;
var baseUrl = "http://localhost:8000";

var PROMPT_TEMPLATE_CACHE = {};

///////////////////URL BUILDERS///////////////////
function getCSRFLoginUrl() {
    return baseUrl + "/accounts/login/";
}

function getLoginUrl() {
    return baseUrl + "/api/v1/auth/login/";
}

function getLogoutUrl() {
    return baseUrl + "/api/v1/auth/logout/";
}

function getUserUrl(username) {
    return baseUrl + "/users/" + username;
}

/*
 * Given the response text of a webpage
 * try to parse the CSRF token out.
 */
function parseCSRFToken(data) {
    var match = data.match(CSRF_REGEX);
    if (match) {
        match = match[0];
        return match.slice(match.indexOf("value=") + 7, match.length - 1); // grab the csrf token
    } else {
        return null;
    }
}

/*
 * Given the repsonse text of a webpage
 * try to parse the username out
 */
function parseUsername(data) {
    var match = data.match(LOGGED_IN_REGEX);
    if (match) {
        match = match[0];
        var username = match.slice("var username = ".length + 1, match.length - 1);
        if (username === "") {
            return null;
        } else {
            return username;
        }
    } else {
        return null;
    }
}

function logErrors(jqXHR, textStatus, errorThrown) {
    console.log(jqXHR);
    console.log(textStatus);
    console.log(errorThrown);
}

/*
 * Parse urls
 * https://gist.github.com/jlong/2428561
 */
function getUrlParser(url) {
    var parser = document.createElement("a");
    parser.href = url;
    return parser;
}

function URI(url) {
    var parser = getUrlParser(url);
    this.protocol = parser.protocol; // => "http:"
    this.hostname = parser.hostname; // => "example.com"
    this.port = parser.port; // => "3000"
    this.pathname = parser.pathname; // => "/pathname/"
    this.search = parser.search; // => "?search=test"
    this.hash = parser.hash; // => "#hash"
    this.host = parser.host; // => "example.com:3000"
    parser.remove();
}

/////// TEMPLATING /////////////
function createMentionTag(data) {
    return data.replace(/(^|\W+)\@([\w\-]+)/gm, "$1@$2");
}

Object.size = function(obj) {
    var size = 0,
        key;
    for (key in obj) {
        if (obj.hasOwnProperty(key)) {
            size++;
        }
    }
    return size;
};

// List of HTML entities for escaping.
var unescapeMap = {
    "&": "&",
    "<": "<",
    ">": ">",
    """: '"',
    "'": "'",
    "`": "`"
};

// Functions for escaping and unescaping strings to/from HTML interpolation.
var createEscaper = function(map) {
    var escaper = function(match) {
        return map[match];
    };
    // Regexes for identifying a key that needs to be escaped
    var source = "(?:" + _.keys(map).join("|") + ")";
    var testRegexp = new RegExp(source);
    var replaceRegexp = new RegExp(source, "g");
    return function(string) {
        string = string === null ? "" : "" + string;
        return testRegexp.test(string) ? string.replace(replaceRegexp, escaper) : string;
    };
};

var _unescape = createEscaper(unescapeMap);

function initTemplates(templatePage, templateCache) {
    var url = chrome.extension.getURL("../html/" + templatePage);
    var templates = $($.ajax({
        type: "GET",
        url: url,
        dataType: "html",
        async: false
    }).responseText);
    for (var i = 0; i < templates.length; i++) {
        var el = $(templates[i]);
        var id = el.attr("id");
        if (id !== undefined) {
            templateCache["#" + id] = el;
        }
    }
}

function getPromptTemplate(templateId, templateArgs) {
    return getTemplate(templateId, templateArgs, "prompt.html", PROMPT_TEMPLATE_CACHE);
}

function getTemplate(templateId, templateArgs, templatePage, templateCache) {
    templateArgs = templateArgs || {};
    if (!Object.size(templateCache)) {
        initTemplates(templatePage, templateCache);
    }
    var template = _unescape(templateCache[templateId].html());
    return $(_.template(template, templateArgs));
}

function hexToRgb(hex) {
  var result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
  return result ? {
      r: Math.min(parseInt(result[1], 16) + 30, 255),
      g: Math.min(parseInt(result[2], 16) + 30, 255),
      b: Math.min(parseInt(result[3], 16) + 30, 255)
  } : null;
}

function muteColor(colorString) {
  var rgbString = '';

  if (colorString) {
    var rgb = hexToRgb(colorString);
    rgbString = "rgb(" + rgb.r + "," + rgb.g + "," + rgb.b + ")";
  }

  return rgbString;
}

function getHostName(url) {
    var match = url.match(/:\/\/(www[0-9]?\.)?(.[^/:]+)/i);
    if (match != null && match.length > 2 && typeof match[2] === 'string' && match[2].length > 0) {
        return match[2];
    }
    else {
        return null;
    }
}

function isInHighlightBlacklist(url) {
    for (let site of highlightBlacklist) {
        var re = new RegExp("^(?:http(?:s)?:\/\/)?(?:[^\.]+\.)?" + site + "\.com(?:.*)?$");
        if (re.test(url)) {
            console.log("BLOCKED! " + url);
            return true;
        }
    }

    return false;
}

// Pulled from Alexa 100
var highlightBlacklist = new Set([
    "facebook",
    "twitter",
    "medium",
    "gmail",
    "google",
    "messenger",
    "stackoverflow",
    "youtube",
    "baidu",
    "reddit",
    "amazon",
    "instagram",
    "live",
    "linkedin",
    "netflix",
    "imgur",
    "ebay",
    "bing",
    "pinterest",
    "github",
    "dropbox",
    "craigslist",
    "soundcloud",
    "spotify",
]);
<|endoftext|>


<commit_before>var xorshift = require('./xorshift');

var n = process.argv[2] || 5e7;

var tick = process.hrtime();
while(n--) {
   xorshift();
}
var tock = process.hrtime(tick);

console.log(tock[0] * 1e3 + tock[1] * 1e-6 + "ms");
<commit_msg>[bench] update benchmark to new api<commit_after>var xorshift = require('./xorshift');

var n = process.argv[2] || 5e7;

var tick = process.hrtime();
while(n--) {
   xorshift.random();
}
var tock = process.hrtime(tick);

console.log(tock[0] * 1e3 + tock[1] * 1e-6 + "ms");
<|endoftext|>


<commit_before>import { cdnUrl } from "../package.json";

const { hash } = window.location;
let config = {};
if (hash) {
  try {
    config = JSON.parse(decodeURIComponent(hash).slice(1));
  } catch (e) {
    console.error("Failed to decode config from hash: ", e);
  }
}

const or = (value, fallback) => (value === undefined ? fallback : value);

const assetBase = process.env.NODE_ENV === "production" ? cdnUrl : "";
// Turn on the incomplete playlist window
export const skinUrl = or(config.skinUrl, `${assetBase}skins/base-2.91.wsz`);
export const audioUrl = or(config.audioUrl, `${assetBase}mp3/llama-2.91.mp3`);
export const playlistEnabled = config.playlist || false;
export const noMarquee = config.noMarquee || false;
export const hideAbout = config.hideAbout || false;
export const initialState = config.initialState || undefined;
<commit_msg>Cachebust skin<commit_after>import { cdnUrl } from "../package.json";

const { hash } = window.location;
let config = {};
if (hash) {
  try {
    config = JSON.parse(decodeURIComponent(hash).slice(1));
  } catch (e) {
    console.error("Failed to decode config from hash: ", e);
  }
}

const or = (value, fallback) => (value === undefined ? fallback : value);

const assetBase = process.env.NODE_ENV === "production" ? cdnUrl : "";
// Turn on the incomplete playlist window
export const skinUrl = or(
  config.skinUrl,
  `${assetBase}skins/base-2.91.wsz?v=1`
);
export const audioUrl = or(config.audioUrl, `${assetBase}mp3/llama-2.91.mp3`);
export const playlistEnabled = config.playlist || false;
export const noMarquee = config.noMarquee || false;
export const hideAbout = config.hideAbout || false;
export const initialState = config.initialState || undefined;
<|endoftext|>