<commit_before>import lighthouse from './lighthouse.js';

var lbry = {
  isConnected: false,
  rootPath: '.',
  daemonConnectionString: 'http://localhost:5279/lbryapi',
  webUiUri: 'http://localhost:5279',
  colors: {
    primary: '#155B4A'
  },
  defaultClientSettings: {
    showNsfw: false,
  }
};

lbry.jsonrpc_call = function (connectionString, method, params, callback, errorCallback, connectFailedCallback, timeout) {
  var xhr = new XMLHttpRequest;
  if (typeof connectFailedCallback !== 'undefined') {
    if (timeout) {
      xhr.timeout = timeout;
    }

    xhr.addEventListener('error', function (e) {
      connectFailedCallback(e);
    });
    xhr.addEventListener('timeout', function() {
      connectFailedCallback(new Error('XMLHttpRequest connection timed out'));
    })
  }
  xhr.addEventListener('load', function() {
    var response = JSON.parse(xhr.responseText);

    if (response.error) {
      if (errorCallback) {
        errorCallback(response.error);
      } else {
        var errorEvent = new CustomEvent('unhandledError', {
          detail: {
            connectionString: connectionString,
            method: method,
            params: params,
            code: response.error.code,
            message: response.error.message,
            data: response.error.data
          }
        });
        document.dispatchEvent(errorEvent)
      }
    } else if (callback) {
      callback(response.result);
    }
  });

  if (connectFailedCallback) {
    xhr.addEventListener('error', function (event) {
      connectFailedCallback(event);
    });
  } else {
    xhr.addEventListener('error', function (event) {
      var errorEvent = new CustomEvent('unhandledError', {
        detail: {
          connectionString: connectionString,
          method: method,
          params: params,
          code: xhr.status,
          message: 'Connection to API server failed'
        }
      });
      document.dispatchEvent(errorEvent);
    });
  }

  xhr.open('POST', connectionString, true);
  xhr.send(JSON.stringify({
    'jsonrpc': '2.0',
    'method': method,
    'params': params,
    'id': 0
  }));
}

lbry.call = function (method, params, callback, errorCallback, connectFailedCallback) {
  lbry.jsonrpc_call(lbry.daemonConnectionString, method, [params], callback, errorCallback, connectFailedCallback);
}

//core
lbry.connect = function(callback)
{
  // Check every half second to see if the daemon's running.
  // Returns true to callback once connected, or false if it takes too long and we give up.
  function checkDaemonRunning(tryNum=0) {
    lbry.daemonRunningStatus(function (runningStatus) {
      if (runningStatus) {
        lbry.isConnected = true;
        callback(true);
      } else {
        if (tryNum <= 600) { // Move # of tries into constant or config option
          setTimeout(function () {
            checkDaemonRunning(tryNum + 1);
          }, 500);
        } else {
          callback(false);
        }
      }
    });
  }
  checkDaemonRunning();
}

lbry.daemonRunningStatus = function (callback) {
  // Returns true/false whether the daemon is running (i.e. fully conncected to the network),
  // or null if the AJAX connection to the daemon fails.

  lbry.call('is_running', {}, callback, null, function () {
    callback(null);
  });
};

lbry.getDaemonStatus = function (callback) {
  lbry.call('daemon_status', {}, callback);
};

lbry.getStartNotice = function(callback) {
  lbry.call('get_start_notice', {}, callback);
}

lbry.checkFirstRun = function(callback) {
  lbry.call('is_first_run', {}, callback);
}

lbry.getNewAddress = function(callback) {
  lbry.call('get_new_address', {}, callback);
}

lbry.checkAddressIsMine = function(address, callback) {
  lbry.call('address_is_mine', {address: address}, callback);
}

lbry.getDaemonSettings = function(callback) {
  lbry.call('get_settings', {}, callback);
}

lbry.setDaemonSettings = function(settings, callback) {
  lbry.call('set_settings', settings, callback);
}

lbry.setDaemonSetting = function(setting, value, callback) {
  var setSettingsArgs = {};
  setSettingsArgs[setting] = value;
  lbry.call('set_settings', setSettingsArgs, callback)
}

lbry.getBalance = function(callback)
{
  lbry.call("get_balance", {}, callback);
}

lbry.sendToAddress = function(amount, address, callback, errorCallback)
{
  lbry.call("send_amount_to_address", { "amount" : amount, "address": address }, callback, errorCallback);
}

lbry.resolveName = function(name, callback) {
  lbry.call('resolve_name', { 'name': name }, callback, () => {
    // For now, assume any error means the name was not resolved
    callback(null);
  });
}

lbry.getStream = function(name, callback) {
  lbry.call('get', { 'name': name }, callback);
};

lbry.getClaimInfo = function(name, callback) {
  lbry.call('get_claim_info', { name: name }, callback);
}

lbry.getMyClaim = function(name, callback) {
  lbry.call('get_my_claim', { name: name }, callback);
}

lbry.getKeyFee = function(name, callback) {
  lbry.call('get_est_cost', { name: name }, callback);
}

lbry.getTotalCost = function(name, size, callback) {
  lbry.call('get_est_cost', {
    name: name,
    size: size,
  }, callback);
}

lbry.getPeersForBlobHash = function(blobHash, callback) {
  lbry.call('get_peers_for_hash', { blob_hash: blobHash }, callback)
}

lbry.getCostInfoForName = function(name, callback) {
  /**
   * Takes a LBRY name; will first try and calculate a total cost using
   * Lighthouse. If Lighthouse can't be reached, it just retrives the
   * key fee.
   *
   * Returns an object with members:
   *   - cost: Number; the calculated cost of the name
   *   - includes_data: Boolean; indicates whether or not the data fee info
   *     from Lighthouse is included.
   */
  function getCostWithData(name, size, callback) {
    lbry.getTotalCost(name, size, (cost) => {
      callback({
        cost: cost,
        includesData: true,
      });
    });
  }

  function getCostNoData(name, callback) {
    lbry.getKeyFee(name, (cost) => {
      callback({
        cost: cost,
        includesData: false,
      });
    });
  }

  lighthouse.getSizeForName(name, (size) => {
    getCostWithData(name, size, callback);
  }, () => {
    getCostNoData(name, callback);
  }, () => {
    getCostNoData(name, callback);
  });
}

lbry.getFileStatus = function(name, callback) {
  lbry.call('get_lbry_file', { 'name': name }, callback);
}

lbry.getFilesInfo = function(callback) {
  lbry.call('get_lbry_files', {}, callback);
}

lbry.getFileInfoByName = function(name, callback) {
  lbry.call('get_lbry_file', {name: name}, callback);
}

lbry.getFileInfoBySdHash = function(sdHash, callback) {
  lbry.call('get_lbry_file', {sd_hash: sdHash}, callback);
}

lbry.getFileInfoByFilename = function(filename, callback) {
  lbry.call('get_lbry_file', {file_name: filename}, callback);
}

lbry.getMyClaims = function(callback) {
  lbry.call('get_name_claims', {}, callback);
}

lbry.startFile = function(name, callback) {
  lbry.call('start_lbry_file', { name: name }, callback);
}

lbry.stopFile = function(name, callback) {
  lbry.call('stop_lbry_file', { name: name }, callback);
}

lbry.deleteFile = function(name, deleteTargetFile=true, callback) {
  lbry.call('delete_lbry_file', {
    name: name,
    delete_target_file: deleteTargetFile,
  }, callback);
}

lbry.revealFile = function(path, callback) {
  lbry.call('reveal', { path: path }, callback);
}

lbry.getFileInfoWhenListed = function(name, callback, timeoutCallback, tryNum=0) {
  // Calls callback with file info when it appears in the list of files returned by lbry.getFilesInfo().
  // If timeoutCallback is provided, it will be called if the file fails to appear.
  lbry.getFilesInfo(function(filesInfo) {
    for (var fileInfo of filesInfo) {
      if (fileInfo.lbry_uri == name) {
        callback(fileInfo);
        return;
      }
    }

    if (tryNum <= 200) {
      setTimeout(function() { lbry.getFileInfoWhenListed(name, callback, timeoutCallback, tryNum + 1) }, 250);
    } else if (timeoutCallback) {
      timeoutCallback();
    }
  });
}

lbry.publish = function(params, fileListedCallback, publishedCallback, errorCallback) {
  // Publishes a file.
  // The optional fileListedCallback is called when the file becomes available in
  // lbry.getFilesInfo() during the publish process.

  // Use ES6 named arguments instead of directly passing param dict?
  lbry.call('publish', params, publishedCallback, (errorInfo) => {
    errorCallback({
      name: fault.fault,
      message: fault.faultString,
    });
  });
  if (fileListedCallback) {
    lbry.getFileInfoWhenListed(params.name, function(fileInfo) {
      fileListedCallback(fileInfo);
    });
  }
}

lbry.getVersionInfo = function(callback) {
  lbry.call('version', {}, callback);
};

lbry.checkNewVersionAvailable = function(callback) {
  lbry.call('version', {}, function(versionInfo) {
    var ver = versionInfo.lbrynet_version.split('.');

    var maj = parseInt(ver[0]),
        min = parseInt(ver[1]),
        patch = parseInt(ver[2]);

    var remoteVer = versionInfo.remote_lbrynet.split('.');
    var remoteMaj = parseInt(remoteVer[0]),
        remoteMin = parseInt(remoteVer[1]),
        remotePatch = parseInt(remoteVer[2]);

    if (maj < remoteMaj) {
      var newVersionAvailable = true;
    } else if (maj == remoteMaj) {
      if (min < remoteMin) {
        var newVersionAvailable = true;
      } else if (min == remoteMin) {
        var newVersionAvailable = (patch < remotePatch);
      } else {
        var newVersionAvailable = false;
      }
    } else {
      var newVersionAvailable = false;
    }
    callback(newVersionAvailable);
  }, function(err) {
    if (err.fault == 'NoSuchFunction') {
      // Really old daemon that can't report a version
      callback(true);
    }
  });
}

lbry.getClientSettings = function() {
  var outSettings = {};
  for (let setting of Object.keys(lbry.defaultClientSettings)) {
    var localStorageVal = localStorage.getItem('setting_' + setting);
    outSettings[setting] = (localStorageVal === null ? lbry.defaultClientSettings[setting] : JSON.parse(localStorageVal));
  }
  return outSettings;
}

lbry.getClientSetting = function(setting) {
  var localStorageVal = localStorage.getItem('setting_' + setting);
  return (localStorageVal === null ? lbry.defaultClientSettings[setting] : JSON.parse(localStorageVal));
}

lbry.setClientSettings = function(settings) {
  for (let setting of Object.keys(settings)) {
    lbry.setClientSetting(setting, settings[setting]);
  }
}

lbry.setClientSetting = function(setting, value) {
  return localStorage.setItem('setting_' + setting, JSON.stringify(value));
}

lbry.getSessionInfo = function(callback) {
  lbry.call('get_lbry_session_info', {}, callback);
}

lbry.reportBug = function(message, callback) {
  lbry.call('upload_log', {
    name_prefix: 'report',
    exclude_previous: false,
    force: true,
    message: message
  }, callback);
}

//utilities
lbry.formatCredits = function(amount, precision)
{
  return amount.toFixed(precision || 1).replace(/\.?0+$/, '');
}

lbry.formatName = function(name) {
  // Converts LBRY name to standard format (all lower case, no special characters, spaces replaced by dashes)
  name = name.replace('/\s+/g', '-');
  name = name.toLowerCase().replace(/[^a-z0-9\-]/g, '');
  return name;
}

lbry.nameIsValid = function(name, checkCase=true) {
  const regexp = new RegExp('^[a-z0-9-]+$', checkCase ? '' : 'i');
  return regexp.test(name);
}

lbry.loadJs = function(src, type, onload)
{
  var lbryScriptTag = document.getElementById('lbry'),
      newScriptTag = document.createElement('script'),
      type = type || 'text/javascript';

  newScriptTag.src = src;
  newScriptTag.type = type;
  if (onload)
  {
    newScriptTag.onload = onload;
  }
  lbryScriptTag.parentNode.insertBefore(newScriptTag, lbryScriptTag);
}

lbry.imagePath = function(file)
{
  return lbry.rootPath + '/img/' + file;
}

lbry.getMediaType = function(contentType, fileName) {
  if (contentType) {
    return /^[^/]+/.exec(contentType);
  } else if (fileName) {
    var dotIndex = fileName.lastIndexOf('.');
    if (dotIndex == -1) {
      return 'unknown';
    }

    var ext = fileName.substr(dotIndex + 1);
    if (/^mp4|mov|m4v|flv|f4v$/i.test(ext)) {
      return 'video';
    } else if (/^mp3|m4a|aac|wav|flac|ogg$/i.test(ext)) {
      return 'audio';
    } else if (/^html|htm|pdf|odf|doc|docx|md|markdown|txt$/i.test(ext)) {
      return 'document';
    } else {
      return 'unknown';
    }
  } else {
    return 'unknown';
  }
}

lbry.stop = function(callback) {
  lbry.call('stop', {}, callback);
};

export default lbry;
<commit_msg>Add developer settings to lbry.js<commit_after>import lighthouse from './lighthouse.js';

var lbry = {
  isConnected: false,
  rootPath: '.',
  daemonConnectionString: 'http://localhost:5279/lbryapi',
  webUiUri: 'http://localhost:5279',
  colors: {
    primary: '#155B4A'
  },
  defaultClientSettings: {
    showNsfw: false,
    debug: false,
    useCustomLighthouseServers: false,
    customLighthouseServers: [],
  }
};

lbry.jsonrpc_call = function (connectionString, method, params, callback, errorCallback, connectFailedCallback, timeout) {
  var xhr = new XMLHttpRequest;
  if (typeof connectFailedCallback !== 'undefined') {
    if (timeout) {
      xhr.timeout = timeout;
    }

    xhr.addEventListener('error', function (e) {
      connectFailedCallback(e);
    });
    xhr.addEventListener('timeout', function() {
      connectFailedCallback(new Error('XMLHttpRequest connection timed out'));
    })
  }
  xhr.addEventListener('load', function() {
    var response = JSON.parse(xhr.responseText);

    if (response.error) {
      if (errorCallback) {
        errorCallback(response.error);
      } else {
        var errorEvent = new CustomEvent('unhandledError', {
          detail: {
            connectionString: connectionString,
            method: method,
            params: params,
            code: response.error.code,
            message: response.error.message,
            data: response.error.data
          }
        });
        document.dispatchEvent(errorEvent)
      }
    } else if (callback) {
      callback(response.result);
    }
  });

  if (connectFailedCallback) {
    xhr.addEventListener('error', function (event) {
      connectFailedCallback(event);
    });
  } else {
    xhr.addEventListener('error', function (event) {
      var errorEvent = new CustomEvent('unhandledError', {
        detail: {
          connectionString: connectionString,
          method: method,
          params: params,
          code: xhr.status,
          message: 'Connection to API server failed'
        }
      });
      document.dispatchEvent(errorEvent);
    });
  }

  xhr.open('POST', connectionString, true);
  xhr.send(JSON.stringify({
    'jsonrpc': '2.0',
    'method': method,
    'params': params,
    'id': 0
  }));
}

lbry.call = function (method, params, callback, errorCallback, connectFailedCallback) {
  lbry.jsonrpc_call(lbry.daemonConnectionString, method, [params], callback, errorCallback, connectFailedCallback);
}

//core
lbry.connect = function(callback)
{
  // Check every half second to see if the daemon's running.
  // Returns true to callback once connected, or false if it takes too long and we give up.
  function checkDaemonRunning(tryNum=0) {
    lbry.daemonRunningStatus(function (runningStatus) {
      if (runningStatus) {
        lbry.isConnected = true;
        callback(true);
      } else {
        if (tryNum <= 600) { // Move # of tries into constant or config option
          setTimeout(function () {
            checkDaemonRunning(tryNum + 1);
          }, 500);
        } else {
          callback(false);
        }
      }
    });
  }
  checkDaemonRunning();
}

lbry.daemonRunningStatus = function (callback) {
  // Returns true/false whether the daemon is running (i.e. fully conncected to the network),
  // or null if the AJAX connection to the daemon fails.

  lbry.call('is_running', {}, callback, null, function () {
    callback(null);
  });
};

lbry.getDaemonStatus = function (callback) {
  lbry.call('daemon_status', {}, callback);
};

lbry.getStartNotice = function(callback) {
  lbry.call('get_start_notice', {}, callback);
}

lbry.checkFirstRun = function(callback) {
  lbry.call('is_first_run', {}, callback);
}

lbry.getNewAddress = function(callback) {
  lbry.call('get_new_address', {}, callback);
}

lbry.checkAddressIsMine = function(address, callback) {
  lbry.call('address_is_mine', {address: address}, callback);
}

lbry.getDaemonSettings = function(callback) {
  lbry.call('get_settings', {}, callback);
}

lbry.setDaemonSettings = function(settings, callback) {
  lbry.call('set_settings', settings, callback);
}

lbry.setDaemonSetting = function(setting, value, callback) {
  var setSettingsArgs = {};
  setSettingsArgs[setting] = value;
  lbry.call('set_settings', setSettingsArgs, callback)
}

lbry.getBalance = function(callback)
{
  lbry.call("get_balance", {}, callback);
}

lbry.sendToAddress = function(amount, address, callback, errorCallback)
{
  lbry.call("send_amount_to_address", { "amount" : amount, "address": address }, callback, errorCallback);
}

lbry.resolveName = function(name, callback) {
  lbry.call('resolve_name', { 'name': name }, callback, () => {
    // For now, assume any error means the name was not resolved
    callback(null);
  });
}

lbry.getStream = function(name, callback) {
  lbry.call('get', { 'name': name }, callback);
};

lbry.getClaimInfo = function(name, callback) {
  lbry.call('get_claim_info', { name: name }, callback);
}

lbry.getMyClaim = function(name, callback) {
  lbry.call('get_my_claim', { name: name }, callback);
}

lbry.getKeyFee = function(name, callback) {
  lbry.call('get_est_cost', { name: name }, callback);
}

lbry.getTotalCost = function(name, size, callback) {
  lbry.call('get_est_cost', {
    name: name,
    size: size,
  }, callback);
}

lbry.getPeersForBlobHash = function(blobHash, callback) {
  lbry.call('get_peers_for_hash', { blob_hash: blobHash }, callback)
}

lbry.getCostInfoForName = function(name, callback) {
  /**
   * Takes a LBRY name; will first try and calculate a total cost using
   * Lighthouse. If Lighthouse can't be reached, it just retrives the
   * key fee.
   *
   * Returns an object with members:
   *   - cost: Number; the calculated cost of the name
   *   - includes_data: Boolean; indicates whether or not the data fee info
   *     from Lighthouse is included.
   */
  function getCostWithData(name, size, callback) {
    lbry.getTotalCost(name, size, (cost) => {
      callback({
        cost: cost,
        includesData: true,
      });
    });
  }

  function getCostNoData(name, callback) {
    lbry.getKeyFee(name, (cost) => {
      callback({
        cost: cost,
        includesData: false,
      });
    });
  }

  lighthouse.getSizeForName(name, (size) => {
    getCostWithData(name, size, callback);
  }, () => {
    getCostNoData(name, callback);
  }, () => {
    getCostNoData(name, callback);
  });
}

lbry.getFileStatus = function(name, callback) {
  lbry.call('get_lbry_file', { 'name': name }, callback);
}

lbry.getFilesInfo = function(callback) {
  lbry.call('get_lbry_files', {}, callback);
}

lbry.getFileInfoByName = function(name, callback) {
  lbry.call('get_lbry_file', {name: name}, callback);
}

lbry.getFileInfoBySdHash = function(sdHash, callback) {
  lbry.call('get_lbry_file', {sd_hash: sdHash}, callback);
}

lbry.getFileInfoByFilename = function(filename, callback) {
  lbry.call('get_lbry_file', {file_name: filename}, callback);
}

lbry.getMyClaims = function(callback) {
  lbry.call('get_name_claims', {}, callback);
}

lbry.startFile = function(name, callback) {
  lbry.call('start_lbry_file', { name: name }, callback);
}

lbry.stopFile = function(name, callback) {
  lbry.call('stop_lbry_file', { name: name }, callback);
}

lbry.deleteFile = function(name, deleteTargetFile=true, callback) {
  lbry.call('delete_lbry_file', {
    name: name,
    delete_target_file: deleteTargetFile,
  }, callback);
}

lbry.revealFile = function(path, callback) {
  lbry.call('reveal', { path: path }, callback);
}

lbry.getFileInfoWhenListed = function(name, callback, timeoutCallback, tryNum=0) {
  // Calls callback with file info when it appears in the list of files returned by lbry.getFilesInfo().
  // If timeoutCallback is provided, it will be called if the file fails to appear.
  lbry.getFilesInfo(function(filesInfo) {
    for (var fileInfo of filesInfo) {
      if (fileInfo.lbry_uri == name) {
        callback(fileInfo);
        return;
      }
    }

    if (tryNum <= 200) {
      setTimeout(function() { lbry.getFileInfoWhenListed(name, callback, timeoutCallback, tryNum + 1) }, 250);
    } else if (timeoutCallback) {
      timeoutCallback();
    }
  });
}

lbry.publish = function(params, fileListedCallback, publishedCallback, errorCallback) {
  // Publishes a file.
  // The optional fileListedCallback is called when the file becomes available in
  // lbry.getFilesInfo() during the publish process.

  // Use ES6 named arguments instead of directly passing param dict?
  lbry.call('publish', params, publishedCallback, (errorInfo) => {
    errorCallback({
      name: fault.fault,
      message: fault.faultString,
    });
  });
  if (fileListedCallback) {
    lbry.getFileInfoWhenListed(params.name, function(fileInfo) {
      fileListedCallback(fileInfo);
    });
  }
}

lbry.getVersionInfo = function(callback) {
  lbry.call('version', {}, callback);
};

lbry.checkNewVersionAvailable = function(callback) {
  lbry.call('version', {}, function(versionInfo) {
    var ver = versionInfo.lbrynet_version.split('.');

    var maj = parseInt(ver[0]),
        min = parseInt(ver[1]),
        patch = parseInt(ver[2]);

    var remoteVer = versionInfo.remote_lbrynet.split('.');
    var remoteMaj = parseInt(remoteVer[0]),
        remoteMin = parseInt(remoteVer[1]),
        remotePatch = parseInt(remoteVer[2]);

    if (maj < remoteMaj) {
      var newVersionAvailable = true;
    } else if (maj == remoteMaj) {
      if (min < remoteMin) {
        var newVersionAvailable = true;
      } else if (min == remoteMin) {
        var newVersionAvailable = (patch < remotePatch);
      } else {
        var newVersionAvailable = false;
      }
    } else {
      var newVersionAvailable = false;
    }
    callback(newVersionAvailable);
  }, function(err) {
    if (err.fault == 'NoSuchFunction') {
      // Really old daemon that can't report a version
      callback(true);
    }
  });
}

lbry.getClientSettings = function() {
  var outSettings = {};
  for (let setting of Object.keys(lbry.defaultClientSettings)) {
    var localStorageVal = localStorage.getItem('setting_' + setting);
    outSettings[setting] = (localStorageVal === null ? lbry.defaultClientSettings[setting] : JSON.parse(localStorageVal));
  }
  return outSettings;
}

lbry.getClientSetting = function(setting) {
  var localStorageVal = localStorage.getItem('setting_' + setting);
  return (localStorageVal === null ? lbry.defaultClientSettings[setting] : JSON.parse(localStorageVal));
}

lbry.setClientSettings = function(settings) {
  for (let setting of Object.keys(settings)) {
    lbry.setClientSetting(setting, settings[setting]);
  }
}

lbry.setClientSetting = function(setting, value) {
  return localStorage.setItem('setting_' + setting, JSON.stringify(value));
}

lbry.getSessionInfo = function(callback) {
  lbry.call('get_lbry_session_info', {}, callback);
}

lbry.reportBug = function(message, callback) {
  lbry.call('upload_log', {
    name_prefix: 'report',
    exclude_previous: false,
    force: true,
    message: message
  }, callback);
}

//utilities
lbry.formatCredits = function(amount, precision)
{
  return amount.toFixed(precision || 1).replace(/\.?0+$/, '');
}

lbry.formatName = function(name) {
  // Converts LBRY name to standard format (all lower case, no special characters, spaces replaced by dashes)
  name = name.replace('/\s+/g', '-');
  name = name.toLowerCase().replace(/[^a-z0-9\-]/g, '');
  return name;
}

lbry.nameIsValid = function(name, checkCase=true) {
  const regexp = new RegExp('^[a-z0-9-]+$', checkCase ? '' : 'i');
  return regexp.test(name);
}

lbry.loadJs = function(src, type, onload)
{
  var lbryScriptTag = document.getElementById('lbry'),
      newScriptTag = document.createElement('script'),
      type = type || 'text/javascript';

  newScriptTag.src = src;
  newScriptTag.type = type;
  if (onload)
  {
    newScriptTag.onload = onload;
  }
  lbryScriptTag.parentNode.insertBefore(newScriptTag, lbryScriptTag);
}

lbry.imagePath = function(file)
{
  return lbry.rootPath + '/img/' + file;
}

lbry.getMediaType = function(contentType, fileName) {
  if (contentType) {
    return /^[^/]+/.exec(contentType);
  } else if (fileName) {
    var dotIndex = fileName.lastIndexOf('.');
    if (dotIndex == -1) {
      return 'unknown';
    }

    var ext = fileName.substr(dotIndex + 1);
    if (/^mp4|mov|m4v|flv|f4v$/i.test(ext)) {
      return 'video';
    } else if (/^mp3|m4a|aac|wav|flac|ogg$/i.test(ext)) {
      return 'audio';
    } else if (/^html|htm|pdf|odf|doc|docx|md|markdown|txt$/i.test(ext)) {
      return 'document';
    } else {
      return 'unknown';
    }
  } else {
    return 'unknown';
  }
}

lbry.stop = function(callback) {
  lbry.call('stop', {}, callback);
};

export default lbry;
<|endoftext|>


<commit_before>/* =========================================================
 * bootstrap-slider.js v2.0.0
 * http://www.eyecon.ro/bootstrap-slider
 * =========================================================
 * Copyright 2012 Stefan Petre
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================= */

!function( $ ) {

	var Slider = function(element, options) {
		this.element = $(element);
		this.picker = $('
'+
							'
'+
								'

'+
								'

'+
								'

'+
							'
'+
							'

'+
						'
')
							.insertBefore(this.element)
							.append(this.element);
		this.id = this.element.data('slider-id')||options.id;
		if (this.id) {
			this.picker[0].id = this.id;
		}

		if (typeof Modernizr !== 'undefined' && Modernizr.touch) {
			this.touchCapable = true;
		}

		var tooltip = this.element.data('slider-tooltip')||options.tooltip;

		this.tooltip = this.picker.find('.tooltip');
		this.tooltipInner = this.tooltip.find('div.tooltip-inner');

		this.orientation = this.element.data('slider-orientation')||options.orientation;
		switch(this.orientation) {
			case 'vertical':
				this.picker.addClass('slider-vertical');
				this.stylePos = 'top';
				this.mousePos = 'pageY';
				this.sizePos = 'offsetHeight';
				this.tooltip.addClass('right')[0].style.left = '100%';
				break;
			default:
				this.picker
					.addClass('slider-horizontal')
					.css('width', '100%');
				this.orientation = 'horizontal';
				this.stylePos = 'left';
				this.mousePos = 'pageX';
				this.sizePos = 'offsetWidth';
				this.tooltip.addClass('top')[0].style.top = -this.tooltip.outerHeight() - 14 + 'px';
				break;
		}

		this.min = this.element.data('slider-min')||options.min;
		this.max = this.element.data('slider-max')||options.max;
		this.step = this.element.data('slider-step')||options.step;
		this.value = this.element.data('slider-value')||options.value;
		if (this.value[1]) {
			this.range = true;
		}

		this.selection = this.element.data('slider-selection')||options.selection;
		this.selectionEl = this.picker.find('.slider-selection');
		if (this.selection === 'none') {
			this.selectionEl.addClass('hide');
		}
		this.selectionElStyle = this.selectionEl[0].style;

		this.handle1 = this.picker.find('.slider-handle:first');
		this.handle1Stype = this.handle1[0].style;
		this.handle1Value = this.picker.find('.slider-handle:first .slider-value');
		this.handle2 = this.picker.find('.slider-handle:last');
		this.handle2Stype = this.handle2[0].style;
		this.handle2Value = this.picker.find('.slider-handle:last .slider-value');

		this.handle = this.element.data('slider-handle')||options.handle;
		switch(this.handle) {
			case 'round':
			case 'triangle':
			case 'rectangular':
				this.handle1.addClass(this.handle);
				this.handle2.addClass(this.handle);
				break
		}

		if (this.range) {
			this.value[0] = Math.max(this.min, Math.min(this.max, this.value[0]));
			this.value[1] = Math.max(this.min, Math.min(this.max, this.value[1]));
		} else {
			this.value = [ Math.max(this.min, Math.min(this.max, this.value))];
			this.handle2.addClass('hide');
			if (this.selection == 'after') {
				this.value[1] = this.max;
			} else {
				this.value[1] = this.min;
			}
		}
		this.diff = this.max - this.min;
		this.percentage = [
			(this.value[0]-this.min)*100/this.diff,
			(this.value[1]-this.min)*100/this.diff,
			this.step*100/this.diff
		];

		this.offset = this.picker.offset();
		this.size = this.picker[0][this.sizePos];

		this.formater = options.formater;

		this.layout();

		if (this.touchCapable) {
			// Touch: Bind touch events:
			this.picker.on({
				touchstart: $.proxy(this.mousedown, this)
			});
		} else {
			this.picker.on({
				mousedown: $.proxy(this.mousedown, this)
			});
		}

		if (tooltip === 'show') {
			this.picker.on({
				mouseenter: $.proxy(this.showTooltip, this),
				mouseleave: $.proxy(this.hideTooltip, this)
			});
		} else {
			this.tooltip.addClass('hide');
		}
	};

	Slider.prototype = {
		constructor: Slider,

		over: false,
		inDrag: false,

		showTooltip: function(){
			this.tooltip.addClass('in');
			//var left = Math.round(this.percent*this.width);
			//this.tooltip.css('left', left - this.tooltip.outerWidth()/2);
			this.over = true;
			this.handle1Value.text('');
		},

		hideTooltip: function(){
			if (this.inDrag === false) {
				this.tooltip.removeClass('in');
			}
			this.over = false;
			this.handle1Value.text(this.getValue());
		},

		layout: function(){
			this.handle1Stype[this.stylePos] = this.percentage[0]+'%';
			this.handle2Stype[this.stylePos] = this.percentage[1]+'%';
			if (this.orientation == 'vertical') {
				this.selectionElStyle.top = Math.min(this.percentage[0], this.percentage[1]) +'%';
				this.selectionElStyle.height = Math.abs(this.percentage[0] - this.percentage[1]) +'%';
			} else {
				this.selectionElStyle.left = Math.min(this.percentage[0], this.percentage[1]) +'%';
				this.selectionElStyle.width = Math.abs(this.percentage[0] - this.percentage[1]) +'%';
			}
			if (this.range) {
				this.tooltipInner.text(
					this.formater(this.value[0]) +
					' : ' +
					this.formater(this.value[1])
				);
				this.tooltip[0].style[this.stylePos] = this.size * (this.percentage[0] + (this.percentage[1] - this.percentage[0])/2)/100 - (this.orientation === 'vertical' ? this.tooltip.outerHeight()/2 : this.tooltip.outerWidth()/2) +'px';
			} else {
				this.tooltipInner.text(
					this.formater(this.value[0])
				);
				this.tooltip[0].style[this.stylePos] = this.size * this.percentage[0]/100 - (this.orientation === 'vertical' ? this.tooltip.outerHeight()/2 : this.tooltip.outerWidth()/2) +'px';
			}
		},

		mousedown: function(ev) {

			// Touch: Get the original event:
			if (this.touchCapable && ev.type === 'touchstart') {
				ev = ev.originalEvent;
			}

			this.offset = this.picker.offset();
			this.size = this.picker[0][this.sizePos];

			var percentage = this.getPercentage(ev);

			if (this.range) {
				var diff1 = Math.abs(this.percentage[0] - percentage);
				var diff2 = Math.abs(this.percentage[1] - percentage);
				this.dragged = (diff1 < diff2) ? 0 : 1;
			} else {
				this.dragged = 0;
			}

			this.percentage[this.dragged] = percentage;
			this.layout();

			if (this.touchCapable) {
				// Touch: Bind touch events:
				$(document).on({
					touchmove: $.proxy(this.mousemove, this),
					touchend: $.proxy(this.mouseup, this)
				});
			} else {
				$(document).on({
					mousemove: $.proxy(this.mousemove, this),
					mouseup: $.proxy(this.mouseup, this)
				});
			}

			this.inDrag = true;
			var val = this.calculateValue();
			this.element.trigger({
					type: 'slideStart',
					value: val
				}).trigger({
					type: 'slide',
					value: val
				});
			return false;
		},

		mousemove: function(ev) {
			// Touch: Get the original event:
			if (this.touchCapable && ev.type === 'touchmove') {
				ev = ev.originalEvent;
			}

			var percentage = this.getPercentage(ev);
			if (this.range) {
				if (this.dragged === 0 && this.percentage[1] < percentage) {
					this.percentage[0] = this.percentage[1];
					this.dragged = 1;
				} else if (this.dragged === 1 && this.percentage[0] > percentage) {
					this.percentage[1] = this.percentage[0];
					this.dragged = 0;
				}
			}
			this.percentage[this.dragged] = percentage;
			this.layout();
			var val = this.calculateValue();
			this.element
				.trigger({
					type: 'slide',
					value: val
				})
				.data('value', val)
				.prop('value', val);
			return false;
		},

		mouseup: function(ev) {
			if (this.touchCapable) {
				// Touch: Bind touch events:
				$(document).off({
					touchmove: this.mousemove,
					touchend: this.mouseup
				});
			} else {
				$(document).off({
					mousemove: this.mousemove,
					mouseup: this.mouseup
				});
			}

			this.inDrag = false;
			if (this.over == false) {
				this.hideTooltip();
			}

			this.element;
			var val = this.calculateValue();
			this.element
				.trigger({
					type: 'slideStop',
					value: val
				})
				.data('value', val)
				.prop('value', val);
			return false;
		},

		calculateValue: function() {
			var val;
			if (this.range) {
				val = [
					(this.min + Math.round((this.diff * this.percentage[0]/100)/this.step)*this.step),
					(this.min + Math.round((this.diff * this.percentage[1]/100)/this.step)*this.step)
				];
				this.value = val;
			} else {
				val = (this.min + Math.round((this.diff * this.percentage[0]/100)/this.step)*this.step);
				this.value = [val, this.value[1]];
			}
			return val;
		},

		getPercentage: function(ev) {
			if (this.touchCapable) {
				ev = ev.touches[0];
			}
			var percentage = (ev[this.mousePos] - this.offset[this.stylePos])*100/this.size;
			percentage = Math.round(percentage/this.percentage[2])*this.percentage[2];
			return Math.max(0, Math.min(100, percentage));
		},

		getValue: function() {
			if (this.range) {
				return this.value;
			}
			return this.value[0];
		},

		setValue: function(val) {
			this.value = val;

			if (this.range) {
				this.value[0] = Math.max(this.min, Math.min(this.max, this.value[0]));
				this.value[1] = Math.max(this.min, Math.min(this.max, this.value[1]));
			} else {
				this.value = [ Math.max(this.min, Math.min(this.max, this.value))];
				this.handle2.addClass('hide');
				if (this.selection == 'after') {
					this.value[1] = this.max;
				} else {
					this.value[1] = this.min;
				}
			}
			this.diff = this.max - this.min;
			this.percentage = [
				(this.value[0]-this.min)*100/this.diff,
				(this.value[1]-this.min)*100/this.diff,
				this.step*100/this.diff
			];
			this.layout();
		}
	};

	$.fn.slider = function ( option, val ) {
		return this.each(function () {
			var $this = $(this),
				data = $this.data('slider'),
				options = typeof option === 'object' && option;
			if (!data)  {
				$this.data('slider', (data = new Slider(this, $.extend({}, $.fn.slider.defaults,options))));
			}
			if (typeof option == 'string') {
				data[option](val);
			}
		})
	};

	$.fn.slider.defaults = {
		min: 0,
		max: 10,
		step: 1,
		orientation: 'horizontal',
		value: 5,
		selection: 'before',
		tooltip: 'show',
		handle: 'round',
		formater: function(value) {
			return value;
		}
	};

	$.fn.slider.Constructor = Slider;

}( window.jQuery191 );
<commit_msg>Changed jQuery var.<commit_after>/* =========================================================
 * bootstrap-slider.js v2.0.0
 * http://www.eyecon.ro/bootstrap-slider
 * =========================================================
 * Copyright 2012 Stefan Petre
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ========================================================= */

!function( $ ) {

	var Slider = function(element, options) {
		this.element = $(element);
		this.picker = $('
'+
							'
'+
								'

'+
								'

'+
								'

'+
							'
'+
							'

'+
						'
')
							.insertBefore(this.element)
							.append(this.element);
		this.id = this.element.data('slider-id')||options.id;
		if (this.id) {
			this.picker[0].id = this.id;
		}

		if (typeof Modernizr !== 'undefined' && Modernizr.touch) {
			this.touchCapable = true;
		}

		var tooltip = this.element.data('slider-tooltip')||options.tooltip;

		this.tooltip = this.picker.find('.tooltip');
		this.tooltipInner = this.tooltip.find('div.tooltip-inner');

		this.orientation = this.element.data('slider-orientation')||options.orientation;
		switch(this.orientation) {
			case 'vertical':
				this.picker.addClass('slider-vertical');
				this.stylePos = 'top';
				this.mousePos = 'pageY';
				this.sizePos = 'offsetHeight';
				this.tooltip.addClass('right')[0].style.left = '100%';
				break;
			default:
				this.picker
					.addClass('slider-horizontal')
					.css('width', '100%');
				this.orientation = 'horizontal';
				this.stylePos = 'left';
				this.mousePos = 'pageX';
				this.sizePos = 'offsetWidth';
				this.tooltip.addClass('top')[0].style.top = -this.tooltip.outerHeight() - 14 + 'px';
				break;
		}

		this.min = this.element.data('slider-min')||options.min;
		this.max = this.element.data('slider-max')||options.max;
		this.step = this.element.data('slider-step')||options.step;
		this.value = this.element.data('slider-value')||options.value;
		if (this.value[1]) {
			this.range = true;
		}

		this.selection = this.element.data('slider-selection')||options.selection;
		this.selectionEl = this.picker.find('.slider-selection');
		if (this.selection === 'none') {
			this.selectionEl.addClass('hide');
		}
		this.selectionElStyle = this.selectionEl[0].style;

		this.handle1 = this.picker.find('.slider-handle:first');
		this.handle1Stype = this.handle1[0].style;
		this.handle1Value = this.picker.find('.slider-handle:first .slider-value');
		this.handle2 = this.picker.find('.slider-handle:last');
		this.handle2Stype = this.handle2[0].style;
		this.handle2Value = this.picker.find('.slider-handle:last .slider-value');

		this.handle = this.element.data('slider-handle')||options.handle;
		switch(this.handle) {
			case 'round':
			case 'triangle':
			case 'rectangular':
				this.handle1.addClass(this.handle);
				this.handle2.addClass(this.handle);
				break
		}

		if (this.range) {
			this.value[0] = Math.max(this.min, Math.min(this.max, this.value[0]));
			this.value[1] = Math.max(this.min, Math.min(this.max, this.value[1]));
		} else {
			this.value = [ Math.max(this.min, Math.min(this.max, this.value))];
			this.handle2.addClass('hide');
			if (this.selection == 'after') {
				this.value[1] = this.max;
			} else {
				this.value[1] = this.min;
			}
		}
		this.diff = this.max - this.min;
		this.percentage = [
			(this.value[0]-this.min)*100/this.diff,
			(this.value[1]-this.min)*100/this.diff,
			this.step*100/this.diff
		];

		this.offset = this.picker.offset();
		this.size = this.picker[0][this.sizePos];

		this.formater = options.formater;

		this.layout();

		if (this.touchCapable) {
			// Touch: Bind touch events:
			this.picker.on({
				touchstart: $.proxy(this.mousedown, this)
			});
		} else {
			this.picker.on({
				mousedown: $.proxy(this.mousedown, this)
			});
		}

		if (tooltip === 'show') {
			this.picker.on({
				mouseenter: $.proxy(this.showTooltip, this),
				mouseleave: $.proxy(this.hideTooltip, this)
			});
		} else {
			this.tooltip.addClass('hide');
		}
	};

	Slider.prototype = {
		constructor: Slider,

		over: false,
		inDrag: false,

		showTooltip: function(){
			this.tooltip.addClass('in');
			//var left = Math.round(this.percent*this.width);
			//this.tooltip.css('left', left - this.tooltip.outerWidth()/2);
			this.over = true;
			this.handle1Value.text('');
		},

		hideTooltip: function(){
			if (this.inDrag === false) {
				this.tooltip.removeClass('in');
			}
			this.over = false;
			this.handle1Value.text(this.getValue());
		},

		layout: function(){
			this.handle1Stype[this.stylePos] = this.percentage[0]+'%';
			this.handle2Stype[this.stylePos] = this.percentage[1]+'%';
			if (this.orientation == 'vertical') {
				this.selectionElStyle.top = Math.min(this.percentage[0], this.percentage[1]) +'%';
				this.selectionElStyle.height = Math.abs(this.percentage[0] - this.percentage[1]) +'%';
			} else {
				this.selectionElStyle.left = Math.min(this.percentage[0], this.percentage[1]) +'%';
				this.selectionElStyle.width = Math.abs(this.percentage[0] - this.percentage[1]) +'%';
			}
			if (this.range) {
				this.tooltipInner.text(
					this.formater(this.value[0]) +
					' : ' +
					this.formater(this.value[1])
				);
				this.tooltip[0].style[this.stylePos] = this.size * (this.percentage[0] + (this.percentage[1] - this.percentage[0])/2)/100 - (this.orientation === 'vertical' ? this.tooltip.outerHeight()/2 : this.tooltip.outerWidth()/2) +'px';
			} else {
				this.tooltipInner.text(
					this.formater(this.value[0])
				);
				this.tooltip[0].style[this.stylePos] = this.size * this.percentage[0]/100 - (this.orientation === 'vertical' ? this.tooltip.outerHeight()/2 : this.tooltip.outerWidth()/2) +'px';
			}
		},

		mousedown: function(ev) {

			// Touch: Get the original event:
			if (this.touchCapable && ev.type === 'touchstart') {
				ev = ev.originalEvent;
			}

			this.offset = this.picker.offset();
			this.size = this.picker[0][this.sizePos];

			var percentage = this.getPercentage(ev);

			if (this.range) {
				var diff1 = Math.abs(this.percentage[0] - percentage);
				var diff2 = Math.abs(this.percentage[1] - percentage);
				this.dragged = (diff1 < diff2) ? 0 : 1;
			} else {
				this.dragged = 0;
			}

			this.percentage[this.dragged] = percentage;
			this.layout();

			if (this.touchCapable) {
				// Touch: Bind touch events:
				$(document).on({
					touchmove: $.proxy(this.mousemove, this),
					touchend: $.proxy(this.mouseup, this)
				});
			} else {
				$(document).on({
					mousemove: $.proxy(this.mousemove, this),
					mouseup: $.proxy(this.mouseup, this)
				});
			}

			this.inDrag = true;
			var val = this.calculateValue();
			this.element.trigger({
					type: 'slideStart',
					value: val
				}).trigger({
					type: 'slide',
					value: val
				});
			return false;
		},

		mousemove: function(ev) {
			// Touch: Get the original event:
			if (this.touchCapable && ev.type === 'touchmove') {
				ev = ev.originalEvent;
			}

			var percentage = this.getPercentage(ev);
			if (this.range) {
				if (this.dragged === 0 && this.percentage[1] < percentage) {
					this.percentage[0] = this.percentage[1];
					this.dragged = 1;
				} else if (this.dragged === 1 && this.percentage[0] > percentage) {
					this.percentage[1] = this.percentage[0];
					this.dragged = 0;
				}
			}
			this.percentage[this.dragged] = percentage;
			this.layout();
			var val = this.calculateValue();
			this.element
				.trigger({
					type: 'slide',
					value: val
				})
				.data('value', val)
				.prop('value', val);
			return false;
		},

		mouseup: function(ev) {
			if (this.touchCapable) {
				// Touch: Bind touch events:
				$(document).off({
					touchmove: this.mousemove,
					touchend: this.mouseup
				});
			} else {
				$(document).off({
					mousemove: this.mousemove,
					mouseup: this.mouseup
				});
			}

			this.inDrag = false;
			if (this.over == false) {
				this.hideTooltip();
			}

			this.element;
			var val = this.calculateValue();
			this.element
				.trigger({
					type: 'slideStop',
					value: val
				})
				.data('value', val)
				.prop('value', val);
			return false;
		},

		calculateValue: function() {
			var val;
			if (this.range) {
				val = [
					(this.min + Math.round((this.diff * this.percentage[0]/100)/this.step)*this.step),
					(this.min + Math.round((this.diff * this.percentage[1]/100)/this.step)*this.step)
				];
				this.value = val;
			} else {
				val = (this.min + Math.round((this.diff * this.percentage[0]/100)/this.step)*this.step);
				this.value = [val, this.value[1]];
			}
			return val;
		},

		getPercentage: function(ev) {
			if (this.touchCapable) {
				ev = ev.touches[0];
			}
			var percentage = (ev[this.mousePos] - this.offset[this.stylePos])*100/this.size;
			percentage = Math.round(percentage/this.percentage[2])*this.percentage[2];
			return Math.max(0, Math.min(100, percentage));
		},

		getValue: function() {
			if (this.range) {
				return this.value;
			}
			return this.value[0];
		},

		setValue: function(val) {
			this.value = val;

			if (this.range) {
				this.value[0] = Math.max(this.min, Math.min(this.max, this.value[0]));
				this.value[1] = Math.max(this.min, Math.min(this.max, this.value[1]));
			} else {
				this.value = [ Math.max(this.min, Math.min(this.max, this.value))];
				this.handle2.addClass('hide');
				if (this.selection == 'after') {
					this.value[1] = this.max;
				} else {
					this.value[1] = this.min;
				}
			}
			this.diff = this.max - this.min;
			this.percentage = [
				(this.value[0]-this.min)*100/this.diff,
				(this.value[1]-this.min)*100/this.diff,
				this.step*100/this.diff
			];
			this.layout();
		}
	};

	$.fn.slider = function ( option, val ) {
		return this.each(function () {
			var $this = $(this),
				data = $this.data('slider'),
				options = typeof option === 'object' && option;
			if (!data)  {
				$this.data('slider', (data = new Slider(this, $.extend({}, $.fn.slider.defaults,options))));
			}
			if (typeof option == 'string') {
				data[option](val);
			}
		})
	};

	$.fn.slider.defaults = {
		min: 0,
		max: 10,
		step: 1,
		orientation: 'horizontal',
		value: 5,
		selection: 'before',
		tooltip: 'show',
		handle: 'round',
		formater: function(value) {
			return value;
		}
	};

	$.fn.slider.Constructor = Slider;

}( window.jquery );
<|endoftext|>


<commit_before>!function ( $, window, document, undefined ) {
    //Helpers

    function preventDefault(e) {
        e.preventDefault();
    }

    function numberOrDefault( str, fallback ) {
        str = (str + "").replace( ",", "." );

        if( isNaN( str ) ) {
            return fallback;
        }
        return +str;
    }

    function normalize( value ) {
        return Math.round( value * 100000000 ) / 100000000;
    }

    function snap( value, step ) {
        var mod = normalize( value % step ),
            mod2 = normalize( step - mod );

        value = mod2 <= mod ? value + mod2 : value - mod;
        return value;
    }

    var clearSelection = function() {

        if( window.getSelection ) {

            if( window.getSelection().empty ) {
                return function() {
                    window.getSelection().empty();
                };
            }
            else if( window.getSelection().removeAllRanges ) {
                return function() {
                    window.getSelection().removeAllRanges();
                };
            }

        }
        else if( document.selection && document.selection.empty ) {
            return function() {
                document.selection.empty();
            }
        }

    }();

    //Constructor
    function Slider( element, options ) {
        this.element = element;
        this.options = options;
        init.call( this );
    }
    //Public methods
    Slider.prototype = {

        disabled: function( val ) {
            val = !!val;
            this.isDisabled = val;
            $( this.slider )[ val ? "addClass" : "removeClass" ]( "disabled" );
            this.element.disabled = val;

            if( !val ) {
                unbindmousemove.call( this );
            }
        },

        destroy: function() {
            $( this.slider ).remove();
            $( this.element ).unbind( ".slider" ).removeData( "slider" );
        },

        constructor: Slider
    };

    //Private methods

    function init() {
        var val,
            decimalIndex;

        this.step = +this.options.step;

        this.min = +this.options.min;
        this.max = +this.options.max;

        if( !( this.step > 0 ) ) {
            this.step = 1;
        }

        if( this.min > this.max ) {
            this.max = this.min;
        }

        if( this.min === this.max ) {
            this.max += 1;
        }
        val = numberOrDefault( this.element.value, this.min );
        decimalIndex = this.step.toString().indexOf(".");

        if( !!~decimalIndex ) {
            this.decimals = this.step.toString().length - ( decimalIndex + 1 );
        }

        this.slider = $( this.options.template ).appendTo( $( $( this.element ).attr("data-target") ) ).get( 0 );
        this.disabled( this.element.disabled );

        $( this.element ).bind( {
            "change.slider": $.proxy( onchange, this ),
            "input.slider": $.proxy( onchange, this ),
            "keydown.slider": $.proxy( onkeydown, this )
        });

        $( this.slider ).bind( "mousedown.slider", $.proxy( onmousedown, this ) );

        calculateBox.call( this );
        setValue.call( this, snap( Math.max( Math.min( val, this.max ), this.min ), this.step ) );
    }

    function onkeydown(e) { //Prevent submits when pressing enter/esc on a manual slider input
        if( e.which === 13 || e.which === 27 ) {
            e.preventDefault();
            e.currentTarget.blur();
        }
    }

    function calculateBox() {
        var $slider = $( this.slider ),
            $knob = $( this.slider.firstChild ),
            width = $slider.width(),
            height = $slider.height();

        this.box = $slider.offset();
        this.box.right = width + this.box.left;
        this.box.bottom = height + this.box.top;
        this.box.isHorizontal = width >= height;

        this.knobBox = {
            width: ($knob.width() / 2 | 0 ) + 2, //Dunno why but pretending the knob is 2px larger settles it better at start and end of sliders
            height: ($knob.height() / 2 | 0 ) + 2
        };
    }
    //React to change events on the original input
    function onchange(e) {
        var val = e.currentTarget.value;
        if( !val.length && e.type=="input" ) { //Allow input to be invalid while typing and coerce when blurred ("change")
            return;
        }
        val = numberOrDefault( e.currentTarget.value, this.min );
        setValue.call( this, snap( Math.max( Math.min( val, this.max ), this.min ), this.step ) );
    }

    //Begin the slider drag process
    function onmousedown( e ) {
        if( e.which === 1 && !this.isDisabled ) {
            var slideEvt = $.Event( "slidestart");
            $( this.element ).trigger( slideEvt );

            if( slideEvt.isDefaultPrevented() ) {
                e.preventDefault();
                return;
            }

            calculateBox.call( this );

            clearSelection();

            $( document ).bind( {
                "mousemove.slider": $.proxy( onmousemove, this ),
                //in case mouse is released while holding perfectly still and e.which !== 1 in mousemove handler isn't detected
                "mouseup.slider": $.proxy( unbindmousemove, this ),
                "selectstart.slider": preventDefault, //Remove all this crap while dragging
                "dragstart.slider": preventDefault
            });

            $( this.slider ).addClass( "focused" );
            this.isSliding = true;

            onmousemove.call( this, e );
        }
    }

    function unbindmousemove() {
        if( this.isSliding ) {
            $( document ).unbind( ".slider" );
            $( this.slider ).removeClass( "focused" );
            this.isSliding = false;
            $( this.element ).trigger( "slideend" );
        }

    }

    function onmousemove( e ) {
        if( e.which !== 1 ) { //in case mouse is released and mouseup event wasn't detected
            unbindmousemove.call( this );
            return;
        }

        var box = this.box,
            position,
            value, mod, pxOffset;

        if( box.isHorizontal ) {
            pxOffset = ( Math.max( Math.min( e.pageX, box.right ), box.left ) - box.left );
            position = pxOffset / ( box.right - box.left );
        }
        else {
            pxOffset = ( Math.max( Math.min( e.pageY, box.bottom ), box.top ) - box.top );
            position = ( 1 - ( pxOffset / ( box.bottom - box.top ) ) );
        }

        value = ( this.max - this.min ) * position + this.min;
        mod = value % this.step;

        if( mod !== 0 ) {
            value = snap( value, this.step );
        }

        setValue.call( this, normalize(value) );
        $( this.element ).trigger( "slide" );

    }

    function setValue( value ) {
        var offset;

        this.element.value = this.decimals ? value.toFixed(this.decimals) : value;

        if( this.box.isHorizontal ) {
            offset = ( ( value - this.min ) / ( this.max - this.min ) * ( this.box.right - this.box.left ) - this.knobBox.width ) + "px";
        }
        else {
            offset = ( ( value - this.min ) / ( this.max - this.min ) * ( this.box.bottom - this.box.top ) - this.knobBox.height ) + "px";
        }

        $( this.slider.firstChild ).css( this.box.isHorizontal ? "left" : "bottom", offset );
    }

    $.fn.slider = function( option ) {
        return this.filter("input").each( function() {

            var $this = $( this ),
                data = $this.data( "slider" ),
                options = typeof option == 'object' && option || {};

            if( !data ) {
                options = $.extend( {}, $.fn.slider.defaults, $this.data(), options );
                $this.data( "slider", ( data = new Slider( this, options ) ) );
            }
            if( typeof option == 'string' ) {
                data[option].apply( data, arguments.length > 1 ? [].slice.call( arguments, 1 ) : [] );
            }
        });
    };

    $.fn.slider.Constructor = Slider;

    $.fn.slider.defaults = {
        min: 1,
        max: 100,
        step: 1,

        template: '

'
    };

    $( function() {
        $( "input[data-enhance='slider']" ).slider();
    });

}( jQuery, window, document );<commit_msg>Keyboard up/down arrows can be used to adjust slider while in the original text input<commit_after>!function ( $, window, document, undefined ) {
    //Helpers

    function preventDefault(e) {
        e.preventDefault();
    }

    function numberOrDefault( str, fallback ) {
        str = (str + "").replace( ",", "." );

        if( isNaN( str ) ) {
            return fallback;
        }
        return +str;
    }

    function normalize( value ) {
        return Math.round( value * 100000000 ) / 100000000;
    }

    function snap( value, step ) {
        var mod = normalize( value % step ),
            mod2 = normalize( step - mod );

        value = mod2 <= mod ? value + mod2 : value - mod;
        return value;
    }

    var clearSelection = function() {

        if( window.getSelection ) {

            if( window.getSelection().empty ) {
                return function() {
                    window.getSelection().empty();
                };
            }
            else if( window.getSelection().removeAllRanges ) {
                return function() {
                    window.getSelection().removeAllRanges();
                };
            }

        }
        else if( document.selection && document.selection.empty ) {
            return function() {
                document.selection.empty();
            }
        }

    }();

    //Constructor
    function Slider( element, options ) {
        this.element = element;
        this.options = options;
        init.call( this );
    }
    //Public methods
    Slider.prototype = {

        disabled: function( val ) {
            val = !!val;
            this.isDisabled = val;
            $( this.slider )[ val ? "addClass" : "removeClass" ]( "disabled" );
            this.element.disabled = val;

            if( !val ) {
                unbindmousemove.call( this );
            }
        },

        destroy: function() {
            $( this.slider ).remove();
            $( this.element ).unbind( ".slider" ).removeData( "slider" );
        },

        constructor: Slider
    };

    //Private methods

    function init() {
        var val,
            decimalIndex;

        this.step = +this.options.step;

        this.min = +this.options.min;
        this.max = +this.options.max;

        if( !( this.step > 0 ) ) {
            this.step = 1;
        }

        if( this.min > this.max ) {
            this.max = this.min;
        }

        if( this.min === this.max ) {
            this.max += 1;
        }
        val = numberOrDefault( this.element.value, this.min );
        decimalIndex = this.step.toString().indexOf(".");

        if( !!~decimalIndex ) {
            this.decimals = this.step.toString().length - ( decimalIndex + 1 );
        }

        this.slider = $( this.options.template ).appendTo( $( $( this.element ).attr("data-target") ) ).get( 0 );
        this.disabled( this.element.disabled );

        $( this.element ).bind( {
            "change.slider": $.proxy( onchange, this ),
            "input.slider": $.proxy( onchange, this ),
            "keydown.slider": $.proxy( onkeydown, this )
        });

        $( this.slider ).bind( "mousedown.slider", $.proxy( onmousedown, this ) );

        calculateBox.call( this );
        setValue.call( this, snap( Math.max( Math.min( val, this.max ), this.min ), this.step ) );
    }

    function calculateBox() {
        var $slider = $( this.slider ),
            $knob = $( this.slider.firstChild ),
            width = $slider.width(),
            height = $slider.height();

        this.box = $slider.offset();
        this.box.right = width + this.box.left;
        this.box.bottom = height + this.box.top;
        this.box.isHorizontal = width >= height;

        this.knobBox = {
            width: ($knob.width() / 2 | 0 ) + 2, //Dunno why but pretending the knob is 2px larger settles it better at start and end of sliders
            height: ($knob.height() / 2 | 0 ) + 2
        };
    }
    //React to change events on the original input
    function onchange(e) {
        var val = e.currentTarget.value;
        if( !val.length && e.type=="input" ) { //Allow input to be invalid while typing and coerce when blurred ("change")
            return;
        }
        val = numberOrDefault( val, this.min );
        setValue.call( this, snap( Math.max( Math.min( val, this.max ), this.min ), this.step ) );
    }

    function onkeydown( e ) {
        var val = numberOrDefault( e.currentTarget.value, this.min );

        switch( e.which ) {

            case 38: // up
                e.preventDefault();
                setValue.call( this, Math.min( this.max, val += this.step ) );

            break;

            case 40: //down
                e.preventDefault();
                setValue.call( this, Math.max( this.min, val -= this.step ) );
            break;

            case 13: //esc or enter
            case 27:
                e.preventDefault(); //Prevent submits when pressing enter/esc on a manual slider input
                e.currentTarget.blur();
            break;
        }
    }

    function onmousewheel( e ) {
        e.preventDefault();

    }

    //Begin the slider drag process
    function onmousedown( e ) {
        if( e.which === 1 && !this.isDisabled ) {
            var slideEvt = $.Event( "slidestart");
            $( this.element ).trigger( slideEvt );

            if( slideEvt.isDefaultPrevented() ) {
                e.preventDefault();
                return;
            }

            calculateBox.call( this );

            clearSelection();

            $( document ).bind( {
                "mousemove.slider": $.proxy( onmousemove, this ),
                //in case mouse is released while holding perfectly still and e.which !== 1 in mousemove handler isn't detected
                "mouseup.slider": $.proxy( unbindmousemove, this ),
                "selectstart.slider": preventDefault, //Remove all this crap while dragging
                "dragstart.slider": preventDefault
            });

            $( this.slider ).addClass( "focused" );
            this.isSliding = true;

            onmousemove.call( this, e );
        }
    }

    function unbindmousemove() {
        if( this.isSliding ) {
            $( document ).unbind( ".slider" );
            $( this.slider ).removeClass( "focused" );
            this.isSliding = false;
            $( this.element ).trigger( "slideend" );
        }

    }

    function onmousemove( e ) {
        if( e.which !== 1 ) { //in case mouse is released and mouseup event wasn't detected
            unbindmousemove.call( this );
            return;
        }

        var box = this.box,
            position,
            value, mod, pxOffset;

        if( box.isHorizontal ) {
            pxOffset = ( Math.max( Math.min( e.pageX, box.right ), box.left ) - box.left );
            position = pxOffset / ( box.right - box.left );
        }
        else {
            pxOffset = ( Math.max( Math.min( e.pageY, box.bottom ), box.top ) - box.top );
            position = ( 1 - ( pxOffset / ( box.bottom - box.top ) ) );
        }

        value = ( this.max - this.min ) * position + this.min;
        mod = value % this.step;

        if( mod !== 0 ) {
            value = snap( value, this.step );
        }

        setValue.call( this, normalize(value) );
        $( this.element ).trigger( "slide" );

    }

    function setValue( value ) {
        var offset;

        this.element.value = this.decimals ? value.toFixed(this.decimals) : value;

        if( this.box.isHorizontal ) {
            offset = ( ( value - this.min ) / ( this.max - this.min ) * ( this.box.right - this.box.left ) - this.knobBox.width ) + "px";
        }
        else {
            offset = ( ( value - this.min ) / ( this.max - this.min ) * ( this.box.bottom - this.box.top ) - this.knobBox.height ) + "px";
        }

        $( this.slider.firstChild ).css( this.box.isHorizontal ? "left" : "bottom", offset );
    }

    $.fn.slider = function( option ) {
        return this.filter("input").each( function() {

            var $this = $( this ),
                data = $this.data( "slider" ),
                options = typeof option == 'object' && option || {};

            if( !data ) {
                options = $.extend( {}, $.fn.slider.defaults, $this.data(), options );
                $this.data( "slider", ( data = new Slider( this, options ) ) );
            }
            if( typeof option == 'string' ) {
                data[option].apply( data, arguments.length > 1 ? [].slice.call( arguments, 1 ) : [] );
            }
        });
    };

    $.fn.slider.Constructor = Slider;

    $.fn.slider.defaults = {
        min: 1,
        max: 100,
        step: 1,

        template: '

'
    };

    $( function() {
        $( "input[data-enhance='slider']" ).slider();
    });

}( jQuery, window, document );<|endoftext|>


<commit_before>import React, {forwardRef} from 'react';
import {Chrome} from './Chrome';
import {
  useFloating,
  flip,
  shift,
  offset,
  arrow,
} from '@floating-ui/react-dom';
import {useState} from 'react';
import useIsomorphicLayoutEffect from 'use-isomorphic-layout-effect';

const Button = forwardRef(({children, ...props}, ref) => (
  <button
    ref={ref}
    className="bg-gray-100 px-2 rounded"
    {...props}
  >
    {children}
  </button>
));

export const Result1 = () => {
  return (
    <Chrome>
      <Button>My button</Button>

My tooltip

    </Chrome>
  );
};

export const Result2 = () => {
  return (
    <Chrome>
      <Button>My button</Button>

        My tooltip

    </Chrome>
  );
};

export const Result3 = () => {
  return (
    <Chrome>
      <Button>My button</Button>

        My tooltip

    </Chrome>
  );
};

export const Result4 = () => {
  const {x, y, reference, floating, strategy} = useFloating();

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip

    </Chrome>
  );
};

export const Result5 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'right',
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip

    </Chrome>
  );
};

export const Result6 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'top',
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip

    </Chrome>
  );
};

export const Result7 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'top',
    middleware: [flip({rootBoundary: 'document'})],
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip

    </Chrome>
  );
};

export const Result8 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'top',
    middleware: [flip({rootBoundary: 'document'})],
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip with more content

    </Chrome>
  );
};

export const Result9 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'top',
    middleware: [flip({rootBoundary: 'document'}), shift()],
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip with more content

    </Chrome>
  );
};

export const Result10 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'top',
    middleware: [
      flip({rootBoundary: 'document'}),
      shift({padding: 5}),
    ],
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip with more content

    </Chrome>
  );
};

export const Result11 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'top',
    middleware: [
      offset(6),
      flip({rootBoundary: 'document'}),
      shift({padding: 5}),
    ],
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip with more content

    </Chrome>
  );
};

export const Result12 = () => {
  const [arrowEl, setArrowEl] = useState(null);

  const {
    x,
    y,
    reference,
    floating,
    strategy,
    placement,
    update,
    middlewareData: {arrow: {x: arrowX, y: arrowY} = {}},
  } = useFloating({
    placement: 'top',
    middleware: [
      offset(6),
      flip({rootBoundary: 'document'}),
      shift({padding: 5}),
      ...(arrowEl ? [arrow({element: arrowEl})] : []),
    ],
  });

  useIsomorphicLayoutEffect(() => {
    arrowEl && update();
  }, [arrowEl]);

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip with more content

    </Chrome>
  );
};

export const Result13 = () => {
  const [show, setShow] = useState(false);
  const [arrowEl, setArrowEl] = useState(null);

  const {
    x,
    y,
    reference,
    floating,
    strategy,
    placement,
    update,
    middlewareData: {arrow: {x: arrowX, y: arrowY} = {}},
  } = useFloating({
    placement: 'top',
    middleware: [
      offset(6),
      flip({rootBoundary: 'document'}),
      shift({padding: 5}),
      ...(arrowEl ? [arrow({element: arrowEl})] : []),
    ],
  });

  useIsomorphicLayoutEffect(() => {
    arrowEl && update();
  }, [arrowEl]);

  return (
    <Chrome>
      <Button
        onMouseEnter={() => setShow(true)}
        onMouseLeave={() => setShow(false)}
        onFocus={() => setShow(true)}
        onBlur={() => setShow(false)}
        ref={reference}
        aria-describedby="tooltip"
      >
        My button
      </Button>
      {show && (

          My tooltip with more content

      )}
    </Chrome>
  );
};
<commit_msg>docs(tutorial): fix styles<commit_after>import React, {forwardRef} from 'react';
import {Chrome} from './Chrome';
import {
  useFloating,
  flip,
  shift,
  offset,
  arrow,
} from '@floating-ui/react-dom';
import {useState} from 'react';
import useIsomorphicLayoutEffect from 'use-isomorphic-layout-effect';

const Button = forwardRef(({children, ...props}, ref) => (
  <button
    ref={ref}
    className="bg-gray-100 px-2 rounded"
    {...props}
  >
    {children}
  </button>
));

export const Result1 = () => {
  return (
    <Chrome>
      <Button>My button</Button>

My tooltip

    </Chrome>
  );
};

export const Result2 = () => {
  return (
    <Chrome>
      <Button>My button</Button>

        My tooltip

    </Chrome>
  );
};

export const Result3 = () => {
  return (
    <Chrome>
      <Button>My button</Button>

        My tooltip

    </Chrome>
  );
};

export const Result4 = () => {
  const {x, y, reference, floating, strategy} = useFloating();

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip

    </Chrome>
  );
};

export const Result5 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'right',
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip

    </Chrome>
  );
};

export const Result6 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'top',
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip

    </Chrome>
  );
};

export const Result7 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'top',
    middleware: [flip({rootBoundary: 'document'})],
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip

    </Chrome>
  );
};

export const Result8 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'top',
    middleware: [flip({rootBoundary: 'document'})],
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip with more content

    </Chrome>
  );
};

export const Result9 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'top',
    middleware: [flip({rootBoundary: 'document'}), shift()],
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip with more content

    </Chrome>
  );
};

export const Result10 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'top',
    middleware: [
      flip({rootBoundary: 'document'}),
      shift({padding: 5}),
    ],
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip with more content

    </Chrome>
  );
};

export const Result11 = () => {
  const {x, y, reference, floating, strategy} = useFloating({
    placement: 'top',
    middleware: [
      offset(6),
      flip({rootBoundary: 'document'}),
      shift({padding: 5}),
    ],
  });

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip with more content

    </Chrome>
  );
};

export const Result12 = () => {
  const [arrowEl, setArrowEl] = useState(null);

  const {
    x,
    y,
    reference,
    floating,
    strategy,
    placement,
    update,
    middlewareData: {arrow: {x: arrowX, y: arrowY} = {}},
  } = useFloating({
    placement: 'top',
    middleware: [
      offset(6),
      flip({rootBoundary: 'document'}),
      shift({padding: 5}),
      ...(arrowEl ? [arrow({element: arrowEl})] : []),
    ],
  });

  useIsomorphicLayoutEffect(() => {
    arrowEl && update();
  }, [arrowEl]);

  return (
    <Chrome>
      <Button ref={reference}>My button</Button>

        My tooltip with more content

    </Chrome>
  );
};

export const Result13 = () => {
  const [show, setShow] = useState(false);
  const [arrowEl, setArrowEl] = useState(null);

  const {
    x,
    y,
    reference,
    floating,
    strategy,
    placement,
    update,
    middlewareData: {arrow: {x: arrowX, y: arrowY} = {}},
  } = useFloating({
    placement: 'top',
    middleware: [
      offset(6),
      flip({rootBoundary: 'document'}),
      shift({padding: 5}),
      ...(arrowEl ? [arrow({element: arrowEl})] : []),
    ],
  });

  useIsomorphicLayoutEffect(() => {
    arrowEl && update();
  }, [arrowEl]);

  return (
    <Chrome>
      <Button
        onMouseEnter={() => setShow(true)}
        onMouseLeave={() => setShow(false)}
        onFocus={() => setShow(true)}
        onBlur={() => setShow(false)}
        ref={reference}
        aria-describedby="tooltip"
      >
        My button
      </Button>
      {show && (

          My tooltip with more content

      )}
    </Chrome>
  );
};
<|endoftext|>


<commit_before> if (localStorage.save) {
     var game = JSON.parse(atob(localStorage.save));
 } else {
     var game = {
         clicks: 0,
         upgrades: [0, 0, 0, 0, 0, 0, 0, 0],
         cps: 0,
         upgradePrice: [15, 100, 500, 2000, 7000, 50000, 1000000, 100000000],
         upgradeCps: [0.1, 0.5, 4, 10, 40, 100, 400, 7000]
     };
 }

 function saveGame() {
     localStorage.save = btoa(JSON.stringify(game));
 }

 function gameLoop() {
     game.clicks += game.cps;
     updateView();
     saveGame();
 }

 function buyUpgrade(x) {
     if (game.clicks >= game.upgradePrice[x]) {
         game.clicks -= game.upgradePrice[x];
         game.upgradePrice[x] = Math.ceil(game.upgradePrice[x] * 1.15);
         game.upgrades[x] += 1;
         game.cps += game.upgradeCps[x];
     }
 }
 var gameLoopInterval = setInterval(gameLoop, 1000);

 function updateView() {
     $("#count").html(Math.round(game.clicks));
     $("#cps").html((Math.round(game.cps * 10) / 10).toFixed(1));
     $("#price0").html(game.upgradePrice[0]);
     $("#price1").html(game.upgradePrice[1]);
     $("#price2").html(game.upgradePrice[2]);
     $("#price3").html(game.upgradePrice[3]);
     $("#price4").html(game.upgradePrice[4]);
     $("#price5").html(game.upgradePrice[5]);
     $("#price6").html(game.upgradePrice[6]);
     $("#price7").html(game.upgradePrice[7]);
     $("#qty0").html(game.upgrades[0]);
     $("#qty1").html(game.upgrades[1]);
     $("#qty2").html(game.upgrades[2]);
     $("#qty3").html(game.upgrades[3]);
     $("#qty4").html(game.upgrades[4]);
     $("#qty5").html(game.upgrades[5]);
     $("#qty6").html(game.upgrades[6]);
     $("#qty7").html(game.upgrades[7]);
 }
 $(function() {
     $("#circle").click(function() {
      if(game.cps>4){
         game.clicks += game.cps / 4;
      }
      else{
         game.clicks += 1;
      }
         updateView();
     });
 });

 function exportSave() {
     prompt('This is your save. Keep it.', btoa(JSON.stringify(game)));
 }

 function importSave() {
     game = JSON.parse(atob(prompt('Enter your save here. Your previous save will not be saved.')));
     saveGame();
     location.reload();
 }

 function wipeSave() {
     if (confirm('Are you sure you would like to delete EVERYTHING?')) {
         clearInterval(gameLoopInterval);
         localStorage.save = "";
         alert('Deleted.');
         location.reload();
     } else {
         alert('Cancelled.');
     }
 }
<commit_msg>Update main.js<commit_after> if (localStorage.save) {
     var game = JSON.parse(atob(localStorage.save));
 } else {
     var game = {
         clicks: 0,
         upgrades: [0, 0, 0, 0, 0, 0, 0, 0],
         cps: 0,
         upgradePrice: [15, 100, 500, 2000, 7000, 50000, 1000000, 100000000],
         upgradeCps: [0.1, 0.5, 4, 10, 40, 100, 400, 7000]
     };
 }

 function saveGame() {
     localStorage.save = btoa(JSON.stringify(game));
 }

 function gameLoop() {
     game.clicks += game.cps;
     updateView();
     saveGame();
 }

 function buyUpgrade(x) {
     if (game.clicks >= game.upgradePrice[x]) {
         game.clicks -= game.upgradePrice[x];
         game.upgradePrice[x] = Math.ceil(game.upgradePrice[x] * 1.15);
         game.upgrades[x] += 1;
         game.cps += game.upgradeCps[x];
     }
 }
 var gameLoopInterval = setInterval(gameLoop, 1000);

 function updateView() {
     $("#count").html(Math.round(game.clicks).toLocaleString());
     $("#cps").html((Math.round(game.cps * 10) / 10).toFixed(1));
     $("#price0").html(game.upgradePrice[0]);
     $("#price1").html(game.upgradePrice[1]);
     $("#price2").html(game.upgradePrice[2]);
     $("#price3").html(game.upgradePrice[3]);
     $("#price4").html(game.upgradePrice[4]);
     $("#price5").html(game.upgradePrice[5]);
     $("#price6").html(game.upgradePrice[6]);
     $("#price7").html(game.upgradePrice[7]);
     $("#qty0").html(game.upgrades[0]);
     $("#qty1").html(game.upgrades[1]);
     $("#qty2").html(game.upgrades[2]);
     $("#qty3").html(game.upgrades[3]);
     $("#qty4").html(game.upgrades[4]);
     $("#qty5").html(game.upgrades[5]);
     $("#qty6").html(game.upgrades[6]);
     $("#qty7").html(game.upgrades[7]);
 }
 $(function() {
     $("#circle").click(function() {
      if(game.cps>4){
         game.clicks += game.cps / 4;
      }
      else{
         game.clicks += 1;
      }
         updateView();
     });
 });

 function exportSave() {
     prompt('This is your save. Keep it.', btoa(JSON.stringify(game)));
 }

 function importSave() {
     game = JSON.parse(atob(prompt('Enter your save here. Your previous save will not be saved.')));
     saveGame();
     location.reload();
 }

 function wipeSave() {
     if (confirm('Are you sure you would like to delete EVERYTHING?')) {
         clearInterval(gameLoopInterval);
         localStorage.save = "";
         alert('Deleted.');
         location.reload();
     } else {
         alert('Cancelled.');
     }
 }
<|endoftext|>


<commit_before>require.config({
    'urlArgs': "bust="+Date.now(), // fix caching troubles
    'paths': {
        "bootstrap": "../lib/bootstrap/js/bootstrap",
        "codemirror": "../CodeMirror/lib/codemirror",
        "jquery": "../lib/jquery",
        "base64": "../lib/base64",
        "mustache": "../lib/mustache",
        "underscore": "../lib/underscore-min",
        "jquery-ui": "../lib/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min",
        "jquery-appear": "../lib/jquery-appear",
        "backbone": "../lib/backbone",
        "jsdiff": "../lib/jsdiff",
        "spin": "../lib/spin",
        "mousewheel": "../lib/jquery-mousewheel",
        "text": "../lib/text",
        "qtip": "../lib/jquery.qtip.custom/jquery.qtip"

    },
    'shim': {
        'codemirror': {
            deps:['jquery'],
            exports:'CodeMirror'
        },
        'backbone': {
            deps:['jquery','underscore'],
            exports:'Backbone'
        },
        'bootstrap': {
            deps:['jquery','jquery-ui']
        },
        'underscore': {
            deps:[],
            exports:'_'
        }
    }
});

require(
    [
        "jquery",
        "geometry",
        'events',
        "jquery-appear",
        "bootstrap",
        "mustache",
        "underscore",
        "jquery-ui",
        "backbone",
        "codemirror",
        "toolbar",
        "facsimile",
        "editor",
        "router",
        "spin",
        "spinner",
        "page",
        "mousewheel",
        "language",
        "bibinfo",
        'container',
        'boxes',
        'compatibility',
        'dialog',
        'browser',
        'document',
        'qtip'
    ], function($, geometry,events) {

        try {
            console.log;
        } catch (err) {
            console = {
                log: function() {},
                trace: function() {}
            };
        }

        $(document).ready(function() {
            events.trigger('appReady');

        });
});
<commit_msg>hilight<commit_after>require.config({
    'urlArgs': "bust="+Date.now(), // fix caching troubles
    'paths': {
        "bootstrap": "../lib/bootstrap/js/bootstrap",
        "codemirror": "../CodeMirror/lib/codemirror",
        "jquery": "../lib/jquery",
        "base64": "../lib/base64",
        "mustache": "../lib/mustache",
        "underscore": "../lib/underscore-min",
        "jquery-ui": "../lib/jquery-ui-1.10.4.custom/js/jquery-ui-1.10.4.custom.min",
        "jquery-appear": "../lib/jquery-appear",
        "backbone": "../lib/backbone",
        "jsdiff": "../lib/jsdiff",
        "spin": "../lib/spin",
        "mousewheel": "../lib/jquery-mousewheel",
        "text": "../lib/text",
        "qtip": "../lib/jquery.qtip.custom/jquery.qtip"

    },
    'shim': {
        'codemirror': {
            deps:['jquery'],
            exports:'CodeMirror'
        },
        'backbone': {
            deps:['jquery','underscore'],
            exports:'Backbone'
        },
        'bootstrap': {
            deps:['jquery','jquery-ui']
        },
        'underscore': {
            deps:[],
            exports:'_'
        }
    }
});

$.fn.selectRange = function(start, end) {
    return this.each(function() {
        if(this.setSelectionRange) {
            this.focus();
            this.setSelectionRange(start, end);
        } else if(this.createTextRange) {
            var range = this.createTextRange();
            range.collapse(true);
            range.moveEnd('character', end);
            range.moveStart('character', start);
            range.select();
        }
    });
};

$.fn.getCursorPosEnd = function() {
    var pos = 0;
    var input = this.get(0);
    // IE Support
    if (document.selection) {
        input.focus();
        var sel = document.selection.createRange();
        pos = sel.text.length;
    }
    // Firefox support
    else if (input.selectionStart || input.selectionStart == '0')
        pos = input.selectionEnd;
    return pos;
};

require(
    [
        "jquery",
        "geometry",
        'events',
        "jquery-appear",
        "bootstrap",
        "mustache",
        "underscore",
        "jquery-ui",
        "backbone",
        "codemirror",
        "toolbar",
        "facsimile",
        "editor",
        "router",
        "spin",
        "spinner",
        "page",
        "mousewheel",
        "language",
        "bibinfo",
        'container',
        'boxes',
        'compatibility',
        'dialog',
        'browser',
        'document',
        'qtip'
    ], function($, geometry,events) {

        try {
            console.log;
        } catch (err) {
            console = {
                log: function() {},
                trace: function() {}
            };
        }

        $(document).ready(function() {
            events.trigger('appReady');
            var textarea = $('.CodeMirror');

            var SAR = {};

            SAR.find = function(){
                var txt = textarea.val();
                var strSearchTerm = "tyhjä";

                txt = txt.toLowerCase();
                strSearchTerm = strSearchTerm.toLowerCase();

                var cursorPos = textarea.getCursorPosEnd();
                var termPos = txt.indexOf(strSearchTerm, cursorPos);

                if(termPos != -1){
                    textarea.selectRange(termPos, termPos+strSearchTerm.length);
                }else{
                    termPos = txt.indexOf(strSearchTerm);
                    if(termPos != -1){
                        textarea.selectRange(termPos, termPos+strSearchTerm.length)
                    }else{
                        console.log("not found");
                    }
                }
            }
        });
});
<|endoftext|>


<commit_before>'use strict';

function repo_drawlogic(){
    entity_group_modify({
      'groups': [
        'canvas',
      ],
      'todo': function(entity){
          canvas_setproperties({
            'properties': {
              'fillStyle': entity_entities[entity]['color'],
            },
          });
          canvas_buffer.fillRect(
            entity_entities[entity]['x'],
            entity_entities[entity]['y'],
            entity_entities[entity]['width'],
            entity_entities[entitay]['height']
          );
      },
    });
}

function repo_logic(){
    if(core_keys[core_storage_data['move-↑']]['state']){
        rotation_rate += .0001;
    }
    if(core_keys[core_storage_data['move-↓']]['state']){
        rotation_rate -= .0001;
    }

    entity_group_modify({
      'groups': [
        'canvas',
      ],
      'todo': function(entity){
          entity_entities[entity]['rotation'] += rotation_rate * (core_storage_data['rings'] - entity_entities[entity]['layer'] + 1);
          if(entity_entities[entity]['rotation'] >= math_tau){
              entity_entities[entity]['rotation'] -= math_tau;
          }else if(entity_entities[entity]['rotation'] < 0){
              entity_entities[entity]['rotation'] += math_tau;
          }

          entity_entities[entity]['x'] = canvas_properties['width-half'] + entity_entities[entity]['layer'] * 10 * Math.cos(entity_entities[entity]['rotation']);
          entity_entities[entity]['y'] = canvas_properties['height-half'] + entity_entities[entity]['layer'] * 10 * Math.sin(entity_entities[entity]['rotation']);
      },
    });

    core_ui_update({
      'ids': {
        'rotation': core_round({
          'decimals': 4,
          'number': rotation_rate,
         }),
      },
    });
}

function repo_init(){
    core_repo_init({
      'events': {
        'restart': {
          'onclick': core_repo_reset,
        },
      },
      'globals': {
        'rotation_rate': 0,
      },
      'info': '<input id=restart type=button value=Restart>',
      'reset': canvas_setmode,
      'storage': {
        'rings': 23,
      },
      'storage-menu': '

<input class=mini id=rings min=1 step=any type=number>Rings
',
      'title': 'QjnyYap.htm',
      'ui': ' Rotation',
    });
    canvas_init();
}
<commit_msg>added mini class to storage inputs<commit_after>'use strict';

function repo_drawlogic(){
    entity_group_modify({
      'groups': [
        'canvas',
      ],
      'todo': function(entity){
          canvas_setproperties({
            'properties': {
              'fillStyle': entity_entities[entity]['color'],
            },
          });
          canvas_buffer.fillRect(
            entity_entities[entity]['x'],
            entity_entities[entity]['y'],
            entity_entities[entity]['width'],
            entity_entities[entity]['height']
          );
      },
    });
}

function repo_logic(){
    if(core_keys[core_storage_data['move-↑']]['state']){
        rotation_rate += .0001;
    }
    if(core_keys[core_storage_data['move-↓']]['state']){
        rotation_rate -= .0001;
    }

    entity_group_modify({
      'groups': [
        'canvas',
      ],
      'todo': function(entity){
          entity_entities[entity]['rotation'] += rotation_rate * (core_storage_data['rings'] - entity_entities[entity]['layer'] + 1);
          if(entity_entities[entity]['rotation'] >= math_tau){
              entity_entities[entity]['rotation'] -= math_tau;
          }else if(entity_entities[entity]['rotation'] < 0){
              entity_entities[entity]['rotation'] += math_tau;
          }

          entity_entities[entity]['x'] = canvas_properties['width-half'] + entity_entities[entity]['layer'] * 10 * Math.cos(entity_entities[entity]['rotation']);
          entity_entities[entity]['y'] = canvas_properties['height-half'] + entity_entities[entity]['layer'] * 10 * Math.sin(entity_entities[entity]['rotation']);
      },
    });

    core_ui_update({
      'ids': {
        'rotation': core_round({
          'decimals': 4,
          'number': rotation_rate,
         }),
      },
    });
}

function repo_init(){
    core_repo_init({
      'events': {
        'restart': {
          'onclick': core_repo_reset,
        },
      },
      'globals': {
        'rotation_rate': 0,
      },
      'info': '<input id=restart type=button value=Restart>',
      'reset': canvas_setmode,
      'storage': {
        'rings': 23,
      },
      'storage-menu': '

<input class=mini id=rings min=1 step=any type=number>Rings
',
      'title': 'QjnyYap.htm',
      'ui': ' Rotation',
    });
    canvas_init();
}
<|endoftext|>


<commit_before>function on_index_html(html) {
    // Look for patterns like:  href="couchbase-server-community_x86_64_2.0.0c-709-rel.rpm.manifest.xml"

    var urls = {}
    var hier = {} // Hierarchy of maps, keyed by name / version / build / arch / pkg.

    var hrefs = html.match(/href="[^\"]+\.manifest\.xml"/g);
    for (var i = 0; i < hrefs.length; i++) {
        var href = hrefs[i];
        var url = href.split('"')[1];
        var s = url.split('_');
        var name = s[0];
        var arch = s.slice(1, s.length - 1).join("_");
        var suffix = s[s.length - 1].split('-');
        var version = suffix[0];
        var build = suffix[1];
        if (suffix && suffix[2]) {
            var pkg = suffix[2].split('.')[suffix[2].split('.').length - 3];

            var p = { "url": url,
                      "name": name,
                      "version": version,
                      "build": build,
                      "arch": arch,
                      "pkg": pkg };
            urls[url] = p;

            add_entry(hier, [name, version, build, arch, pkg], p);
        }
    }

    $('#content').html(gen(hier));
}

function add_entry(hier, path, val) {
    for (var i = 0; i < path.length - 1; i++) {
        var m = hier[path[i]];
        if (!m) {
            m = hier[path[i]] = {};
        }
        hier = m;
    }
    hier[path[path.length - 1]] = val;
}

function gen(hier) {
    var r = [];
    var names = keys(hier).sort();
    for (var i0 = 0; i0 < names.length; i0++) {
        var name = names[i0];
        r[r.length] = '
' + name + '
';
        var versions = hier[name];
        var k1 = keys(versions).sort();
        for (var i1 = 0; i1 < k1.length; i1++) {
            var version = k1[i1];
            r[r.length] = '
' + version + '
';
            var builds = versions[version];
            var k2 = keys(builds).sort().reverse();
            for (var i2 = 0; i2 < k2.length; i2++) {
                var build = k2[i2];
                r[r.length] = '
' + build + '
';
                var archs = builds[build];
                var k3 = keys(archs).sort();
                for (var i3 = 0; i3 < k3.length; i3++) {
                    var arch = k3[i3];
                    r[r.length] = '
' + arch + '
';
                    var pkgs = archs[arch];
                    var k4 = keys(pkgs).sort();
                    for (var i4 = 0; i4 < k4.length; i4++) {
                        var pkg = k4[i4];
                        var p = pkgs[pkg];
                        var u = (p.url || '').replace('.manifest.xml', '');
                        r[r.length] = '
' + pkg + '
';
                    }
                    r[r.length] = '

';
                }
                r[r.length] = '

';
            }
            r[r.length] = '

';
        }
        r[r.length] = '

';
    }
    return r;
}

function keys(m) {
    var keys = [];
    for (var key in m) {
        keys[keys.length] = key;
    }
    return keys;
}

function on_ready() {
    $.get("../../index.html", null,
          function(response, textStatus, jqXHR){
              on_index_html(response);
          });
}

$(document).ready(on_ready);
<commit_msg>fixed relative url<commit_after>function on_index_html(html) {
    // Look for patterns like:  href="couchbase-server-community_x86_64_2.0.0c-709-rel.rpm.manifest.xml"

    var urls = {}
    var hier = {} // Hierarchy of maps, keyed by name / version / build / arch / pkg.

    var hrefs = html.match(/href="[^\"]+\.manifest\.xml"/g);
    for (var i = 0; i < hrefs.length; i++) {
        var href = hrefs[i];
        var url = href.split('"')[1];
        var s = url.split('_');
        var name = s[0];
        var arch = s.slice(1, s.length - 1).join("_");
        var suffix = s[s.length - 1].split('-');
        var version = suffix[0];
        var build = suffix[1];
        if (suffix && suffix[2]) {
            var pkg = suffix[2].split('.')[suffix[2].split('.').length - 3];

            var p = { "url": url,
                      "name": name,
                      "version": version,
                      "build": build,
                      "arch": arch,
                      "pkg": pkg };
            urls[url] = p;

            add_entry(hier, [name, version, build, arch, pkg], p);
        }
    }

    $('#content').html(gen(hier));
}

function add_entry(hier, path, val) {
    for (var i = 0; i < path.length - 1; i++) {
        var m = hier[path[i]];
        if (!m) {
            m = hier[path[i]] = {};
        }
        hier = m;
    }
    hier[path[path.length - 1]] = val;
}

function gen(hier) {
    var r = [];
    var names = keys(hier).sort();
    for (var i0 = 0; i0 < names.length; i0++) {
        var name = names[i0];
        r[r.length] = '
' + name + '
';
        var versions = hier[name];
        var k1 = keys(versions).sort();
        for (var i1 = 0; i1 < k1.length; i1++) {
            var version = k1[i1];
            r[r.length] = '
' + version + '
';
            var builds = versions[version];
            var k2 = keys(builds).sort().reverse();
            for (var i2 = 0; i2 < k2.length; i2++) {
                var build = k2[i2];
                r[r.length] = '
' + build + '
';
                var archs = builds[build];
                var k3 = keys(archs).sort();
                for (var i3 = 0; i3 < k3.length; i3++) {
                    var arch = k3[i3];
                    r[r.length] = '
' + arch + '
';
                    var pkgs = archs[arch];
                    var k4 = keys(pkgs).sort();
                    for (var i4 = 0; i4 < k4.length; i4++) {
                        var pkg = k4[i4];
                        var p = pkgs[pkg];
                        var u = (p.url || '').replace('.manifest.xml', '');
                        r[r.length] = '
' + pkg + '
';
                    }
                    r[r.length] = '

';
                }
                r[r.length] = '

';
            }
            r[r.length] = '

';
        }
        r[r.length] = '

';
    }
    return r;
}

function keys(m) {
    var keys = [];
    for (var key in m) {
        keys[keys.length] = key;
    }
    return keys;
}

function on_ready() {
    $.get("../../index.html", null,
          function(response, textStatus, jqXHR){
              on_index_html(response);
          });
}

$(document).ready(on_ready);
<|endoftext|>