<commit_before>(function() {

// Listen for storage changes.
chrome.storage.onChanged.addListener(function(changes, namespace) {
  for (key in changes) {
    if (key == "settings") {
     applySettings(changes[key].newValue);
   }
  }
});

// Updates column visibility from the settings object for visibility.
function updateColumnVisibility(visibility) {
  for (var column in visibility) {
    var $el = $(".poloniex-ninja." + column);
    if (visibility[column]) {
      $el.removeClass("poloniex-ninja-hidden");
    } else {
      $el.addClass("poloniex-ninja-hidden");
    }
  }
}

// Applies all settings to the page.
function applySettings(settings) {
  updateColumnVisibility(settings['balance_column_visibility']);
}

// Asnychronous version of the each method to keep responsiveness.
$.fn.extend({
  eachAsync: function(callback) {
    $(this).each(function() {
      var $element = $(this);
      setTimeout(function() {
        callback.call($element);
      }, 10);
    })
  }
});

// TODO: Use config from user storage obtained from background script.
var config = {
  'USD_DECIMALS': 2,
  'CHANGE_PERCENT_DECIMALS': 2,
  'AVG_BUY_PRICE_DECIMALS': 8,
  'BALANCE_PRECISION': 1e-7,
  'CHANGE_PRECISION': 0.01,
  'HISTORY_UPDATE_INTERVAL_MS': 60000
}

// The current state.
var state = {'data': null,
             'history': null,
             'avgBuyPriceOfCoin': null,
             'lastHistoryUpdate': 0};

function getTimestamp() {
  return + new Date();
}

// Returns the path portion of the current page without the leading slash.
function getPagePath() {
  var urlParts = window.location.href.split("/");
  return urlParts.splice(3).join("/");
}

// Adjusts the theme (dark/light) context.
function adjustTheme() {
  if ($("link[href*='dark']").length > 0) {
    $("body").addClass("poloniex-ninja-dark");
  }
}

// Loads the entire trade history for average buy value and change estimate.
// The results may be cached. A callback is executed with the results and
// a boolean argument determining whether the results are cached.
function loadTradeHistory(callback) {
  if ((getTimestamp() - state.lastHistoryUpdate) >
      config.HISTORY_UPDATE_INTERVAL_MS) {
    $.getJSON(
      "/private.php?command=returnPersonalTradeHistory&start=0&end=1917895987",
      function(history) {
        state.history = history;
        state.lastHistoryUpdate = getTimestamp();
        callback(history, /*cached=*/false);
      });
  } else {
    // Provide cached results.
    callback(state.history, /*cached=*/true);
  }
}

// Fires a callback once at init and each time DOM is modified.
function onInitAndChange(query, callback) {
  $(query).eachAsync(function() {
    var $element = $(this);
    $element.bind("DOMSubtreeModified", function(e) {
      callback($element);
    });
    callback($element);
  });
}

// Finds the cell in the provided row matching the given header text.
function getCellForHeader($rowElement, headerText) {
  var $matchingHeader =
      $("#balancesTable thead th div:contains('" + headerText + "')");
  if ($matchingHeader.length == 0)
    return null;
  var index = $matchingHeader.closest("th").prevAll().length;
  return $($rowElement).find("td:eq(" + index + ")");
}

// Returns a numeric value from inner HTML or null if not available.
function getFloatValueFromDom(query) {
  if ($(query).length == 0)
    return null;

  var value = parseFloat($(query).text());
  if (value != NaN && value != Infinity) {
    return value;
  }

  return null;
}

// Gets the total estimate of holdings in USD or null if not available.
function getUsdHoldings() {
  return getFloatValueFromDom("#accountValue_usd");
}

// Gets the total estimate of holdings in BTC or null if not available.
function getBtcHoldings() {
  return getFloatValueFromDom("#accountValue_btc");
}

// Returns the bitcoin value in the provided row or null if not available.
function getBitcoinValue($row) {
  var $bitcoinValueCell = getCellForHeader($row, "BTC Value");
  return getFloatValueFromDom($bitcoinValueCell);
}

// Updates USD balance on each row.
function updateUsdBalance($rowQuery) {
  $rowQuery.eachAsync(function() {
    if (!state.data)
      return;

    var $row = $(this);
    var bitcoinValue = getBitcoinValue($row);

    if (bitcoinValue == null)
      return;

    var usdValue =
        (bitcoinValue * state.data.btcPrice).toFixed(config.USD_DECIMALS);

    $row.find(".usd_value:first").html("$ " + usdValue);
  });
}

// Fetches the cached historical avgBuyPrice for a given row and calls the
// callback when data is available.
function getHistoricalSummary($row, callback) {
  var coin = $row.find("td.coin").text();
  if (coin == null || state.avgBuyPriceOfCoin == null ||
      !(coin in state.avgBuyPriceOfCoin)) {
    return;
  }

  var currentBalance = getFloatValueFromDom($row.find("td.balance"));
  var btcValue = getFloatValueFromDom($row.find("td.value"));

  var avgBuyPrice = state.avgBuyPriceOfCoin[coin];
  var avgBuyValue = avgBuyPrice * currentBalance;
  var changePercent = (btcValue - avgBuyValue) * 100 / avgBuyValue;

  callback({
    'avgBuyPrice': avgBuyPrice,
    'avgBuyValue': avgBuyValue,
    'changePercent': changePercent
  });
}

// Updates the average buy price and value in one or more rows.
function updateAvgBuyPriceAndValue($rowQuery) {
  $rowQuery.eachAsync(function() {
    var $row = $(this);
    getHistoricalSummary($row, function(r) {
      var $avgBuyPriceCell = $row.find("td.avg_buy_price");
      var $avgBuyValueCell = $row.find("td.avg_buy_value");
      $avgBuyPriceCell.html(
          r.avgBuyPrice.toFixed(config.AVG_BUY_PRICE_DECIMALS));
      $avgBuyValueCell.html(
          r.avgBuyValue.toFixed(config.AVG_BUY_PRICE_DECIMALS));
    });
  });
}

// Formats the change percentage.
function formatChangePercent(changePercent) {
  return (changePercent > 0 ? "+": "") +
         changePercent.toFixed(config.CHANGE_PERCENT_DECIMALS);
}

// Updates the change column in one or more rows.
function updateChangePercent($rowQuery) {
  $rowQuery.eachAsync(function() {
    var $row = $(this);
    getHistoricalSummary($row, function(r) {
      var $changePercentCell = $row.find("td.change_percent");
      $changePercentCell.html(formatChangePercent(r.changePercent));
      $changePercentCell
        .removeClass("neutral")
        .removeClass("positive")
        .removeClass("negative")
        .addClass(getChangeClass(r.changePercent));
    });
  });
}

// Gets the estimated value or 0 if not available.
function getBtcPriceEstimate() {
 var usdHoldings = getUsdHoldings();
 if (usdHoldings == null)
   return 0.0;

 var btcHoldings = getBtcHoldings();
 if (btcHoldings == null)
   return 0.0;

 if (btcHoldings == 0.0)
  return 0.0;

  return usdHoldings / btcHoldings;
}

// Get the row associated with the coin.
function getCoinRow(coinName) {
  return $("#balancesTable td.coin a:contains(" + coinName + ")").closest("tr");
}

// Returns the class of the change.
function getChangeClass(changePercent) {
  if (Math.abs(changePercent) < config.BALANCE_PRECISION) {
    return "neutral";
  } else if (changePercent < 0) {
    return "negative";
  } else {
    return "positive";
  }
}

// Computes the average buy price from the trade history.
function computeAvgBuyPrice(transactions, currentBalance) {
  var balance = currentBalance;
  var rates = [];
  var totalBuyAmount = 0.0;
  var computed = false;

  // Scrub through the history and determine the rates and amounts for buys.
  for (var i = 0; i < transactions.length; i++) {
    var t = transactions[i];
    var amount = parseFloat(t.amount);
    var fee = parseFloat(t.fee);
    var rate = parseFloat(t.rate);
    var sign = (t.type == "buy") ? 1 : -1;
    var netAmount = amount * (1.0 - fee) * sign;
    balance -= netAmount;

    if (t.type == "buy") {
      rates.push({amount: netAmount, rate: rate});
      totalBuyAmount += netAmount;
    }

    if (balance < config.BALANCE_PRECISION)  {
      computed = true;
      break;
    }
  }

  // Failed accoutning for the balance, may be mixture of other currencies.
  if (!computed)
    return null;

  // Compute the average buy price.
  var avgBuyPrice = 0.0;
  for (var i = 0; i < rates.length; i++) {
    avgBuyPrice += rates[i].rate * (rates[i].amount / totalBuyAmount);
  }

  return avgBuyPrice;
}

// Fires the callback once the progress bar has loaded.
function onProgressComplete(callback) {
  var remainingTime = 30000;
  var period = 10;
  var interval = setInterval(function() {
    remainingTime -= period;
    if (remainingTime <= 0) {
      clearInterval(interval);
      return;
    }
    var style = $("#wdProgress").attr("style");
    if (style && parseFloat(style.split(': ').pop()) >= 50) {
      callback();
      clearInterval(interval);
    }
  }, period);
}

// Loads the historical data and saves to the state cache.
// If a callback is provided, it will execute once the cache has been updated.
function computeAvgBuyPriceAsync(callback) {
  loadTradeHistory(function(history, cached) {
    // We don't need to recompute on the cached history.
    if (cached && callback) {
      callback();
      return;
    }

    state.avgBuyPriceOfCoin = {}
    for (var pair in history) {
      var parts = pair.split("_");
      var baseCoin = parts[0];

      // FIXME: Only supporting BTC trading for now.
      if (baseCoin != "BTC") {
        continue;
      }

      var targetCoin = parts[1];
      var $row = getCoinRow(targetCoin);
      var currentBalance = getFloatValueFromDom($row.find("td.balance"));

      // Skip near zero balance.
      if (currentBalance < config.BALANCE_PRECISION) {
        continue;
      }

      // Store the results.
      state.avgBuyPriceOfCoin[targetCoin] =
          computeAvgBuyPrice(history[pair], currentBalance);
    }

    if (callback) {
      callback();
    }
  });
}

// Adds the extra columns for the current balances.
function addExtraBalanceTableColumns() {
  console.info("PoloNinja: Adding extra balance columns.");

  state.data = {btcPrice: getBtcPriceEstimate()};

  // Fix the dynamic rows on deposit/withdraw.
  $("#balancesTable tbody").on("DOMNodeInserted", "tr", function(e) {
    if ($(e.target).attr("id") == "actionRow") {
      $(e.target).find("td:first").attr(
          "colspan", $("#balancesTable thead tr th").length);
    }
  });

  // Attach headers.
  var $lastHeader = $("#balancesTable thead:first tr th:last");
  $("AVG Buy Price	")
      .insertBefore($lastHeader);
  $("AVG Buy Value	")
      .insertBefore($lastHeader);
  $("Change	")
      .insertBefore($lastHeader);
  $("USD Value	")
      .insertBefore($lastHeader);

  // Add extra cells for USD as each row is inserted.
  $("#balancesTable tbody").on("DOMNodeInserted", "tr", function(x) {
    if (x.target.nodeName != "TR" || !$(x.target).hasClass("filterable"))
      return;

    var $row = $(x.target);

    // Skip row if already initialized.
    if ($row.find(".usd_value").length > 0)
      return;

    // Add placeholder cells.
    var $lc = $row.find("td:last");
    $("n/a	").insertBefore($lc);
    $("n/a	").insertBefore($lc);
    $("n/a	").insertBefore($lc);
    $("n/a	").insertBefore($lc);
  });

  // Once the progress bar has indicated that stuff is loaded, fill out the
  // columns and bind events.
  onProgressComplete(function() {
    // Apply current settings.
    getAllSettings(applySettings);

    computeAvgBuyPriceAsync(function() {
      var $filterable = $("#balancesTable tbody tr.filterable");

      // Update avg buy price and value as total balance changes.
      onInitAndChange(
          $filterable.find("td.balance"),
          function($cell) {
            computeAvgBuyPriceAsync(function() {
              updateAvgBuyPriceAndValue($cell.closest("tr"));
            });
          });

      // Update the change column as avg buy value or btc value changes.
      onInitAndChange(
          $filterable.find("td.value, td.avg_buy_value, td.avg_buy_price"),
          function($cell) {
            updateChangePercent($cell.closest("tr"));
          });

      // Update USD prices as bitcoin value changes.
      onInitAndChange(
          $filterable.find("td.value"),
          function($cell) {
            updateUsdBalance($cell.closest("tr"));
          });

      // Update all extra columns as the price changes.
      onInitAndChange(
          "#accountValue_btc",
          function() {
            state.data.btcPrice = getBtcPriceEstimate();
            updateUsdBalance($("#balancesTable tbody tr.filterable"));
            computeAvgBuyPriceAsync(function() {
              updateAvgBuyPriceAndValue(
                  $("#balancesTable tbody tr.filterable"));
            });
          });
    });  // computeAvgBuyPriceAsync
  });  // onProgressComplete
}

// Program entry point.
function main() {
  // Match the page and apply a DOM layer.
  var doAdjustTheme = true;
  if (getPagePath().match(/balances.*/)) {
    addExtraBalanceTableColumns();
  } else {
    console.info("PoloNinja: No modifications were done on this page.");
    doAdjustTheme = false;
  }

  if (doAdjustTheme) {
    adjustTheme();
  }

  // Apply current settings.
  getAllSettings(applySettings);
}

main();

})();  // Scope isolation.
<commit_msg>improve getCoinRow to fetch the correct row for the given coin Fix #3<commit_after>(function() {

// Listen for storage changes.
chrome.storage.onChanged.addListener(function(changes, namespace) {
  for (key in changes) {
    if (key == "settings") {
     applySettings(changes[key].newValue);
   }
  }
});

// Updates column visibility from the settings object for visibility.
function updateColumnVisibility(visibility) {
  for (var column in visibility) {
    var $el = $(".poloniex-ninja." + column);
    if (visibility[column]) {
      $el.removeClass("poloniex-ninja-hidden");
    } else {
      $el.addClass("poloniex-ninja-hidden");
    }
  }
}

// Applies all settings to the page.
function applySettings(settings) {
  updateColumnVisibility(settings['balance_column_visibility']);
}

// Asnychronous version of the each method to keep responsiveness.
$.fn.extend({
  eachAsync: function(callback) {
    $(this).each(function() {
      var $element = $(this);
      setTimeout(function() {
        callback.call($element);
      }, 10);
    })
  }
});

// TODO: Use config from user storage obtained from background script.
var config = {
  'USD_DECIMALS': 2,
  'CHANGE_PERCENT_DECIMALS': 2,
  'AVG_BUY_PRICE_DECIMALS': 8,
  'BALANCE_PRECISION': 1e-7,
  'CHANGE_PRECISION': 0.01,
  'HISTORY_UPDATE_INTERVAL_MS': 60000
}

// The current state.
var state = {'data': null,
             'history': null,
             'avgBuyPriceOfCoin': null,
             'lastHistoryUpdate': 0};

function getTimestamp() {
  return + new Date();
}

// Returns the path portion of the current page without the leading slash.
function getPagePath() {
  var urlParts = window.location.href.split("/");
  return urlParts.splice(3).join("/");
}

// Adjusts the theme (dark/light) context.
function adjustTheme() {
  if ($("link[href*='dark']").length > 0) {
    $("body").addClass("poloniex-ninja-dark");
  }
}

// Loads the entire trade history for average buy value and change estimate.
// The results may be cached. A callback is executed with the results and
// a boolean argument determining whether the results are cached.
function loadTradeHistory(callback) {
  if ((getTimestamp() - state.lastHistoryUpdate) >
      config.HISTORY_UPDATE_INTERVAL_MS) {
    $.getJSON(
      "/private.php?command=returnPersonalTradeHistory&start=0&end=1917895987",
      function(history) {
        state.history = history;
        state.lastHistoryUpdate = getTimestamp();
        callback(history, /*cached=*/false);
      });
  } else {
    // Provide cached results.
    callback(state.history, /*cached=*/true);
  }
}

// Fires a callback once at init and each time DOM is modified.
function onInitAndChange(query, callback) {
  $(query).eachAsync(function() {
    var $element = $(this);
    $element.bind("DOMSubtreeModified", function(e) {
      callback($element);
    });
    callback($element);
  });
}

// Finds the cell in the provided row matching the given header text.
function getCellForHeader($rowElement, headerText) {
  var $matchingHeader =
      $("#balancesTable thead th div:contains('" + headerText + "')");
  if ($matchingHeader.length == 0)
    return null;
  var index = $matchingHeader.closest("th").prevAll().length;
  return $($rowElement).find("td:eq(" + index + ")");
}

// Returns a numeric value from inner HTML or null if not available.
function getFloatValueFromDom(query) {
  if ($(query).length == 0)
    return null;

  var value = parseFloat($(query).text());
  if (value != NaN && value != Infinity) {
    return value;
  }

  return null;
}

// Gets the total estimate of holdings in USD or null if not available.
function getUsdHoldings() {
  return getFloatValueFromDom("#accountValue_usd");
}

// Gets the total estimate of holdings in BTC or null if not available.
function getBtcHoldings() {
  return getFloatValueFromDom("#accountValue_btc");
}

// Returns the bitcoin value in the provided row or null if not available.
function getBitcoinValue($row) {
  var $bitcoinValueCell = getCellForHeader($row, "BTC Value");
  return getFloatValueFromDom($bitcoinValueCell);
}

// Updates USD balance on each row.
function updateUsdBalance($rowQuery) {
  $rowQuery.eachAsync(function() {
    if (!state.data)
      return;

    var $row = $(this);
    var bitcoinValue = getBitcoinValue($row);

    if (bitcoinValue == null)
      return;

    var usdValue =
        (bitcoinValue * state.data.btcPrice).toFixed(config.USD_DECIMALS);

    $row.find(".usd_value:first").html("$ " + usdValue);
  });
}

// Fetches the cached historical avgBuyPrice for a given row and calls the
// callback when data is available.
function getHistoricalSummary($row, callback) {
  var coin = $row.find("td.coin").text();
  if (coin == null || state.avgBuyPriceOfCoin == null ||
      !(coin in state.avgBuyPriceOfCoin)) {
    return;
  }

  var currentBalance = getFloatValueFromDom($row.find("td.balance"));
  var btcValue = getFloatValueFromDom($row.find("td.value"));

  var avgBuyPrice = state.avgBuyPriceOfCoin[coin];
  var avgBuyValue = avgBuyPrice * currentBalance;
  var changePercent = (btcValue - avgBuyValue) * 100 / avgBuyValue;

  callback({
    'avgBuyPrice': avgBuyPrice,
    'avgBuyValue': avgBuyValue,
    'changePercent': changePercent
  });
}

// Updates the average buy price and value in one or more rows.
function updateAvgBuyPriceAndValue($rowQuery) {
  $rowQuery.eachAsync(function() {
    var $row = $(this);
    getHistoricalSummary($row, function(r) {
      var $avgBuyPriceCell = $row.find("td.avg_buy_price");
      var $avgBuyValueCell = $row.find("td.avg_buy_value");
      $avgBuyPriceCell.html(
          r.avgBuyPrice.toFixed(config.AVG_BUY_PRICE_DECIMALS));
      $avgBuyValueCell.html(
          r.avgBuyValue.toFixed(config.AVG_BUY_PRICE_DECIMALS));
    });
  });
}

// Formats the change percentage.
function formatChangePercent(changePercent) {
  return (changePercent > 0 ? "+": "") +
         changePercent.toFixed(config.CHANGE_PERCENT_DECIMALS);
}

// Updates the change column in one or more rows.
function updateChangePercent($rowQuery) {
  $rowQuery.eachAsync(function() {
    var $row = $(this);
    getHistoricalSummary($row, function(r) {
      var $changePercentCell = $row.find("td.change_percent");
      $changePercentCell.html(formatChangePercent(r.changePercent));
      $changePercentCell
        .removeClass("neutral")
        .removeClass("positive")
        .removeClass("negative")
        .addClass(getChangeClass(r.changePercent));
    });
  });
}

// Gets the estimated value or 0 if not available.
function getBtcPriceEstimate() {
 var usdHoldings = getUsdHoldings();
 if (usdHoldings == null)
   return 0.0;

 var btcHoldings = getBtcHoldings();
 if (btcHoldings == null)
   return 0.0;

 if (btcHoldings == 0.0)
  return 0.0;

  return usdHoldings / btcHoldings;
}

// Get the row associated with the coin.
function getCoinRow(coinName) {
  return $("#balancesTable tr[data-url='" + coinName + "']");
}

// Returns the class of the change.
function getChangeClass(changePercent) {
  if (Math.abs(changePercent) < config.BALANCE_PRECISION) {
    return "neutral";
  } else if (changePercent < 0) {
    return "negative";
  } else {
    return "positive";
  }
}

// Computes the average buy price from the trade history.
function computeAvgBuyPrice(transactions, currentBalance) {
  var balance = currentBalance;
  var rates = [];
  var totalBuyAmount = 0.0;
  var computed = false;

  // Scrub through the history and determine the rates and amounts for buys.
  for (var i = 0; i < transactions.length; i++) {
    var t = transactions[i];
    var amount = parseFloat(t.amount);
    var fee = parseFloat(t.fee);
    var rate = parseFloat(t.rate);
    var sign = (t.type == "buy") ? 1 : -1;
    var netAmount = amount * (1.0 - fee) * sign;
    balance -= netAmount;

    if (t.type == "buy") {
      rates.push({amount: netAmount, rate: rate});
      totalBuyAmount += netAmount;
    }

    if (balance < config.BALANCE_PRECISION)  {
      computed = true;
      break;
    }
  }

  // Failed accoutning for the balance, may be mixture of other currencies.
  if (!computed)
    return null;

  // Compute the average buy price.
  var avgBuyPrice = 0.0;
  for (var i = 0; i < rates.length; i++) {
    avgBuyPrice += rates[i].rate * (rates[i].amount / totalBuyAmount);
  }

  return avgBuyPrice;
}

// Fires the callback once the progress bar has loaded.
function onProgressComplete(callback) {
  var remainingTime = 30000;
  var period = 10;
  var interval = setInterval(function() {
    remainingTime -= period;
    if (remainingTime <= 0) {
      clearInterval(interval);
      return;
    }
    var style = $("#wdProgress").attr("style");
    if (style && parseFloat(style.split(': ').pop()) >= 50) {
      callback();
      clearInterval(interval);
    }
  }, period);
}

// Loads the historical data and saves to the state cache.
// If a callback is provided, it will execute once the cache has been updated.
function computeAvgBuyPriceAsync(callback) {
  loadTradeHistory(function(history, cached) {
    // We don't need to recompute on the cached history.
    if (cached && callback) {
      callback();
      return;
    }

    state.avgBuyPriceOfCoin = {}
    for (var pair in history) {
      var parts = pair.split("_");
      var baseCoin = parts[0];

      // FIXME: Only supporting BTC trading for now.
      if (baseCoin != "BTC") {
        continue;
      }

      var targetCoin = parts[1];
      var $row = getCoinRow(targetCoin);
      var currentBalance = getFloatValueFromDom($row.find("td.balance"));

      // Skip near zero balance.
      if (currentBalance < config.BALANCE_PRECISION) {
        continue;
      }

      // Store the results.
      state.avgBuyPriceOfCoin[targetCoin] =
          computeAvgBuyPrice(history[pair], currentBalance);
    }

    if (callback) {
      callback();
    }
  });
}

// Adds the extra columns for the current balances.
function addExtraBalanceTableColumns() {
  console.info("PoloNinja: Adding extra balance columns.");

  state.data = {btcPrice: getBtcPriceEstimate()};

  // Fix the dynamic rows on deposit/withdraw.
  $("#balancesTable tbody").on("DOMNodeInserted", "tr", function(e) {
    if ($(e.target).attr("id") == "actionRow") {
      $(e.target).find("td:first").attr(
          "colspan", $("#balancesTable thead tr th").length);
    }
  });

  // Attach headers.
  var $lastHeader = $("#balancesTable thead:first tr th:last");
  $("AVG Buy Price	")
      .insertBefore($lastHeader);
  $("AVG Buy Value	")
      .insertBefore($lastHeader);
  $("Change	")
      .insertBefore($lastHeader);
  $("USD Value	")
      .insertBefore($lastHeader);

  // Add extra cells for USD as each row is inserted.
  $("#balancesTable tbody").on("DOMNodeInserted", "tr", function(x) {
    if (x.target.nodeName != "TR" || !$(x.target).hasClass("filterable"))
      return;

    var $row = $(x.target);

    // Skip row if already initialized.
    if ($row.find(".usd_value").length > 0)
      return;

    // Add placeholder cells.
    var $lc = $row.find("td:last");
    $("n/a	").insertBefore($lc);
    $("n/a	").insertBefore($lc);
    $("n/a	").insertBefore($lc);
    $("n/a	").insertBefore($lc);
  });

  // Once the progress bar has indicated that stuff is loaded, fill out the
  // columns and bind events.
  onProgressComplete(function() {
    // Apply current settings.
    getAllSettings(applySettings);

    computeAvgBuyPriceAsync(function() {
      var $filterable = $("#balancesTable tbody tr.filterable");

      // Update avg buy price and value as total balance changes.
      onInitAndChange(
          $filterable.find("td.balance"),
          function($cell) {
            computeAvgBuyPriceAsync(function() {
              updateAvgBuyPriceAndValue($cell.closest("tr"));
            });
          });

      // Update the change column as avg buy value or btc value changes.
      onInitAndChange(
          $filterable.find("td.value, td.avg_buy_value, td.avg_buy_price"),
          function($cell) {
            updateChangePercent($cell.closest("tr"));
          });

      // Update USD prices as bitcoin value changes.
      onInitAndChange(
          $filterable.find("td.value"),
          function($cell) {
            updateUsdBalance($cell.closest("tr"));
          });

      // Update all extra columns as the price changes.
      onInitAndChange(
          "#accountValue_btc",
          function() {
            state.data.btcPrice = getBtcPriceEstimate();
            updateUsdBalance($("#balancesTable tbody tr.filterable"));
            computeAvgBuyPriceAsync(function() {
              updateAvgBuyPriceAndValue(
                  $("#balancesTable tbody tr.filterable"));
            });
          });
    });  // computeAvgBuyPriceAsync
  });  // onProgressComplete
}

// Program entry point.
function main() {
  // Match the page and apply a DOM layer.
  var doAdjustTheme = true;
  if (getPagePath().match(/balances.*/)) {
    addExtraBalanceTableColumns();
  } else {
    console.info("PoloNinja: No modifications were done on this page.");
    doAdjustTheme = false;
  }

  if (doAdjustTheme) {
    adjustTheme();
  }

  // Apply current settings.
  getAllSettings(applySettings);
}

main();

})();  // Scope isolation.
<|endoftext|>


<commit_before>/**
 * content.js
 */
"use strict";
{
  /* api */
  const {i18n, runtime} = browser;

  /**
   * log error
   * @param {!Object} e - Error
   * @returns {boolean} - false
   */
  const logError = e => {
    console.error(e);
    return false;
  };

  /**
   * is string
   * @param {*} o - object to check
   * @returns {boolean} - result
   */
  const isString = o => typeof o === "string" || o instanceof String;

  /**
   * copy to clipboard
   * @param {string} text - text to copy
   * @returns {void} - Promise.<void>
   */
  const copyToClipboard = async text => {
    if (isString(text)) {
      const body = document.querySelector("body");
      const elm = document.createElement("div");
      const range = document.createRange();
      const selection = window.getSelection();
      elm.textContent = text;
      elm.style.all = "unset";
      elm.style.appearance = "none";
      elm.style.width = "0";
      elm.style.height = "0";
      elm.style.position = "absolute";
      body.append(elm);
      range.selectNodeContents(elm);
      selection.addRange(range);
      document.execCommand("copy");
      selection.removeRange(selection.getRangeAt(selection.rangeCount - 1));
      body.removeChild(elm);
    }
  };

  /**
   * get user input
   * @param {string} value - value
   * @returns {string} - input value
   */
  const getInput = async (value = "") => {
    const msg = await i18n.getMessage("userInput");
    value = await window.prompt(msg, value);
    return value;
  };

  /**
   * create HTML Anchor
   * @param {string} content - content
   * @param {string} title - title
   * @param {string} url - title
   * @returns {Object} - Promise.<?string>
   */
  const createHtml = async (content, title, url) =>
    isString(content) && isString(title) && isString(url) &&
    `<a href="${url}" title="${title}">${content}</a>` || null;

  /**
   * create Markdown Link
   * @param {string} content - content
   * @param {string} title - title
   * @param {string} url - title
   * @returns {Object} - Promise.<string>
   */
  const createMarkdown = async (content, title, url) =>
    isString(content) && isString(title) && isString(url) &&
    `[${content}](${url} "${title}")` || null;

  /**
   * create Text Link
   * @param {string} content - content
   * @param {string} url - title
   * @returns {Object} - Promise.<?string>
   */
  const createText = async (content, url) =>
    isString(content) && isString(url) && `${content} <${url}>` || null;

  /**
   * extract message
   * @param {*} msg - message
   * @returns {Object} - Promise.<?AsyncFunction>
   */
  const extractMsg = async (msg = {}) => {
    const {data, input, menuItemId} = msg;
    let func;
    if (data) {
      const {info, tab} = data;
      if (info && tab) {
        const {selectionText} = info;
        const {title, url} = tab;
        const content = input && await getInput(selectionText || title) ||
                        selectionText || title;
        switch (menuItemId) {
          case "htmlAnchor":
          case "htmlAnchor.input":
            func = createHtml(content, title, url).then(copyToClipboard);
            break;
          case "markdownLink":
          case "markdownLink.input":
            func = createMarkdown(content, title, url).then(copyToClipboard);
            break;
          case "textLink":
          case "textLink.input":
            func = createText(content, url).then(copyToClipboard);
            break;
          default:
        }
      }
    }
    return func || null;
  };

  /**
   * send status
   * @param {!Object} evt - event
   * @returns {Object} - Promise.<AsincFunction>
   */
  const sendStatus = async evt => {
    const enabled = /^(?:application\/(?:(?:[\w\-.]+\+)?xml|image\/[\w\-.]+\+xml|text\/(?:ht|x)ml)$/.test(document.contentType);
    const msg = {
      [evt.type]: enabled,
    };
    return runtime.sendMessage(msg);
  };

  /* listeners */
  runtime.onMessage.addListener(msg => extractMsg(msg).catch(logError));
  document.addEventListener(
    "DOMContentLoaded", evt => sendStatus(evt).catch(logError), false
  );
  window.addEventListener(
    "contextmenu", evt => sendStatus(evt).catch(logError), false
  );
}
<commit_msg>Fix RegExp<commit_after>/**
 * content.js
 */
"use strict";
{
  /* api */
  const {i18n, runtime} = browser;

  /**
   * log error
   * @param {!Object} e - Error
   * @returns {boolean} - false
   */
  const logError = e => {
    console.error(e);
    return false;
  };

  /**
   * is string
   * @param {*} o - object to check
   * @returns {boolean} - result
   */
  const isString = o => typeof o === "string" || o instanceof String;

  /**
   * copy to clipboard
   * @param {string} text - text to copy
   * @returns {void} - Promise.<void>
   */
  const copyToClipboard = async text => {
    if (isString(text)) {
      const body = document.querySelector("body");
      const elm = document.createElement("div");
      const range = document.createRange();
      const selection = window.getSelection();
      elm.textContent = text;
      elm.style.all = "unset";
      elm.style.appearance = "none";
      elm.style.width = "0";
      elm.style.height = "0";
      elm.style.position = "absolute";
      body.append(elm);
      range.selectNodeContents(elm);
      selection.addRange(range);
      document.execCommand("copy");
      selection.removeRange(selection.getRangeAt(selection.rangeCount - 1));
      body.removeChild(elm);
    }
  };

  /**
   * get user input
   * @param {string} value - value
   * @returns {string} - input value
   */
  const getInput = async (value = "") => {
    const msg = await i18n.getMessage("userInput");
    value = await window.prompt(msg, value);
    return value;
  };

  /**
   * create HTML Anchor
   * @param {string} content - content
   * @param {string} title - title
   * @param {string} url - title
   * @returns {Object} - Promise.<?string>
   */
  const createHtml = async (content, title, url) =>
    isString(content) && isString(title) && isString(url) &&
    `<a href="${url}" title="${title}">${content}</a>` || null;

  /**
   * create Markdown Link
   * @param {string} content - content
   * @param {string} title - title
   * @param {string} url - title
   * @returns {Object} - Promise.<string>
   */
  const createMarkdown = async (content, title, url) =>
    isString(content) && isString(title) && isString(url) &&
    `[${content}](${url} "${title}")` || null;

  /**
   * create Text Link
   * @param {string} content - content
   * @param {string} url - title
   * @returns {Object} - Promise.<?string>
   */
  const createText = async (content, url) =>
    isString(content) && isString(url) && `${content} <${url}>` || null;

  /**
   * extract message
   * @param {*} msg - message
   * @returns {Object} - Promise.<?AsyncFunction>
   */
  const extractMsg = async (msg = {}) => {
    const {data, input, menuItemId} = msg;
    let func;
    if (data) {
      const {info, tab} = data;
      if (info && tab) {
        const {selectionText} = info;
        const {title, url} = tab;
        const content = input && await getInput(selectionText || title) ||
                        selectionText || title;
        switch (menuItemId) {
          case "htmlAnchor":
          case "htmlAnchor.input":
            func = createHtml(content, title, url).then(copyToClipboard);
            break;
          case "markdownLink":
          case "markdownLink.input":
            func = createMarkdown(content, title, url).then(copyToClipboard);
            break;
          case "textLink":
          case "textLink.input":
            func = createText(content, url).then(copyToClipboard);
            break;
          default:
        }
      }
    }
    return func || null;
  };

  /**
   * send status
   * @param {!Object} evt - event
   * @returns {Object} - Promise.<AsincFunction>
   */
  const sendStatus = async evt => {
    const enabled = /^(?:application\/(?:(?:[\w\-.]+\+)?xml)|image\/[\w\-.]+\+xml|text\/(?:ht|x)ml)$/.test(document.contentType);
    const msg = {
      [evt.type]: enabled,
    };
    return runtime.sendMessage(msg);
  };

  /* listeners */
  runtime.onMessage.addListener(msg => extractMsg(msg).catch(logError));
  document.addEventListener(
    "DOMContentLoaded", evt => sendStatus(evt).catch(logError), false
  );
  window.addEventListener(
    "contextmenu", evt => sendStatus(evt).catch(logError), false
  );
}
<|endoftext|>


<commit_before>import global from './global';
import DEFAULTS from './defaults';
import TEMPLATE from './template';
import render from './render';
import preview from './preview';
import events from './events';
import handlers from './handlers';
import change from './change';
import methods from './methods';
import {
  ACTION_ALL,
  CLASS_HIDDEN,
  CLASS_HIDE,
  CLASS_INVISIBLE,
  CLASS_MODAL,
  CLASS_MOVE,
  DATA_ACTION,
  EVENT_CROP,
  EVENT_ERROR,
  EVENT_LOAD,
  EVENT_READY,
  NAMESPACE,
  REGEXP_DATA_URL,
  REGEXP_DATA_URL_JPEG,
  REGEXP_TAG_NAME,
} from './constants';
import {
  addClass,
  addListener,
  addTimestamp,
  arrayBufferToDataURL,
  dataURLToArrayBuffer,
  dispatchEvent,
  extend,
  getData,
  getImageNaturalSizes,
  getOrientation,
  isCrossOriginURL,
  isFunction,
  isPlainObject,
  parseOrientation,
  proxy,
  removeClass,
  removeListener,
  setData,
} from './utilities';

const AnotherCropper = global.Cropper;

class Cropper {
  /**
   * Create a new Cropper.
   * @param {Element} element - The target element for cropping.
   * @param {Object} [options={}] - The configuration options.
   */
  constructor(element, options = {}) {
    if (!element || !REGEXP_TAG_NAME.test(element.tagName)) {
      throw new Error('The first argument is required and must be an <img> or <canvas> element.');
    }

    this.element = element;
    this.options = extend({}, DEFAULTS, isPlainObject(options) && options);
    this.complete = false;
    this.cropped = false;
    this.disabled = false;
    this.isImg = false;
    this.limited = false;
    this.loaded = false;
    this.ready = false;
    this.replaced = false;
    this.wheeling = false;
    this.originalUrl = '';
    this.canvasData = null;
    this.cropBoxData = null;
    this.previews = null;
    this.pointers = {};
    this.init();
  }

  init() {
    const { element } = this;
    const tagName = element.tagName.toLowerCase();
    let url;

    if (getData(element, NAMESPACE)) {
      return;
    }

    setData(element, NAMESPACE, this);

    if (tagName === 'img') {
      this.isImg = true;

      // e.g.: "img/picture.jpg"
      url = element.getAttribute('src');
      this.originalUrl = url;

      // Stop when it's a blank image
      if (!url) {
        return;
      }

      // e.g.: "http://example.com/img/picture.jpg"
      url = element.src;
    } else if (tagName === 'canvas' && global.HTMLCanvasElement) {
      url = element.toDataURL();
    }

    this.load(url);
  }

  load(url) {
    if (!url) {
      return;
    }

    this.url = url;
    this.imageData = {};

    const { element, options } = this;

    if (!options.checkOrientation || !global.ArrayBuffer) {
      this.clone();
      return;
    }

    // XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari
    if (REGEXP_DATA_URL.test(url)) {
      if (REGEXP_DATA_URL_JPEG.test(url)) {
        this.read(dataURLToArrayBuffer(url));
      } else {
        this.clone();
      }

      return;
    }

    const xhr = new XMLHttpRequest();

    xhr.onerror = () => {
      this.clone();
    };

    xhr.onload = () => {
      this.read(xhr.response);
    };

    if (options.checkCrossOrigin && isCrossOriginURL(url) && element.crossOrigin) {
      url = addTimestamp(url);
    }

    xhr.open('get', url);
    xhr.responseType = 'arraybuffer';
    xhr.withCredentials = element.crossOrigin === 'use-credentials';
    xhr.send();
  }

  read(arrayBuffer) {
    const { options, imageData } = this;
    const orientation = getOrientation(arrayBuffer);
    let rotate = 0;
    let scaleX = 1;
    let scaleY = 1;

    if (orientation > 1) {
      this.url = arrayBufferToDataURL(arrayBuffer, 'image/jpeg');
      ({ rotate, scaleX, scaleY } = parseOrientation(orientation));
    }

    if (options.rotatable) {
      imageData.rotate = rotate;
    }

    if (options.scalable) {
      imageData.scaleX = scaleX;
      imageData.scaleY = scaleY;
    }

    this.clone();
  }

  clone() {
    const { element, url } = this;
    let crossOrigin;
    let crossOriginUrl;

    if (this.options.checkCrossOrigin && isCrossOriginURL(url)) {
      ({ crossOrigin } = element);

      if (crossOrigin) {
        crossOriginUrl = url;
      } else {
        crossOrigin = 'anonymous';

        // Bust cache when there is not a "crossOrigin" property
        crossOriginUrl = addTimestamp(url);
      }
    }

    this.crossOrigin = crossOrigin;
    this.crossOriginUrl = crossOriginUrl;

    const image = document.createElement('img');

    if (crossOrigin) {
      image.crossOrigin = crossOrigin;
    }

    image.src = crossOriginUrl || url;

    const start = proxy(this.start, this);
    const stop = proxy(this.stop, this);

    this.image = image;
    this.onStart = start;
    this.onStop = stop;

    if (this.isImg) {
      if (element.complete) {
        this.start();
      } else {
        addListener(element, EVENT_LOAD, start);
      }
    } else {
      addListener(image, EVENT_LOAD, start);
      addListener(image, EVENT_ERROR, stop);
      addClass(image, CLASS_HIDE);
      element.parentNode.insertBefore(image, element.nextSibling);
    }
  }

  start(event) {
    const image = this.isImg ? this.element : this.image;

    if (event) {
      removeListener(image, EVENT_LOAD, this.onStart);
      removeListener(image, EVENT_ERROR, this.onStop);
    }

    getImageNaturalSizes(image, (naturalWidth, naturalHeight) => {
      extend(this.imageData, {
        naturalWidth,
        naturalHeight,
        aspectRatio: naturalWidth / naturalHeight,
      });
      this.loaded = true;
      this.build();
    });
  }

  stop() {
    const { image } = this;

    removeListener(image, EVENT_LOAD, this.onStart);
    removeListener(image, EVENT_ERROR, this.onStop);
    image.parentNode.removeChild(image);
    this.image = null;
  }

  build() {
    if (!this.loaded) {
      return;
    }

    // Unbuild first when replace
    if (this.ready) {
      this.unbuild();
    }

    const { element, options, image } = this;

    // Create cropper elements
    const container = element.parentNode;
    const template = document.createElement('div');

    template.innerHTML = TEMPLATE;

    const cropper = template.querySelector(`.${NAMESPACE}-container`);
    const canvas = cropper.querySelector(`.${NAMESPACE}-canvas`);
    const dragBox = cropper.querySelector(`.${NAMESPACE}-drag-box`);
    const cropBox = cropper.querySelector(`.${NAMESPACE}-crop-box`);
    const face = cropBox.querySelector(`.${NAMESPACE}-face`);

    this.container = container;
    this.cropper = cropper;
    this.canvas = canvas;
    this.dragBox = dragBox;
    this.cropBox = cropBox;
    this.viewBox = cropper.querySelector(`.${NAMESPACE}-view-box`);
    this.face = face;

    canvas.appendChild(image);

    // Hide the original image
    addClass(element, CLASS_HIDDEN);

    // Inserts the cropper after to the current image
    container.insertBefore(cropper, element.nextSibling);

    // Show the image if is hidden
    if (!this.isImg) {
      removeClass(image, CLASS_HIDE);
    }

    this.initPreview();
    this.bind();

    options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;
    options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;

    this.cropped = options.autoCrop;

    if (options.autoCrop) {
      if (options.modal) {
        addClass(dragBox, CLASS_MODAL);
      }
    } else {
      addClass(cropBox, CLASS_HIDDEN);
    }

    if (!options.guides) {
      addClass(cropBox.getElementsByClassName(`${NAMESPACE}-dashed`), CLASS_HIDDEN);
    }

    if (!options.center) {
      addClass(cropBox.getElementsByClassName(`${NAMESPACE}-center`), CLASS_HIDDEN);
    }

    if (options.background) {
      addClass(cropper, `${NAMESPACE}-bg`);
    }

    if (!options.highlight) {
      addClass(face, CLASS_INVISIBLE);
    }

    if (options.cropBoxMovable) {
      addClass(face, CLASS_MOVE);
      setData(face, DATA_ACTION, ACTION_ALL);
    }

    if (!options.cropBoxResizable) {
      addClass(cropBox.getElementsByClassName(`${NAMESPACE}-line`), CLASS_HIDDEN);
      addClass(cropBox.getElementsByClassName(`${NAMESPACE}-point`), CLASS_HIDDEN);
    }

    this.setDragMode(options.dragMode);
    this.render();
    this.ready = true;
    this.setData(options.data);

    // Call the "ready" option asynchronously to keep "image.cropper" is defined
    this.completing = setTimeout(() => {
      if (isFunction(options.ready)) {
        addListener(element, EVENT_READY, options.ready, {
          once: true,
        });
      }

      dispatchEvent(element, EVENT_READY);
      dispatchEvent(element, EVENT_CROP, this.getData());

      this.complete = true;
    }, 0);
  }

  unbuild() {
    if (!this.ready) {
      return;
    }

    if (!this.complete) {
      clearTimeout(this.completing);
    }

    this.ready = false;
    this.complete = false;
    this.initialImageData = null;

    // Clear `initialCanvasData` is necessary when replace
    this.initialCanvasData = null;
    this.initialCropBoxData = null;
    this.containerData = null;
    this.canvasData = null;

    // Clear `cropBoxData` is necessary when replace
    this.cropBoxData = null;
    this.unbind();
    this.resetPreview();
    this.previews = null;
    this.viewBox = null;
    this.cropBox = null;
    this.dragBox = null;
    this.canvas = null;
    this.container = null;
    this.cropper.parentNode.removeChild(this.cropper);
    this.cropper = null;
  }

  /**
   * Get the no conflict cropper class.
   * @returns {Cropper} The cropper class.
   */
  static noConflict() {
    global.Cropper = AnotherCropper;
    return Cropper;
  }

  /**
   * Change the default options.
   * @param {Object} options - The new default options.
   */
  static setDefaults(options) {
    extend(DEFAULTS, isPlainObject(options) && options);
  }
}

extend(Cropper.prototype, render, preview, events, handlers, change, methods);

export default Cropper;
<commit_msg>fix: fix the originalUrl when src is not present (#234)<commit_after>import global from './global';
import DEFAULTS from './defaults';
import TEMPLATE from './template';
import render from './render';
import preview from './preview';
import events from './events';
import handlers from './handlers';
import change from './change';
import methods from './methods';
import {
  ACTION_ALL,
  CLASS_HIDDEN,
  CLASS_HIDE,
  CLASS_INVISIBLE,
  CLASS_MODAL,
  CLASS_MOVE,
  DATA_ACTION,
  EVENT_CROP,
  EVENT_ERROR,
  EVENT_LOAD,
  EVENT_READY,
  NAMESPACE,
  REGEXP_DATA_URL,
  REGEXP_DATA_URL_JPEG,
  REGEXP_TAG_NAME,
} from './constants';
import {
  addClass,
  addListener,
  addTimestamp,
  arrayBufferToDataURL,
  dataURLToArrayBuffer,
  dispatchEvent,
  extend,
  getData,
  getImageNaturalSizes,
  getOrientation,
  isCrossOriginURL,
  isFunction,
  isPlainObject,
  parseOrientation,
  proxy,
  removeClass,
  removeListener,
  setData,
} from './utilities';

const AnotherCropper = global.Cropper;

class Cropper {
  /**
   * Create a new Cropper.
   * @param {Element} element - The target element for cropping.
   * @param {Object} [options={}] - The configuration options.
   */
  constructor(element, options = {}) {
    if (!element || !REGEXP_TAG_NAME.test(element.tagName)) {
      throw new Error('The first argument is required and must be an <img> or <canvas> element.');
    }

    this.element = element;
    this.options = extend({}, DEFAULTS, isPlainObject(options) && options);
    this.complete = false;
    this.cropped = false;
    this.disabled = false;
    this.isImg = false;
    this.limited = false;
    this.loaded = false;
    this.ready = false;
    this.replaced = false;
    this.wheeling = false;
    this.originalUrl = '';
    this.canvasData = null;
    this.cropBoxData = null;
    this.previews = null;
    this.pointers = {};
    this.init();
  }

  init() {
    const { element } = this;
    const tagName = element.tagName.toLowerCase();
    let url;

    if (getData(element, NAMESPACE)) {
      return;
    }

    setData(element, NAMESPACE, this);

    if (tagName === 'img') {
      this.isImg = true;

      // e.g.: "img/picture.jpg"
      url = element.getAttribute('src') || '';
      this.originalUrl = url;

      // Stop when it's a blank image
      if (!url) {
        return;
      }

      // e.g.: "http://example.com/img/picture.jpg"
      url = element.src;
    } else if (tagName === 'canvas' && global.HTMLCanvasElement) {
      url = element.toDataURL();
    }

    this.load(url);
  }

  load(url) {
    if (!url) {
      return;
    }

    this.url = url;
    this.imageData = {};

    const { element, options } = this;

    if (!options.checkOrientation || !global.ArrayBuffer) {
      this.clone();
      return;
    }

    // XMLHttpRequest disallows to open a Data URL in some browsers like IE11 and Safari
    if (REGEXP_DATA_URL.test(url)) {
      if (REGEXP_DATA_URL_JPEG.test(url)) {
        this.read(dataURLToArrayBuffer(url));
      } else {
        this.clone();
      }

      return;
    }

    const xhr = new XMLHttpRequest();

    xhr.onerror = () => {
      this.clone();
    };

    xhr.onload = () => {
      this.read(xhr.response);
    };

    if (options.checkCrossOrigin && isCrossOriginURL(url) && element.crossOrigin) {
      url = addTimestamp(url);
    }

    xhr.open('get', url);
    xhr.responseType = 'arraybuffer';
    xhr.withCredentials = element.crossOrigin === 'use-credentials';
    xhr.send();
  }

  read(arrayBuffer) {
    const { options, imageData } = this;
    const orientation = getOrientation(arrayBuffer);
    let rotate = 0;
    let scaleX = 1;
    let scaleY = 1;

    if (orientation > 1) {
      this.url = arrayBufferToDataURL(arrayBuffer, 'image/jpeg');
      ({ rotate, scaleX, scaleY } = parseOrientation(orientation));
    }

    if (options.rotatable) {
      imageData.rotate = rotate;
    }

    if (options.scalable) {
      imageData.scaleX = scaleX;
      imageData.scaleY = scaleY;
    }

    this.clone();
  }

  clone() {
    const { element, url } = this;
    let crossOrigin;
    let crossOriginUrl;

    if (this.options.checkCrossOrigin && isCrossOriginURL(url)) {
      ({ crossOrigin } = element);

      if (crossOrigin) {
        crossOriginUrl = url;
      } else {
        crossOrigin = 'anonymous';

        // Bust cache when there is not a "crossOrigin" property
        crossOriginUrl = addTimestamp(url);
      }
    }

    this.crossOrigin = crossOrigin;
    this.crossOriginUrl = crossOriginUrl;

    const image = document.createElement('img');

    if (crossOrigin) {
      image.crossOrigin = crossOrigin;
    }

    image.src = crossOriginUrl || url;

    const start = proxy(this.start, this);
    const stop = proxy(this.stop, this);

    this.image = image;
    this.onStart = start;
    this.onStop = stop;

    if (this.isImg) {
      if (element.complete) {
        this.start();
      } else {
        addListener(element, EVENT_LOAD, start);
      }
    } else {
      addListener(image, EVENT_LOAD, start);
      addListener(image, EVENT_ERROR, stop);
      addClass(image, CLASS_HIDE);
      element.parentNode.insertBefore(image, element.nextSibling);
    }
  }

  start(event) {
    const image = this.isImg ? this.element : this.image;

    if (event) {
      removeListener(image, EVENT_LOAD, this.onStart);
      removeListener(image, EVENT_ERROR, this.onStop);
    }

    getImageNaturalSizes(image, (naturalWidth, naturalHeight) => {
      extend(this.imageData, {
        naturalWidth,
        naturalHeight,
        aspectRatio: naturalWidth / naturalHeight,
      });
      this.loaded = true;
      this.build();
    });
  }

  stop() {
    const { image } = this;

    removeListener(image, EVENT_LOAD, this.onStart);
    removeListener(image, EVENT_ERROR, this.onStop);
    image.parentNode.removeChild(image);
    this.image = null;
  }

  build() {
    if (!this.loaded) {
      return;
    }

    // Unbuild first when replace
    if (this.ready) {
      this.unbuild();
    }

    const { element, options, image } = this;

    // Create cropper elements
    const container = element.parentNode;
    const template = document.createElement('div');

    template.innerHTML = TEMPLATE;

    const cropper = template.querySelector(`.${NAMESPACE}-container`);
    const canvas = cropper.querySelector(`.${NAMESPACE}-canvas`);
    const dragBox = cropper.querySelector(`.${NAMESPACE}-drag-box`);
    const cropBox = cropper.querySelector(`.${NAMESPACE}-crop-box`);
    const face = cropBox.querySelector(`.${NAMESPACE}-face`);

    this.container = container;
    this.cropper = cropper;
    this.canvas = canvas;
    this.dragBox = dragBox;
    this.cropBox = cropBox;
    this.viewBox = cropper.querySelector(`.${NAMESPACE}-view-box`);
    this.face = face;

    canvas.appendChild(image);

    // Hide the original image
    addClass(element, CLASS_HIDDEN);

    // Inserts the cropper after to the current image
    container.insertBefore(cropper, element.nextSibling);

    // Show the image if is hidden
    if (!this.isImg) {
      removeClass(image, CLASS_HIDE);
    }

    this.initPreview();
    this.bind();

    options.aspectRatio = Math.max(0, options.aspectRatio) || NaN;
    options.viewMode = Math.max(0, Math.min(3, Math.round(options.viewMode))) || 0;

    this.cropped = options.autoCrop;

    if (options.autoCrop) {
      if (options.modal) {
        addClass(dragBox, CLASS_MODAL);
      }
    } else {
      addClass(cropBox, CLASS_HIDDEN);
    }

    if (!options.guides) {
      addClass(cropBox.getElementsByClassName(`${NAMESPACE}-dashed`), CLASS_HIDDEN);
    }

    if (!options.center) {
      addClass(cropBox.getElementsByClassName(`${NAMESPACE}-center`), CLASS_HIDDEN);
    }

    if (options.background) {
      addClass(cropper, `${NAMESPACE}-bg`);
    }

    if (!options.highlight) {
      addClass(face, CLASS_INVISIBLE);
    }

    if (options.cropBoxMovable) {
      addClass(face, CLASS_MOVE);
      setData(face, DATA_ACTION, ACTION_ALL);
    }

    if (!options.cropBoxResizable) {
      addClass(cropBox.getElementsByClassName(`${NAMESPACE}-line`), CLASS_HIDDEN);
      addClass(cropBox.getElementsByClassName(`${NAMESPACE}-point`), CLASS_HIDDEN);
    }

    this.setDragMode(options.dragMode);
    this.render();
    this.ready = true;
    this.setData(options.data);

    // Call the "ready" option asynchronously to keep "image.cropper" is defined
    this.completing = setTimeout(() => {
      if (isFunction(options.ready)) {
        addListener(element, EVENT_READY, options.ready, {
          once: true,
        });
      }

      dispatchEvent(element, EVENT_READY);
      dispatchEvent(element, EVENT_CROP, this.getData());

      this.complete = true;
    }, 0);
  }

  unbuild() {
    if (!this.ready) {
      return;
    }

    if (!this.complete) {
      clearTimeout(this.completing);
    }

    this.ready = false;
    this.complete = false;
    this.initialImageData = null;

    // Clear `initialCanvasData` is necessary when replace
    this.initialCanvasData = null;
    this.initialCropBoxData = null;
    this.containerData = null;
    this.canvasData = null;

    // Clear `cropBoxData` is necessary when replace
    this.cropBoxData = null;
    this.unbind();
    this.resetPreview();
    this.previews = null;
    this.viewBox = null;
    this.cropBox = null;
    this.dragBox = null;
    this.canvas = null;
    this.container = null;
    this.cropper.parentNode.removeChild(this.cropper);
    this.cropper = null;
  }

  /**
   * Get the no conflict cropper class.
   * @returns {Cropper} The cropper class.
   */
  static noConflict() {
    global.Cropper = AnotherCropper;
    return Cropper;
  }

  /**
   * Change the default options.
   * @param {Object} options - The new default options.
   */
  static setDefaults(options) {
    extend(DEFAULTS, isPlainObject(options) && options);
  }
}

extend(Cropper.prototype, render, preview, events, handlers, change, methods);

export default Cropper;
<|endoftext|>


<commit_before>/*
 * Copyright (C) 2012-2016 InSeven Limited.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

Gameboy = {};

Gameboy.Key = {
  START: 7,
  SELECT: 6,
  A: 4,
  B: 5,
  UP: 2,
  DOWN: 3,
  LEFT: 1,
  RIGHT: 0
};

var gbologger = new App.Logging(window.config.logging_level, "gbo");
var saveStateContext;
var saveState = {};

function cout(message, level) {
  var l = App.Logging.Level.INFO;
  if (level === 0) {
    l = App.Logging.Level.DEBUG;
  } else if (level === 1) {
    l = App.Logging.Level.INFO;
  } else if (level === 2) {
    l = App.Logging.Level.WARNING;
  } else {
    l = App.Logging.Level.ERROR;
  }
  gbologger.log(l, message);
}

function arrayToBase64(u8Arr) {
  return utilities.arrayToBase64(u8Arr);
}

function base64ToArray(b64encoded) {
  return utilities.base64ToArray(b64encoded);
}

function loadSaveStateContext(context) {

  saveStateContext = context;
  saveState = {};

  var deferred = new jQuery.Deferred();
  window.app.store.propertiesForDomain(saveStateContext, function(properties) {

    for (var key in properties) {
      if (properties.hasOwnProperty(key)) {
        saveState[key] = properties[key];
      }
    }

    deferred.resolve();
  });
  return deferred.promise();
}

function setValue(key, value) {

  // JSON-encode the RTC as this cannot be stored in its default form.
  if (key.substring(0, 4) === "RTC_") {
    value = JSON.stringify(value);
  }

  var previous = saveState[key];
  if (previous !== value) {
    saveState[key] = value;
    window.app.setValue(saveStateContext, key, value);
  }

}

function deleteValue(key) {
  delete saveState[key];
  window.app.deleteValue(saveStateContext, key);
}

function findValue(key) {

  var value = saveState[key];

  // JSON-decode the RTC.
  if (value !== undefined && key.substring(0, 4) === "RTC_") {
    value = JSON.parse(value);
  }

  return value;
}

function startWrapper(identifier, canvas, ROM) {
  var deferred = jQuery.Deferred();
  loadSaveStateContext("game-" + identifier).then(function() {
    try {
      start(canvas, ROM, true);
      deferred.resolve();
    } catch (e) {
      deferred.reject(e);
    }
  });
  return deferred.promise();
}

(function($) {

  App.GameBoy = function(store, library) {
    this.init(store, library);
  };

  App.GameBoy.Settings = {
    ENABLE_SOUND:           0, // (defaults to true)
    ENABLE_GBC_BIOS:        1, // Boot with boot rom first (defaults to true)
    DISABLE_COLORS:         2, // Priority to game boy mode (defaults to false)
    VOLUME_LEVEL:           3, // Volume (defaults to 1)
    ENABLE_COLORIZATION:    4, // Colorize the game boy mode (defaults to true)
    TYPED_ARRAYS_DISALLOW:  5, // Disallow typed arrays (defaults to false)
    EMULATOR_LOOP_INTERVAL: 6, // Interval for the emulator loop (defaults to 4)
    AUDIO_BUFFER_MIN_SPAN:  7, // (defaults to 15)
    AUDIO_BUFFER_MAX_SPAN:  8, // (defaults to 30)
    ROM_ONLY_OVERRIDE:      9, // Override to allow for MBC1 instead of ROM only (defaults to false)
    MBC_ENABLE_OVERRIDE:    10, // Override MBC RAM disabling and always allow reading and writing to the banks (defaults to false)
    GB_BOOT_ROM_UTILIZED:   11, // Use the GameBoy boot ROM instead of the GameBoy Color boot ROM (defaults to false)
    SOFTWARE_RESIZING:      12, // Scale the canvas in JS, or let the browser scale the canvas (defaults to false)
    RESIZE_SMOOTHING:       13 // Use image smoothing based scaling (defaults to true)
  };

  App.GameBoy.State = {
    IDLE: 0,
    LOADING: 1,
    RUNNING: 2,
    ERROR: 3
  };

  jQuery.extend(App.GameBoy.prototype, {

    init: function(store, library) {
      var self = this;
      self.store = store;
      self.library = library;
      self.state = App.GameBoy.State.IDLE;
      self.stateChangeCallbacks = [];
      self.logging = new App.Logging(window.config.logging_level, "gameboy");
      self.speed = 1;

      settings[App.GameBoy.Settings.ENABLE_SOUND] = true;
      settings[App.GameBoy.Settings.SOFTWARE_RESIZING] = false;
      settings[App.GameBoy.Settings.ENABLE_COLORIZATION] = false;
      settings[App.GameBoy.Settings.RESIZE_SMOOTHING] = false;
      settings[App.GameBoy.Settings.EMULATOR_LOOP_INTERVAL] = 12;

    },

    setSoundEnabled: function(enabled) {
      var self = this;
      if (enabled === true) {
        settings[App.GameBoy.Settings.ENABLE_SOUND] = true;
        if (gameboy) {
          gameboy.initSound();
        }
      } else {
        settings[App.GameBoy.Settings.ENABLE_SOUND] = false;
        if (gameboy) {
          gameboy.stopSound();
        }
      }
    },

    setSpeed: function(speed) {
      var self = this;
      self.speed = speed;
      if (gameboy) {
        gameboy.setSpeed(speed);
      }
    },

    onStateChange: function(callback) {
      var self = this;
      self.stateChangeCallbacks.push(callback);
    },

    setState: function(state) {
      var self = this;
      if (self.state !== state) {
        self.state = state;

        // Fire the state change callbacks.
        for (var i = 0; i < self.stateChangeCallbacks.length; i++) {
          var callback = self.stateChangeCallbacks[i];
          callback(state);
        }
      }
    },

    pause: function() {
      var self = this;
      pause();
    },

    run: function() {
      var self = this;
      // Do not attempt to run unless we have been in the running state.
      if (self.state === App.GameBoy.State.RUNNING) {
        run();
      }
    },

    keyDown: function(keycode) {
      var self = this;
      GameBoyJoyPadEvent(keycode, true);
    },

    keyUp: function(keycode) {
      var self = this;
      GameBoyJoyPadEvent(keycode, false);
    },

    clear: function() {
      var self = this;
      clearLastEmulation();
      self.data = undefined;
      self.setState(App.GameBoy.State.IDLE);
    },

    reset: function() {
      var self = this;
      return self._insertCartridge(self.identifier, self.data);
    },

    load: function(identifier) {
      var self = this;
      var deferred = $.Deferred();

      var resetStateAndReject = function(e) {
        self.logging.warning("Unable to load game");
        self.setState(App.GameBoy.State.IDLE);
        deferred.reject(e);
      };

      self.library.fetch(identifier).then(function(data) {
        self._insertCartridge(identifier, data).then(function() {
          deferred.resolve();
        }).fail(resetStateAndReject);
      }).fail(resetStateAndReject);

      return deferred.promise();
    },

    _insertCartridge: function(identifier, data) {
      var self = this;
      var deferred = $.Deferred();
      self.identifier = identifier;
      self.data = data;
      startWrapper(identifier, document.getElementById('LCD'), data).then(function() {
        setTimeout(function() {
          if (gameboy) {
            gameboy.setSpeed(self.speed);
          }
          self.setState(App.GameBoy.State.RUNNING);
          deferred.resolve();
        }, 100);
      }).fail(function(e) {
        deferred.reject(e);
      });
      return deferred.promise();
    }

  });

})(jQuery);
<commit_msg>Re-ordering the settings to match their definition.<commit_after>/*
 * Copyright (C) 2012-2016 InSeven Limited.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

Gameboy = {};

Gameboy.Key = {
  START: 7,
  SELECT: 6,
  A: 4,
  B: 5,
  UP: 2,
  DOWN: 3,
  LEFT: 1,
  RIGHT: 0
};

var gbologger = new App.Logging(window.config.logging_level, "gbo");
var saveStateContext;
var saveState = {};

function cout(message, level) {
  var l = App.Logging.Level.INFO;
  if (level === 0) {
    l = App.Logging.Level.DEBUG;
  } else if (level === 1) {
    l = App.Logging.Level.INFO;
  } else if (level === 2) {
    l = App.Logging.Level.WARNING;
  } else {
    l = App.Logging.Level.ERROR;
  }
  gbologger.log(l, message);
}

function arrayToBase64(u8Arr) {
  return utilities.arrayToBase64(u8Arr);
}

function base64ToArray(b64encoded) {
  return utilities.base64ToArray(b64encoded);
}

function loadSaveStateContext(context) {

  saveStateContext = context;
  saveState = {};

  var deferred = new jQuery.Deferred();
  window.app.store.propertiesForDomain(saveStateContext, function(properties) {

    for (var key in properties) {
      if (properties.hasOwnProperty(key)) {
        saveState[key] = properties[key];
      }
    }

    deferred.resolve();
  });
  return deferred.promise();
}

function setValue(key, value) {

  // JSON-encode the RTC as this cannot be stored in its default form.
  if (key.substring(0, 4) === "RTC_") {
    value = JSON.stringify(value);
  }

  var previous = saveState[key];
  if (previous !== value) {
    saveState[key] = value;
    window.app.setValue(saveStateContext, key, value);
  }

}

function deleteValue(key) {
  delete saveState[key];
  window.app.deleteValue(saveStateContext, key);
}

function findValue(key) {

  var value = saveState[key];

  // JSON-decode the RTC.
  if (value !== undefined && key.substring(0, 4) === "RTC_") {
    value = JSON.parse(value);
  }

  return value;
}

function startWrapper(identifier, canvas, ROM) {
  var deferred = jQuery.Deferred();
  loadSaveStateContext("game-" + identifier).then(function() {
    try {
      start(canvas, ROM, true);
      deferred.resolve();
    } catch (e) {
      deferred.reject(e);
    }
  });
  return deferred.promise();
}

(function($) {

  App.GameBoy = function(store, library) {
    this.init(store, library);
  };

  App.GameBoy.Settings = {
    ENABLE_SOUND:           0, // (defaults to true)
    ENABLE_GBC_BIOS:        1, // Boot with boot rom first (defaults to true)
    DISABLE_COLORS:         2, // Priority to game boy mode (defaults to false)
    VOLUME_LEVEL:           3, // Volume (defaults to 1)
    ENABLE_COLORIZATION:    4, // Colorize the game boy mode (defaults to true)
    TYPED_ARRAYS_DISALLOW:  5, // Disallow typed arrays (defaults to false)
    EMULATOR_LOOP_INTERVAL: 6, // Interval for the emulator loop (defaults to 4)
    AUDIO_BUFFER_MIN_SPAN:  7, // (defaults to 15)
    AUDIO_BUFFER_MAX_SPAN:  8, // (defaults to 30)
    ROM_ONLY_OVERRIDE:      9, // Override to allow for MBC1 instead of ROM only (defaults to false)
    MBC_ENABLE_OVERRIDE:    10, // Override MBC RAM disabling and always allow reading and writing to the banks (defaults to false)
    GB_BOOT_ROM_UTILIZED:   11, // Use the GameBoy boot ROM instead of the GameBoy Color boot ROM (defaults to false)
    SOFTWARE_RESIZING:      12, // Scale the canvas in JS, or let the browser scale the canvas (defaults to false)
    RESIZE_SMOOTHING:       13 // Use image smoothing based scaling (defaults to true)
  };

  App.GameBoy.State = {
    IDLE: 0,
    LOADING: 1,
    RUNNING: 2,
    ERROR: 3
  };

  jQuery.extend(App.GameBoy.prototype, {

    init: function(store, library) {
      var self = this;
      self.store = store;
      self.library = library;
      self.state = App.GameBoy.State.IDLE;
      self.stateChangeCallbacks = [];
      self.logging = new App.Logging(window.config.logging_level, "gameboy");
      self.speed = 1;

      settings[App.GameBoy.Settings.ENABLE_SOUND] = true;
      settings[App.GameBoy.Settings.ENABLE_COLORIZATION] = false;
      settings[App.GameBoy.Settings.SOFTWARE_RESIZING] = false;
      settings[App.GameBoy.Settings.RESIZE_SMOOTHING] = false;
      settings[App.GameBoy.Settings.EMULATOR_LOOP_INTERVAL] = 12;

    },

    setSoundEnabled: function(enabled) {
      var self = this;
      if (enabled === true) {
        settings[App.GameBoy.Settings.ENABLE_SOUND] = true;
        if (gameboy) {
          gameboy.initSound();
        }
      } else {
        settings[App.GameBoy.Settings.ENABLE_SOUND] = false;
        if (gameboy) {
          gameboy.stopSound();
        }
      }
    },

    setSpeed: function(speed) {
      var self = this;
      self.speed = speed;
      if (gameboy) {
        gameboy.setSpeed(speed);
      }
    },

    onStateChange: function(callback) {
      var self = this;
      self.stateChangeCallbacks.push(callback);
    },

    setState: function(state) {
      var self = this;
      if (self.state !== state) {
        self.state = state;

        // Fire the state change callbacks.
        for (var i = 0; i < self.stateChangeCallbacks.length; i++) {
          var callback = self.stateChangeCallbacks[i];
          callback(state);
        }
      }
    },

    pause: function() {
      var self = this;
      pause();
    },

    run: function() {
      var self = this;
      // Do not attempt to run unless we have been in the running state.
      if (self.state === App.GameBoy.State.RUNNING) {
        run();
      }
    },

    keyDown: function(keycode) {
      var self = this;
      GameBoyJoyPadEvent(keycode, true);
    },

    keyUp: function(keycode) {
      var self = this;
      GameBoyJoyPadEvent(keycode, false);
    },

    clear: function() {
      var self = this;
      clearLastEmulation();
      self.data = undefined;
      self.setState(App.GameBoy.State.IDLE);
    },

    reset: function() {
      var self = this;
      return self._insertCartridge(self.identifier, self.data);
    },

    load: function(identifier) {
      var self = this;
      var deferred = $.Deferred();

      var resetStateAndReject = function(e) {
        self.logging.warning("Unable to load game");
        self.setState(App.GameBoy.State.IDLE);
        deferred.reject(e);
      };

      self.library.fetch(identifier).then(function(data) {
        self._insertCartridge(identifier, data).then(function() {
          deferred.resolve();
        }).fail(resetStateAndReject);
      }).fail(resetStateAndReject);

      return deferred.promise();
    },

    _insertCartridge: function(identifier, data) {
      var self = this;
      var deferred = $.Deferred();
      self.identifier = identifier;
      self.data = data;
      startWrapper(identifier, document.getElementById('LCD'), data).then(function() {
        setTimeout(function() {
          if (gameboy) {
            gameboy.setSpeed(self.speed);
          }
          self.setState(App.GameBoy.State.RUNNING);
          deferred.resolve();
        }, 100);
      }).fail(function(e) {
        deferred.reject(e);
      });
      return deferred.promise();
    }

  });

})(jQuery);
<|endoftext|>


<commit_before>/**
 * Created by zuk38 & przemoli on 9/11/15.
 */
(function () {
  angular.module('ui.grid').config(['$provide', function($provide) {
    $provide.decorator('i18nService', ['$delegate', function($delegate) {
      $delegate.add('pl', {
        headerCell: {
          aria: {
            defaultFilterLabel: 'Filter dla kolumny',
            removeFilter: 'Usuń filter',
            columnMenuButtonLabel: 'Menu kolumny'
          },
          priority: 'Prioritet:',
          filterLabel: "Filtr dla kolumny: "
        },
        aggregate: {
          label: 'pozycji'
        },
        groupPanel: {
          description: 'Przeciągnij nagłówek kolumny tutaj, aby pogrupować według niej.'
        },
        search: {
          placeholder: 'Szukaj...',
          showingItems: 'Widoczne pozycje:',
          selectedItems: 'Zaznaczone pozycje:',
          totalItems: 'Wszystkich pozycji:',
          size: 'Rozmiar strony:',
          first: 'Pierwsza strona',
          next: 'Następna strona',
          previous: 'Poprzednia strona',
          last: 'Ostatnia strona'
        },
        menu: {
          text: 'Wybierz kolumny:'
        },
        sort: {
          ascending: 'Sortuj rosnąco',
          descending: 'Sortuj malejąco',
          none: 'Brak sortowania',
          remove: 'Wyłącz sortowanie'
        },
        column: {
          hide: 'Ukryj kolumne'
        },
        aggregation: {
          count: 'Razem pozycji: ',
            sum: 'Razem: ',
            avg: 'Średnia: ',
            min: 'Min: ',
            max: 'Max: '
        },
        pinning: {
          pinLeft: 'Przypnij do lewej',
          pinRight: 'Przypnij do prawej',
          unpin: 'Odepnij'
        },
        columnMenu: {
          close: 'Zamknij'
        },
        gridMenu: {
          aria: {
            buttonLabel: 'Menu Grida'
          },
          columns: 'Kolumny:',
          importerTitle: 'Importuj plik',
          exporterAllAsCsv: 'Eksportuj wszystkie dane do csv',
          exporterVisibleAsCsv: 'Eksportuj widoczne dane do csv',
          exporterSelectedAsCsv: 'Eksportuj zaznaczone dane do csv',
          exporterAllAsPdf: 'Eksportuj wszystkie dane do pdf',
          exporterVisibleAsPdf: 'Eksportuj widoczne dane do pdf',
          exporterSelectedAsPdf: 'Eksportuj zaznaczone dane do pdf',
          clearAllFilters: 'Wyczyść filtry'
        },
        importer: {
          noHeaders: 'Nie udało się wczytać nazw kolumn. Czy plik posiada nagłówek?',
          noObjects: 'Nie udalo się wczytać pozycji. Czy plik zawiera dane??',
          invalidCsv: 'Nie udało się przetworzyć pliku, jest to prawidlowy plik CSV??',
          invalidJson: 'Nie udało się przetworzyć pliku, jest to prawidlowy plik Json?',
          jsonNotArray: 'Importowany plik json musi zawierać tablicę, importowanie przerwane.'
        },
        pagination: {
          aria: {
            pageToFirst: 'Pierwsza strona',
            pageBack: 'Poprzednia strona',
            pageSelected: 'Wybrana strona',
            pageForward: 'Następna strona',
            pageToLast: 'Ostatnia strona'
          },
          sizes: 'pozycji na stronę',
          totalItems: 'pozycji',
          through: 'do',
          of: 'z'
        },
        grouping: {
          group: 'Grupuj',
          ungroup: 'Rozgrupuj',
          aggregate_count: 'Zbiorczo: Razem',
          aggregate_sum: 'Zbiorczo: Suma',
          aggregate_max: 'Zbiorczo: Max',
          aggregate_min: 'Zbiorczo: Min',
          aggregate_avg: 'Zbiorczo: Średnia',
          aggregate_remove: 'Zbiorczo: Usuń'
        }
      });
      return $delegate;
    }]);
  }]);
})();
<commit_msg>Update pl.js<commit_after>/**
 * Created by zuk38 & przemoli on 9/11/15.
 */
(function () {
  angular.module('ui.grid').config(['$provide', function($provide) {
    $provide.decorator('i18nService', ['$delegate', function($delegate) {
      $delegate.add('pl', {
        headerCell: {
          aria: {
            defaultFilterLabel: 'Filter dla kolumny',
            removeFilter: 'Usuń filter',
            columnMenuButtonLabel: 'Menu kolumny'
          },
          priority: 'Prioritet:',
          filterLabel: "Filtr dla kolumny: "
        },
        aggregate: {
          label: 'pozycji'
        },
        groupPanel: {
          description: 'Przeciągnij nagłówek kolumny tutaj, aby pogrupować według niej.'
        },
        search: {
          placeholder: 'Szukaj...',
          showingItems: 'Widoczne pozycje:',
          selectedItems: 'Zaznaczone pozycje:',
          totalItems: 'Wszystkich pozycji:',
          size: 'Rozmiar strony:',
          first: 'Pierwsza strona',
          next: 'Następna strona',
          previous: 'Poprzednia strona',
          last: 'Ostatnia strona'
        },
        menu: {
          text: 'Wybierz kolumny:'
        },
        sort: {
          ascending: 'Sortuj rosnąco',
          descending: 'Sortuj malejąco',
          none: 'Brak sortowania',
          remove: 'Wyłącz sortowanie'
        },
        column: {
          hide: 'Ukryj kolumne'
        },
        aggregation: {
          count: 'Razem pozycji: ',
            sum: 'Razem: ',
            avg: 'Średnia: ',
            min: 'Min: ',
            max: 'Max: '
        },
        pinning: {
          pinLeft: 'Przypnij do lewej',
          pinRight: 'Przypnij do prawej',
          unpin: 'Odepnij'
        },
        columnMenu: {
          close: 'Zamknij'
        },
        gridMenu: {
          aria: {
            buttonLabel: 'Menu Grida'
          },
          columns: 'Kolumny:',
          importerTitle: 'Importuj plik',
          exporterAllAsCsv: 'Eksportuj wszystkie dane do csv',
          exporterVisibleAsCsv: 'Eksportuj widoczne dane do csv',
          exporterSelectedAsCsv: 'Eksportuj zaznaczone dane do csv',
          exporterAllAsPdf: 'Eksportuj wszystkie dane do pdf',
          exporterVisibleAsPdf: 'Eksportuj widoczne dane do pdf',
          exporterSelectedAsPdf: 'Eksportuj zaznaczone dane do pdf',
          clearAllFilters: 'Wyczyść filtry'
        },
        importer: {
          noHeaders: 'Nie udało się wczytać nazw kolumn. Czy plik posiada nagłówek?',
          noObjects: 'Nie udalo się wczytać pozycji. Czy plik zawiera dane??',
          invalidCsv: 'Nie udało się przetworzyć pliku, jest to prawidlowy plik CSV??',
          invalidJson: 'Nie udało się przetworzyć pliku, jest to prawidlowy plik Json?',
          jsonNotArray: 'Importowany plik json musi zawierać tablicę, importowanie przerwane.'
        },
        pagination: {
          aria: {
            pageToFirst: 'Pierwsza strona',
            pageBack: 'Poprzednia strona',
            pageSelected: 'Wybrana strona',
            pageForward: 'Następna strona',
            pageToLast: 'Ostatnia strona'
          },
          sizes: 'pozycji na stronę',
          totalItems: 'pozycji',
          through: 'do',
          of: 'z'
        },
        grouping: {
          group: 'Grupuj',
          ungroup: 'Rozgrupuj',
          aggregate_count: 'Zbiorczo: Razem',
          aggregate_sum: 'Zbiorczo: Suma',
          aggregate_max: 'Zbiorczo: Max',
          aggregate_min: 'Zbiorczo: Min',
          aggregate_avg: 'Zbiorczo: Średnia',
          aggregate_remove: 'Zbiorczo: Usuń'
        },
        validate: {
          error: 'Błąd:',
          minLength: 'Wartość powinna składać się z co najmniej THRESHOLD znaków.',
          maxLength: 'Wartość powinna składać się z przynajmniej THRESHOLD znaków.',
          required: 'Wartość jest wymagana.'
        }
      });
      return $delegate;
    }]);
  }]);
})();
<|endoftext|>


<commit_before>/**
 * lsys_js
 * @author AdamTavares / http://adamtavares.com
*/
var LSYS = LSYS || { REVISION: '1' }
LSYS.Sys = function( _iter, _angle, _start ) {

	this.iter = _iter;      	// {int} The total number of iterations.
	this.angle = _angle;     	// {float} The angle.
	this.start = _start;    	// {string} The starting state
	this.rules = {};    		// {object} The rules of the L-System.

	//------------------------------------------------------------
	//  Generated by the program
	//------------------------------------------------------------
	this.n = 0;         		// {int}  The current iteration.
	this.output = this.start;	// {string} The string which becomes a output with the help of a renderer

	//------------------------------------------------------------
	//  Everything after the 3rd parameter is a rule
	//------------------------------------------------------------
	for ( var i=3; i<arguments.length; i++ ) {
		var map = arguments[i].split('=');
		this.rules[map[0]] = map[1];
	}

	return {

		/**
		 *	Run the system the specified # of times i.e. this.iter
		 */
		go: function() {
			while( this.n < this.iter ) {
				this.next();
				this.n++;
			}
		},

		/**
		 *  Apply the next rule
		 */
		next: function() {
			//------------------------------------------------------------
			//  Apply next rule
			//------------------------------------------------------------
			var chars = this.output.split('');
			for ( var i in chars ) {
				if ( chars[i] in this.rules ) {
					chars[i] = this.rules[ chars[i] ];
				}
			}
			this.output = chars.join('');
		},

		draw: function( _renderer, _technique ) {
			//------------------------------------------------------------
			//  TODO: check to see if renderer is valid...
			//------------------------------------------------------------
			_renderer.draw( this.output, this.angle );
		},

		//------------------------------------------------------------
		//  Expose variables you'll need
		//------------------------------------------------------------
		n: this.n,
		rules: this.rules,
		iter: this.iter,
		output: this.output,
		angle: this.angle
	}
}

LSYS.Renderer = function( _canvasId ) {
	this.canvas = document.getElementById( _canvasId );
	this.constants = {
		'+': 'COUNTERCLOCK',
		'-': 'CLOCKWISE',
		'[': 'PUSH',
		']': 'POP',
		'C': 'COLOR'
	};
}

//------------------------------------------------------------
//  Some handy math functions
//------------------------------------------------------------
Math.toRad = function( _degrees ) {
	return _degrees*Math.PI / 180;
}

Math.toCart = function( _radius, _angle ) {
	return [ _radius*Math.cos( _angle ), _radius*Math.sin( _angle ) ];
}

//------------------------------------------------------------
//  2D renderer class
//------------------------------------------------------------
LSYS.TwoD = function( _canvasId ){
	LSYS.Renderer.call( this, _canvasId );
	this.ctx = this.canvas.getContext('2d');

	return {
		draw: function( _input, _angle ) {

			//------------------------------------------------------------
			//  Get the coordinates with unit distance
			//------------------------------------------------------------
			var angle = _angle;
			var coords = [];
			var x = 0;
			var y = 0;
			var bigX = 0;
			var bigY = 0;
			coords.push( [x,y] );

			//------------------------------------------------------------
			//  Loop through the LSys input string
			//------------------------------------------------------------
			var chars = _input.split('');
			for ( var i in chars ) {
				if ( chars[i] in this.constants ) {
					switch ( this.constants[ chars[i] ] ) {
						case 'COUNTERCLOCK':
							angle += _angle;
							break;
						case 'CLOCKWISE':
							angle -= _angle;
							break;
					}
				}
				else {
					var vector = Math.toCart( 1, Math.toRad( angle ) );
					x += vector[0];
					y += vector[1];
					coords.push( [x,y] );

					//------------------------------------------------------------
					//  Keep track of the biggest coordinates so we can
					//  determine the boundary box of the image
					//------------------------------------------------------------
					bigX = ( x > bigX ) ? x : bigX;
					bigY = ( y > bigY ) ? y : bigY;
				}
			}

			//------------------------------------------------------------
			//  Draw the thing to the canvas
			//------------------------------------------------------------
			var i = 1;
			while ( i < coords.length ) {
				this.ctx.beginPath();
				this.ctx.moveTo( coords[i-1][0], coords[i-1][1] );
                this.ctx.lineTo( coords[i][0], coords[i][1] );
				this.ctx.stroke();
                this.ctx.closePath();
                this.ctx.stroke();
				i++;
			}
		},
		constants: this.constants,
		ctx: this.ctx
	}
}
LSYS.TwoD.prototype = Object.create( LSYS.Renderer.prototype );

//------------------------------------------------------------
//  3D renderer class
//------------------------------------------------------------
LSYS.ThreeD = function(){
	return {
		draw: function( _input, _angle ) {}
	}
}
LSYS.ThreeD.prototype = Object.create( LSYS.Renderer.prototype );

//------------------------------------------------------------
// Stuff to investigate.
//------------------------------------------------------------
// semi-Thue grammar
// Chomsky hierarchy
// L-systems are now commonly known as parametric L systems.
// G = ( V, w, P )

/*
      process: function process(cmds, draw)
      {
         this._stack = [];

         var angle = this._angle;
         var distance = this._distance;
         var lastX;
         var lastY;

         if (draw)
         {
            var canvas = document.getElementById('canvas');
            var ctx = canvas.getContext('2d');

            // clear the background
            ctx.save();
            ctx.fillStyle = "rgb(255,255,255)";
            ctx.fillRect(0, 0, WIDTH, HEIGHT);

            // offset as required
            ctx.translate(this._xOffset, 0);

            // initial colour if specific colouring not used
            ctx.strokeStyle = "rgb(0,0,0)";
         }

         // start at grid 0,0 facing north with no colour index
         var pos = new LSystems.Location(0.0, 0.0, 90.0, -1);

         // process each command in turn
         var yOffset = this._yOffset, maxStackDepth = this._maxStackDepth;
         var colourList = this._colourList, stack = this._stack;
         var renderLineWidths = this._renderLineWidths;
         var rad, width, colour, lastColour = null;
         var c, len = cmds.length;
         for (var i=0; i<len; i++)
         {
            c = cmds.charAt(i);

            switch (c)
            {
               case COLOUR:
               {
                  // get colour index from next character
                  pos.colour = (cmds.charAt(++i) - '0');
                  break;
               }

               case ANTICLOCK:
               {
                  pos.heading += angle;
                  break;
               }

               case CLOCKWISE:
               {
                  pos.heading -= angle;
                  break;
               }

               case PUSH:
               {
                  stack.push(new LSystems.Location(pos.x, pos.y, pos.heading, pos.colour));
                  break;
               }

               case POP:
               {
                  pos = stack.pop();
                  break;
               }

               default:
               {
                  if (!this._constants[c])
                  {
                     lastX = pos.x;
                     lastY = pos.y;

                     // move the turtle
                     rad = pos.heading * RAD;
                     pos.x += distance * Math.cos(rad);
                     pos.y += distance * Math.sin(rad);

                     if (draw)
                     {
                        // render this element
                        if (renderLineWidths)
                        {
                           width = (maxStackDepth - stack.length);
                           ctx.lineWidth = width >= 1 ? width : 1;
                        }
                        colour = colourList[pos.colour];
                        if (colour && lastColour !== colour)
                        {
                           ctx.strokeStyle = colour;
                           lastColour = colour;
                        }
                        ctx.beginPath();
                        ctx.moveTo(lastX, HEIGHT - (lastY + yOffset));
                        ctx.lineTo(pos.x, HEIGHT - (pos.y + yOffset));
                        ctx.closePath();
                        ctx.stroke();
                     }
                     else
                     {
                        // remember min/max position
                        if (pos.x < this._minx) this._minx = pos.x;
                        else if (pos.x > this._maxx) this._maxx = pos.x;
                        if (pos.y < this._miny) this._miny = pos.y;
                        else if (pos.y > this._maxy) this._maxy = pos.y;
                        if (stack.length > this._maxStackDepth) this._maxStackDepth = stack.length;
                     }
                  }
                  break;
               }
            }
         }

         // finalise rendering
         if (draw)
         {
            ctx.restore();
         }
      }
   };
   */<commit_msg>updated lsys code with last nights work.<commit_after>/**
 * @author AdamTavares / http://adamtavares.com
*/
var LSYS = LSYS || { REVISION: '1' }
LSYS.Sys = function( _iter, _angle, _start ) {

	this.iter = _iter;			// {int} The total number of iterations.
	this.angle = _angle;		// {float} The angle.
	this.start = _start;		// {string} The starting state
	this.rules = {};			// {object} The rules of the L-System.

	//------------------------------------------------------------
	//	Generated by the program
	//------------------------------------------------------------
	this.n = 0;					// {int}  The current iteration.
	this.output = this.start;	// {string} The string which becomes a output with the help of a renderer

	//------------------------------------------------------------
	//	Everything after the 3rd parameter is a rule
	//------------------------------------------------------------
	for ( var i=3; i<arguments.length; i++ ) {
		var map = arguments[i].split('=');
		this.rules[map[0]] = map[1];
	}

	return {

		/**
		 *	Run the system the specified # of times i.e. this.iter
		 */
		run: function() {
			while( this.n < this.iter ) {
				this.next();
				this.n++;
			}
		},

		/**
		 *	Apply the next rule
		 */
		next: function() {
			//------------------------------------------------------------
			//	Apply next rule
			//------------------------------------------------------------
			var chars = this.output.split('');
			for ( var i in chars ) {
				if ( chars[i] in this.rules ) {
					chars[i] = this.rules[ chars[i] ];
				}
			}
			this.output = chars.join('');
		},

		draw: function( _renderer, _technique ) {
			//------------------------------------------------------------
			//	TODO: check to see if renderer is valid...
			//------------------------------------------------------------
			_renderer.draw( this.output, this.angle );
		},

		//------------------------------------------------------------
		//	Expose variables you'll need
		//------------------------------------------------------------
		n: this.n,
		rules: this.rules,
		iter: this.iter,
		output: this.output,
		angle: this.angle
	}
}

LSYS.Renderer = function( _canvasId ) {
	this.canvas = document.getElementById( _canvasId );
	this.constants = {
		'+': 'COUNTERCLOCK',
		'-': 'CLOCKWISE',
		'[': 'PUSH',
		']': 'POP',
		'C': 'COLOR'
	};
}

//------------------------------------------------------------
//	2D renderer class
//------------------------------------------------------------
LSYS.TwoD = function( _canvasId ){
	LSYS.Renderer.call( this, _canvasId );
	this.ctx = this.canvas.getContext('2d');

	return {

		draw: function( _input, _angle ) {

			//------------------------------------------------------------
			//	Get the coordinates with unit distance
			//------------------------------------------------------------
			var angle = _angle;
			var x = 0;
			var y = 0;
			var maxX = 0;
			var maxY = 0;
			var minX = 0;
			var minY = 0;
			coords = [];
			coords.push( [x,y] );

			//------------------------------------------------------------
			//	Loop through the LSys input string
			//------------------------------------------------------------
			var chars = _input.split('');
			for ( var i in chars ) {
				if ( chars[i] in this.constants ) {
					switch ( this.constants[ chars[i] ] ) {
						case 'COUNTERCLOCK':
							angle += _angle;
							break;
						case 'CLOCKWISE':
							angle -= _angle;
							break;
					}
				}
				else {
					var vector = Math.toCart( 1, Math.toRad( angle ) );
					x += vector[0];
					y += vector[1];
					coords.push( [x,y] );

					//------------------------------------------------------------
					//	Keep track of the biggest and smallest coordinates so
					//	we can determine the boundary box of the image
					//------------------------------------------------------------
					maxX = ( x > maxX ) ? x : maxX;
					maxY = ( y > maxY ) ? y : maxY;
					minX = ( x < minX ) ? x : minX;
					minY = ( y < minY ) ? y : minY;
				}
			}

			//------------------------------------------------------------
			//  Get the values you need to nudge the shape in place
			//------------------------------------------------------------
			var nudgeX = ( minX < 0 ) ? minX*-1 : 0;
			var nudgeY = ( minY < 0 ) ? minY*-1 : 0;

			//------------------------------------------------------------
			//  Scale the thing up to the size of the canvas
			//------------------------------------------------------------
			var rx = this.canvas.width / ( maxX + nudgeX );
			var ry = this.canvas.height / ( maxY + nudgeY );
			var scale = ( rx < ry ) ? rx : ry;

			//------------------------------------------------------------
			//	Draw the thing to the canvas
			//------------------------------------------------------------
			var i = 1;
			while ( i < coords.length ) {
				this.ctx.beginPath();
				this.ctx.moveTo( (coords[i-1][0]+nudgeX)*scale, (coords[i-1][1]+nudgeY)*scale );
				this.ctx.lineTo( (coords[i][0]+nudgeX)*scale, (coords[i][1]+nudgeY)*scale );
				this.ctx.stroke();
				this.ctx.closePath();
				this.ctx.stroke();
				i++;
			}
		},
		constants: this.constants,
		ctx: this.ctx,
		canvas: this.canvas
	}
}
LSYS.TwoD.prototype = Object.create( LSYS.Renderer.prototype );

//------------------------------------------------------------
//	3D renderer class
//------------------------------------------------------------
LSYS.ThreeD = function(){
	return {
		draw: function( _input, _angle ) {}
	}
}
LSYS.ThreeD.prototype = Object.create( LSYS.Renderer.prototype );

//------------------------------------------------------------
//  Library
//------------------------------------------------------------
LSYS.DragonCurve = function( _canvasId ) {
	var sys = LSYS.Sys( 12, 90, 'FX', 'X=X+YF+', 'Y=-FX-Y' );
	sys.run();
	var renderer = LSYS.TwoD( _canvasId );
	sys.draw( renderer );
}

LSYS.HexagonSierpinski = function( _canvasId ) {
	var sys = LSYS.Sys( 8, 60, 'A', 'A=B-A-B', 'B=A+B+A' );
	sys.run();
	var renderer = LSYS.TwoD( _canvasId );
	sys.draw( renderer );
}

//------------------------------------------------------------
//	Some handy math functions
//------------------------------------------------------------
Math.toRad = function( _degrees ) {
	return _degrees*Math.PI / 180;
}

Math.toCart = function( _radius, _angle ) {
	return [ _radius*Math.cos( _angle ), _radius*Math.sin( _angle ) ];
}

//------------------------------------------------------------
// Stuff to investigate.
//------------------------------------------------------------
// semi-Thue grammar
// Chomsky hierarchy
// L-systems are now commonly known as parametric L systems.
// G = ( V, w, P )

/*
	  process: function process(cmds, draw)
	  {
		 this._stack = [];

		 var angle = this._angle;
		 var distance = this._distance;
		 var lastX;
		 var lastY;

		 if (draw)
		 {
			var canvas = document.getElementById('canvas');
			var ctx = canvas.getContext('2d');

			// clear the background
			ctx.save();
			ctx.fillStyle = "rgb(255,255,255)";
			ctx.fillRect(0, 0, WIDTH, HEIGHT);

			// offset as required
			ctx.translate(this._xOffset, 0);

			// initial colour if specific colouring not used
			ctx.strokeStyle = "rgb(0,0,0)";
		 }

		 // start at grid 0,0 facing north with no colour index
		 var pos = new LSystems.Location(0.0, 0.0, 90.0, -1);

		 // process each command in turn
		 var yOffset = this._yOffset, maxStackDepth = this._maxStackDepth;
		 var colourList = this._colourList, stack = this._stack;
		 var renderLineWidths = this._renderLineWidths;
		 var rad, width, colour, lastColour = null;
		 var c, len = cmds.length;
		 for (var i=0; i<len; i++)
		 {
			c = cmds.charAt(i);

			switch (c)
			{
			   case COLOUR:
			   {
				  // get colour index from next character
				  pos.colour = (cmds.charAt(++i) - '0');
				  break;
			   }

			   case ANTICLOCK:
			   {
				  pos.heading += angle;
				  break;
			   }

			   case CLOCKWISE:
			   {
				  pos.heading -= angle;
				  break;
			   }

			   case PUSH:
			   {
				  stack.push(new LSystems.Location(pos.x, pos.y, pos.heading, pos.colour));
				  break;
			   }

			   case POP:
			   {
				  pos = stack.pop();
				  break;
			   }

			   default:
			   {
				  if (!this._constants[c])
				  {
					 lastX = pos.x;
					 lastY = pos.y;

					 // move the turtle
					 rad = pos.heading * RAD;
					 pos.x += distance * Math.cos(rad);
					 pos.y += distance * Math.sin(rad);

					 if (draw)
					 {
						// render this element
						if (renderLineWidths)
						{
						   width = (maxStackDepth - stack.length);
						   ctx.lineWidth = width >= 1 ? width : 1;
						}
						colour = colourList[pos.colour];
						if (colour && lastColour !== colour)
						{
						   ctx.strokeStyle = colour;
						   lastColour = colour;
						}
						ctx.beginPath();
						ctx.moveTo(lastX, HEIGHT - (lastY + yOffset));
						ctx.lineTo(pos.x, HEIGHT - (pos.y + yOffset));
						ctx.closePath();
						ctx.stroke();
					 }
					 else
					 {
						// remember min/max position
						if (pos.x < this._minx) this._minx = pos.x;
						else if (pos.x > this._maxx) this._maxx = pos.x;
						if (pos.y < this._miny) this._miny = pos.y;
						else if (pos.y > this._maxy) this._maxy = pos.y;
						if (stack.length > this._maxStackDepth) this._maxStackDepth = stack.length;
					 }
				  }
				  break;
			   }
			}
		 }

		 // finalise rendering
		 if (draw)
		 {
			ctx.restore();
		 }
	  }
   };
   */<|endoftext|>