├── .gitignore ├── assets ├── images │ ├── favicon.ico │ └── jermass.png ├── fonts │ ├── ubuntu-v15-latin-700.woff │ ├── ubuntu-v15-latin-700.woff2 │ ├── ubuntu-v15-latin-regular.woff │ └── ubuntu-v15-latin-regular.woff2 └── css │ └── styles.scss ├── _plugins ├── numbers_in_words │ ├── version.rb │ ├── parsing │ │ ├── parse_status.rb │ │ ├── fraction_parsing.rb │ │ ├── parse_fractions.rb │ │ ├── pair_parsing.rb │ │ ├── parse_individual_number.rb │ │ ├── special.rb │ │ ├── to_number.rb │ │ └── number_parser.rb │ ├── duck_punch.rb │ ├── powers_of_ten.rb │ ├── number_group.rb │ ├── to_word.rb │ ├── writer.rb │ ├── exceptional_numbers.rb │ └── fraction.rb ├── num2words-jekyll.rb ├── capitalize_all.rb ├── numbers_in_words.rb └── humanize.rb ├── _site └── _headers ├── _includes ├── index.js ├── bundle.js └── package-lock.json ├── README.md ├── Gemfile ├── _config.yml ├── Gemfile.lock ├── _data └── debt.csv ├── scripts.js ├── index.html └── LICENSE /.gitignore: -------------------------------------------------------------------------------- 1 | _site 2 | .sass-cache 3 | .jekyll-cache 4 | .jekyll-metadata 5 | vendor 6 | -------------------------------------------------------------------------------- /assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JermaSites/JermaDebt/HEAD/assets/images/favicon.ico -------------------------------------------------------------------------------- /assets/images/jermass.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JermaSites/JermaDebt/HEAD/assets/images/jermass.png -------------------------------------------------------------------------------- /_plugins/numbers_in_words/version.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module NumbersInWords 4 | VERSION = '1.0.1' 5 | end 6 | -------------------------------------------------------------------------------- /_site/_headers: -------------------------------------------------------------------------------- 1 | /* 2 | Permissions-Policy: browsing-topics=() 3 | 4 | https://:debt.pages.dev/* 5 | X-Robots-Tag: noindex 6 | -------------------------------------------------------------------------------- /assets/fonts/ubuntu-v15-latin-700.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JermaSites/JermaDebt/HEAD/assets/fonts/ubuntu-v15-latin-700.woff -------------------------------------------------------------------------------- /assets/fonts/ubuntu-v15-latin-700.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JermaSites/JermaDebt/HEAD/assets/fonts/ubuntu-v15-latin-700.woff2 -------------------------------------------------------------------------------- /assets/fonts/ubuntu-v15-latin-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JermaSites/JermaDebt/HEAD/assets/fonts/ubuntu-v15-latin-regular.woff -------------------------------------------------------------------------------- /assets/fonts/ubuntu-v15-latin-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JermaSites/JermaDebt/HEAD/assets/fonts/ubuntu-v15-latin-regular.woff2 -------------------------------------------------------------------------------- /_plugins/num2words-jekyll.rb: -------------------------------------------------------------------------------- 1 | require_relative 'numbers_in_words' 2 | 3 | module Jekyll 4 | 5 | module NumberWordsJekyll 6 | def num2words(value) 7 | return NumbersInWords.in_words(value) 8 | end 9 | end 10 | 11 | end 12 | 13 | Liquid::Template.register_filter(Jekyll::NumberWordsJekyll) 14 | -------------------------------------------------------------------------------- /_plugins/capitalize_all.rb: -------------------------------------------------------------------------------- 1 | require 'liquid' 2 | require 'uri' 3 | 4 | # Capitalize all words of the input 5 | module Jekyll 6 | module CapitalizeAll 7 | def capitalize_all(words) 8 | return words.split(' ').map(&:capitalize).join(' ').gsub(/(?<=-)\w/, &:upcase) 9 | end 10 | end 11 | end 12 | 13 | Liquid::Template.register_filter(Jekyll::CapitalizeAll) -------------------------------------------------------------------------------- /_includes/index.js: -------------------------------------------------------------------------------- 1 | import { Flip } from 'number-flip' 2 | var debt = parseInt((document.getElementById("debtCounterNum").innerHTML.slice(1)).replace(/,/g, '')); 3 | document.getElementById("debtCounterNum").innerHTML = "$"; 4 | new Flip({ 5 | node: document.getElementById("debtCounterNum"), 6 | from: 0, 7 | to: debt, 8 | separator: ',', 9 | duration: 2 // second 10 | }) -------------------------------------------------------------------------------- /_plugins/numbers_in_words/parsing/parse_status.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module NumbersInWords 4 | class ParseStatus 5 | attr_accessor :reset, :memory, :answer 6 | 7 | def initialize 8 | @reset = true 9 | @memory = 0 10 | @answer = 0 11 | end 12 | 13 | def calculate 14 | answer + memory 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /_plugins/numbers_in_words/duck_punch.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module NumbersInWords 4 | module NumericExtension 5 | def in_words(fraction: false) 6 | NumbersInWords::ToWord.new(self).in_words(fraction: fraction) 7 | end 8 | end 9 | 10 | module StringExtension 11 | def in_numbers(only_compress: false) 12 | NumbersInWords::ToNumber.new(self, only_compress).call 13 | end 14 | end 15 | end 16 | 17 | class String 18 | include NumbersInWords::StringExtension 19 | end 20 | 21 | class Numeric 22 | include NumbersInWords::NumericExtension 23 | end 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # JermaDebt 2 | 3 | Website to track Jerma985's Debt to chat 4 | 5 | Make a PR or File an Issue if you want something changed! :) 6 | 7 | ### To change the debt 8 | 9 | Submit a PR with a new entry in `_data/debt.csv` 10 | 11 | `DebtChange` is the amount to change it by (negative is down) 12 | 13 | **Please ensure this is a number and not a string (no quotation marks, commas or any non-numerical values)** 14 | 15 | `Date` is a short date corresponding to when the change happened in the format of `Jan. 1st, 1970` 16 | 17 | `Reason` is a very short summary to be used on graphs and the like. 18 | 19 | `ReasonMarkdown` is a longer summary with markdown support to add citation URLs. 20 | 21 | `Active` is set to `FALSE` when an item that was previously added to the site has since been overruled. It is only here for historical purposes, any other time it should be set to `TRUE`. 22 | -------------------------------------------------------------------------------- /_plugins/numbers_in_words/parsing/fraction_parsing.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module NumbersInWords 4 | module FractionParsing 5 | def fraction(text) 6 | return unless possible_fraction?(text) 7 | 8 | NumbersInWords.exceptional_numbers.lookup_fraction(text) 9 | end 10 | 11 | def strip_punctuation(text) 12 | text = text.downcase.gsub(/[^a-z 0-9]/, ' ') 13 | to_remove = true 14 | 15 | to_remove = text.gsub! ' ', ' ' while to_remove 16 | 17 | text 18 | end 19 | 20 | def possible_fraction?(text) 21 | words = text.split(' ') 22 | result = words & NumbersInWords.exceptional_numbers.fraction_names 23 | result.length.positive? 24 | end 25 | 26 | def text_including_punctuation 27 | to_s.strip 28 | end 29 | 30 | def text 31 | strip_punctuation text_including_punctuation 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /_plugins/numbers_in_words/parsing/parse_fractions.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module NumbersInWords 4 | class ParseFractions 5 | attr_reader :nums 6 | 7 | def initialize(nums) 8 | @nums = nums.map(&:to_f) 9 | end 10 | 11 | def call 12 | return if no_fractions? 13 | 14 | just_fraction || calculate 15 | end 16 | 17 | def calculate 18 | (parse(numbers) * parse(fractions)).rationalize(EPSILON).to_f 19 | end 20 | 21 | def parse(numbers) 22 | NumberParser.new.parse(numbers) 23 | end 24 | 25 | def numbers 26 | nums[0..index_of_fraction - 1] 27 | end 28 | 29 | def fractions 30 | nums[index_of_fraction..-1] 31 | end 32 | 33 | def just_fraction 34 | return nums.first if index_of_fraction.zero? 35 | end 36 | 37 | def index_of_fraction 38 | nums.index { |n| n < 1.0 } 39 | end 40 | 41 | def no_fractions? 42 | nums.all? { |n| n.zero? || n >= 1.0 } 43 | end 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | # Hello! This is where you manage which Jekyll version is used to run. 3 | # When you want to use a different version, change it below, save the 4 | # file and run `bundle install`. Run Jekyll with `bundle exec`, like so: 5 | # 6 | # bundle exec jekyll serve 7 | # 8 | # This will help ensure the proper Jekyll version is running. 9 | # Happy Jekylling! 10 | gem "jekyll" 11 | # This is the default theme for new Jekyll sites. You may change this to anything you like. 12 | gem "minima" 13 | # If you want to use GitHub Pages, remove the "gem "jekyll"" above and 14 | # uncomment the line below. To upgrade, run `bundle update github-pages`. 15 | # gem "github-pages", group: :jekyll_plugins 16 | # If you have any plugins, put them here! 17 | # group :jekyll_plugins do 18 | # gem "jekyll-feed", 19 | # end 20 | 21 | # Windows and JRuby does not include zoneinfo files, so bundle the tzinfo-data gem 22 | # and associated library. 23 | platforms :mingw, :x64_mingw, :mswin, :jruby do 24 | gem "tzinfo" 25 | gem "tzinfo-data" 26 | end 27 | 28 | # Performance-booster for watching directories on Windows 29 | gem "wdm", :platforms => [:mingw, :x64_mingw, :mswin] 30 | 31 | gem 'eventmachine' #needed for windows live reloading? -------------------------------------------------------------------------------- /_plugins/numbers_in_words/parsing/pair_parsing.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module NumbersInWords 4 | class PairParsing 5 | attr_accessor :ints 6 | attr_reader :only_compress 7 | 8 | def initialize(ints, only_compress) 9 | @ints = ints 10 | @only_compress = only_compress 11 | end 12 | 13 | # 15,16 14 | # 85,16 15 | def pair_parse 16 | ints = compressed 17 | return ints if only_compress 18 | 19 | return ints[0] if ints.length == 1 20 | 21 | sum = 0 22 | 23 | ints.each do |n| 24 | sum *= n >= 10 ? 100 : 10 25 | sum += n 26 | end 27 | 28 | sum 29 | end 30 | 31 | private 32 | 33 | # [40, 2] => [42] 34 | def compressed 35 | return [] if ints.empty? 36 | 37 | result = [] 38 | index = 0 39 | 40 | index, result = compress_numbers(result, index) 41 | 42 | result << ints[-1] if index < ints.length 43 | 44 | result 45 | end 46 | 47 | def compress_numbers(result, index) 48 | while index < ints.length - 1 49 | int, jump = compress_int(ints[index], ints[index + 1]) 50 | result << int 51 | index += jump 52 | end 53 | 54 | [index, result] 55 | end 56 | 57 | def compress_int(int, next_int) 58 | tens = (int % 10).zero? && int > 10 59 | return [int + next_int, 2] if tens && next_int < 10 60 | 61 | [int, 1] 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /_plugins/numbers_in_words/powers_of_ten.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module NumbersInWords 4 | GOOGOL = 10**100 5 | 6 | POWERS_OF_TEN = { 7 | 0 => 'one', 8 | 1 => 'ten', 9 | 2 => 'hundred', 10 | 1 * 3 => 'thousand', 11 | 2 * 3 => 'million', 12 | 3 * 3 => 'billion', 13 | 4 * 3 => 'trillion', 14 | 5 * 3 => 'quadrillion', 15 | 6 * 3 => 'quintillion', 16 | 7 * 3 => 'sextillion', 17 | 8 * 3 => 'septillion', 18 | 9 * 3 => 'octillion', 19 | 10 * 3 => 'nonillion', 20 | 11 * 3 => 'decillion', 21 | 12 * 3 => 'undecillion', 22 | 13 * 3 => 'duodecillion', 23 | 14 * 3 => 'tredecillion', 24 | 15 * 3 => 'quattuordecillion', 25 | 16 * 3 => 'quindecillion', 26 | 17 * 3 => 'sexdecillion', 27 | 18 * 3 => 'septendecillion', 28 | 19 * 3 => 'octodecillion', 29 | 20 * 3 => 'novemdecillion', 30 | 21 * 3 => 'vigintillion', 31 | 22 * 3 => 'unvigintillion', 32 | 23 * 3 => 'duovigintillion', 33 | 24 * 3 => 'trevigintillion', 34 | 25 * 3 => 'quattuorvigintillion', 35 | 26 * 3 => 'quinvigintillion', 36 | 27 * 3 => 'sexvigintillion', 37 | 28 * 3 => 'septenvigintillion', 38 | 29 * 3 => 'octovigintillion', 39 | 30 * 3 => 'novemvigintillion', 40 | 31 * 3 => 'trigintillion', 41 | 32 * 3 => 'untrigintillion', 42 | 33 * 3 => 'duotrigintillion', 43 | 100 => 'googol', 44 | 101 * 3 => 'centillion', 45 | GOOGOL => 'googolplex' 46 | }.freeze 47 | 48 | POWERS_RX = Regexp.union(POWERS_OF_TEN.values[1..-1]).freeze 49 | end 50 | -------------------------------------------------------------------------------- /_plugins/numbers_in_words/parsing/parse_individual_number.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module NumbersInWords 4 | class ParseIndividualNumber 5 | extend Forwardable 6 | def_delegators :parse_status, :reset=, :memory=, :answer=, :reset, :memory, :answer 7 | 8 | attr_reader :parse_status, :num 9 | 10 | def initialize(parse_status, num) 11 | @parse_status = parse_status 12 | @num = num 13 | end 14 | 15 | def call 16 | if reset 17 | clear 18 | else 19 | handle_power_of_ten 20 | 21 | update_memory 22 | end 23 | 24 | [reset, memory, answer] 25 | end 26 | 27 | private 28 | 29 | def clear 30 | self.reset = false 31 | self.memory += num 32 | end 33 | 34 | def handle_power_of_ten 35 | # x4. multiply memory by 10^9 because memory < power of ten 36 | return unless power_of_ten?(num) 37 | return unless power_of_ten(num) > 2 38 | 39 | self.memory *= num 40 | # 17. add memory to answer (and reset) (memory pow of ten > 2) 41 | self.answer += memory 42 | self.memory = 0 43 | self.reset = true 44 | end 45 | 46 | def update_memory 47 | self.memory = new_memory 48 | end 49 | 50 | def new_memory 51 | if memory < num 52 | memory * num 53 | else 54 | memory + num 55 | end 56 | end 57 | 58 | def power_of_ten(integer) 59 | Math.log10(integer) 60 | end 61 | 62 | def power_of_ten?(integer) 63 | return true if integer.zero? 64 | 65 | power_of_ten(integer) == power_of_ten(integer).to_i 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | # Welcome to Jekyll! 2 | # 3 | # This config file is meant for settings that affect your whole blog, values 4 | # which you are expected to set up once and rarely edit after that. If you find 5 | # yourself editing this file very often, consider using Jekyll's data files 6 | # feature for the data you need to update frequently. 7 | # 8 | # For technical reasons, this file is *NOT* reloaded automatically when you use 9 | # 'bundle exec jekyll serve'. If you change this file, please restart the server process. 10 | # 11 | # If you need help with YAML syntax, here are some quick references for you: 12 | # https://learn-the-web.algonquindesign.ca/topics/markdown-yaml-cheat-sheet/#yaml 13 | # https://learnxinyminutes.com/docs/yaml/ 14 | # 15 | # Site settings 16 | # These are used to personalize your new site. If you look in the HTML files, 17 | # you will see them accessed via {{ site.title }}, {{ site.email }}, and so on. 18 | # You can create any custom variable you would like, and they will be accessible 19 | # in the templates via {{ site.myvariable }}. 20 | 21 | # Exclude from processing. 22 | # The following items will not be processed, by default. 23 | # Any item listed under the `exclude:` key here will be automatically added to 24 | # the internal "default list". 25 | # 26 | # Excluded items can be processed by explicitly listing the directories or 27 | # their entries' file path in the `include:` list. 28 | # 29 | # exclude: 30 | # - .sass-cache/ 31 | # - .jekyll-cache/ 32 | # - gemfiles/ 33 | # - Gemfile 34 | # - Gemfile.lock 35 | # - node_modules/ 36 | # - vendor/bundle/ 37 | # - vendor/cache/ 38 | # - vendor/gems/ 39 | # - vendor/ruby/ 40 | 41 | include: 42 | - _data/ -------------------------------------------------------------------------------- /_plugins/numbers_in_words/parsing/special.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative './fraction_parsing' 4 | 5 | module NumbersInWords 6 | class Special 7 | extend Forwardable 8 | def_delegator :that, :to_s 9 | 10 | include FractionParsing 11 | 12 | attr_reader :that, :only_compress 13 | 14 | def initialize(that, only_compress) 15 | @that = that 16 | @only_compress = only_compress 17 | end 18 | 19 | def call 20 | float || 21 | negative || 22 | fraction(that) || 23 | mixed_words_and_digits || 24 | one 25 | end 26 | 27 | def float 28 | text_including_punctuation.to_f if text =~ /^-?\d+(.\d+)?$/ 29 | end 30 | 31 | def negative 32 | stripped = strip_minus text 33 | return unless stripped 34 | 35 | stripped_n = NumbersInWords.in_numbers(stripped, only_compress: only_compress) 36 | only_compress ? stripped_n.map { |k| k * -1 } : -1 * stripped_n 37 | end 38 | 39 | def mixed_words_and_digits 40 | return unless numeric?(that) 41 | 42 | in_words = that.split(' ').map { |word| numeric?(word) ? NumbersInWords.in_words(word) : word }.join(' ') 43 | ToNumber.new(in_words, only_compress).call 44 | end 45 | 46 | def numeric?(word) 47 | word.match(/\d+/) 48 | end 49 | 50 | def strip_minus(txt) 51 | txt.gsub(/^minus/, '') if txt =~ /^minus/ 52 | end 53 | 54 | def one 55 | one = check_one text 56 | 57 | return unless one 58 | 59 | res = NumbersInWords.in_numbers(one[1]) 60 | only_compress ? [res] : res 61 | end 62 | 63 | def check_one(txt) 64 | txt.match(/^one (#{POWERS_RX})$/) 65 | end 66 | end 67 | end 68 | -------------------------------------------------------------------------------- /_plugins/numbers_in_words.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'numbers_in_words/version' 4 | require_relative 'numbers_in_words/exceptional_numbers' 5 | require_relative 'numbers_in_words/parsing/number_parser' 6 | require_relative 'numbers_in_words/to_word' 7 | require_relative 'numbers_in_words/parsing/to_number' 8 | 9 | module NumbersInWords 10 | LENGTH_OF_GOOGOL = 101 # length of the string i.e. one with 100 zeros 11 | Error = ::Class.new(::StandardError) 12 | AmbiguousParsingError = ::Class.new(Error) 13 | DivideByZeroError = ::Class.new(Error) 14 | InvalidNumber = ::Class.new(Error) 15 | 16 | class << self 17 | extend Forwardable 18 | def_delegators :exceptional_numbers, :fraction 19 | 20 | def in_words(num, fraction: false) 21 | ToWord.new(num).in_words(fraction: fraction) 22 | end 23 | 24 | def in_numbers(words, only_compress: false) 25 | ToNumber.new(words, only_compress).call 26 | end 27 | 28 | def exceptional_numbers 29 | @exceptional_numbers ||= ExceptionalNumbers.new 30 | end 31 | 32 | def lookup(number) 33 | exceptional_numbers.lookup(number) 34 | end 35 | 36 | def exceptional_number(text) 37 | exceptional_numbers_to_i[text] 38 | end 39 | 40 | def power_of_ten(text) 41 | powers_of_ten_to_i[text] 42 | end 43 | 44 | private 45 | 46 | def exceptional_numbers_to_i 47 | @exceptional_numbers_to_i ||= swap_keys exceptional_numbers.to_h 48 | end 49 | 50 | def powers_of_ten_to_i 51 | @powers_of_ten_to_i ||= swap_keys POWERS_OF_TEN 52 | end 53 | 54 | def swap_keys(hash) 55 | hash.each_with_object({}) { |(k, v), h| h[v] = k } 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /_plugins/numbers_in_words/parsing/to_number.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative './special' 4 | require_relative './fraction_parsing' 5 | 6 | module NumbersInWords 7 | class ToNumber 8 | include FractionParsing 9 | extend Forwardable 10 | def_delegator :that, :to_s 11 | 12 | attr_reader :that, :only_compress 13 | 14 | def initialize(that, only_compress) 15 | @that = that 16 | @only_compress = only_compress 17 | end 18 | 19 | def call 20 | special || decimal || as_numbers 21 | end 22 | 23 | private 24 | 25 | def special 26 | Special.new(that, only_compress).call 27 | end 28 | 29 | def decimal 30 | match = check_decimal text 31 | return unless match 32 | 33 | integer = NumbersInWords.in_numbers(match.pre_match) 34 | decimal = NumbersInWords.in_numbers(match.post_match) 35 | integer + "0.#{decimal}".to_f 36 | end 37 | 38 | def as_numbers 39 | numbers = word_array_to_nums text.split(' ') 40 | 41 | NumbersInWords::NumberParser.new.parse numbers, only_compress: only_compress 42 | end 43 | 44 | def word_array_to_nums(words) 45 | words.map { |i| word_to_num(i) }.compact 46 | end 47 | 48 | # handles simple single word numbers 49 | # e.g. one, seven, twenty, eight, thousand etc 50 | def word_to_num(word) 51 | text = canonize(word.to_s.chomp.strip) 52 | 53 | NumbersInWords.exceptional_number(text) || fraction(text) || power(text) 54 | end 55 | 56 | def power(text) 57 | power = NumbersInWords.power_of_ten(text) 58 | 59 | 10**power if power 60 | end 61 | 62 | def canonize(word) 63 | aliases[word] || word 64 | end 65 | 66 | def aliases 67 | { 68 | 'a' => 'one', 69 | 'oh' => 'zero' 70 | } 71 | end 72 | 73 | def check_decimal(txt) 74 | txt.match(/\spoint\s/) 75 | end 76 | end 77 | end 78 | -------------------------------------------------------------------------------- /_plugins/numbers_in_words/number_group.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module NumbersInWords 4 | class NumberGroup 5 | include Enumerable 6 | attr_accessor :number 7 | 8 | def self.groups_of(number, size) 9 | new(number).groups(size) 10 | end 11 | 12 | def initialize(number) 13 | @number = number 14 | end 15 | 16 | # split into groups this gives us 1234567 => 123 456 7 17 | # so we need to reverse first 18 | # in stages 19 | def groups(size) 20 | # 1234567 => %w(765 432 1) 21 | @array = in_groups_of(@number.to_s.reverse.split(''), size) 22 | # %w(765 432 1) => %w(1 432 765) 23 | @array.reverse! 24 | 25 | # %w(1 432 765) => [1, 234, 567] 26 | @array.map! { |group| group.reverse.join('').to_i } 27 | @array.reverse! # put in ascending order of power of ten 28 | 29 | power = 0 30 | 31 | # [1, 234, 567] => {6 => 1, 3 => 234, 0 => 567} 32 | @array.each_with_object({}) do |digits, o| 33 | o[power] = digits 34 | power += size 35 | end 36 | end 37 | 38 | def split_decimals 39 | return unless @number.is_a? Float 40 | 41 | int, decimal = @number.to_s.split '.' 42 | 43 | [int.to_i, decimal.split(//).map(&:to_i)] 44 | end 45 | 46 | def split_googols 47 | googols = @number.to_s[0..-LENGTH_OF_GOOGOL].to_i 48 | remainder = @number.to_s[(1 - LENGTH_OF_GOOGOL)..-1].to_i 49 | [googols, remainder] 50 | end 51 | 52 | private 53 | 54 | def in_groups_of(array, number, fill_with = nil) 55 | # size % number gives how many extra we have; 56 | # subtracting from number gives how many to add; 57 | # modulo number ensures we don't add group of just fill. 58 | padding = (number - array.size % number) % number 59 | collection = array.dup.concat(Array.new(padding, fill_with)) 60 | 61 | collection.each_slice(number).to_a 62 | end 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /_plugins/numbers_in_words/to_word.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative 'writer' 4 | require_relative 'number_group' 5 | require_relative 'fraction' 6 | 7 | module NumbersInWords 8 | # Arbitrarily small number for rationalizing fractions 9 | EPSILON = 0.0000000001 10 | 11 | class ToWord 12 | attr_reader :that 13 | 14 | def initialize(that) 15 | @that = that 16 | end 17 | 18 | def to_i 19 | that.to_i 20 | end 21 | 22 | def negative 23 | return unless to_i.negative? 24 | 25 | 'minus ' + NumbersInWords.in_words(-@that) 26 | end 27 | 28 | def in_words(fraction: false) 29 | as_fraction(fraction) || 30 | handle_exceptional_numbers || 31 | decimals || 32 | negative || 33 | output 34 | end 35 | 36 | def as_fraction(fraction) 37 | return Fraction.in_words(that) if fraction 38 | end 39 | 40 | def decimals 41 | int, decimals = NumberGroup.new(@that).split_decimals 42 | return unless int 43 | 44 | out = NumbersInWords.in_words(int) + ' point ' 45 | decimals.each do |decimal| 46 | out << NumbersInWords.in_words(decimal.to_i) + ' ' 47 | end 48 | out.strip 49 | end 50 | 51 | def output 52 | output = if to_i.to_s.length == 2 # 20-99 53 | handle_tens(to_i) 54 | else 55 | Writer.new(that).call # longer numbers 56 | end 57 | 58 | output.strip 59 | end 60 | 61 | def handle_tens(number) 62 | output = '' 63 | 64 | tens = (number / 10).round * 10 # write the tens 65 | 66 | output += NumbersInWords.lookup(tens) # e.g. eighty 67 | 68 | digit = number - tens # write the digits 69 | 70 | unless digit.zero? 71 | join = number < 100 ? '-' : ' ' 72 | output << join + NumbersInWords.in_words(digit) 73 | end 74 | 75 | output 76 | end 77 | 78 | def handle_exceptional_numbers 79 | return unless @that.is_a?(Integer) 80 | 81 | NumbersInWords.exceptional_numbers.lookup(@that) 82 | end 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /_plugins/numbers_in_words/writer.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module NumbersInWords 4 | class Writer 5 | def initialize(that) 6 | @that = that 7 | end 8 | 9 | def call 10 | length = @that.to_s.length 11 | output = 12 | if length == 3 13 | # e.g. 113 splits into "one hundred" and "thirteen" 14 | write_groups(2) 15 | 16 | # more than one hundred less than one googol 17 | elsif length < LENGTH_OF_GOOGOL 18 | write_groups(3) 19 | 20 | elsif length >= LENGTH_OF_GOOGOL 21 | write_googols 22 | end 23 | output.strip 24 | end 25 | 26 | def group_words(size) 27 | # 1000 and over Numbers are split into groups of three 28 | groups = NumberGroup.groups_of @that, size 29 | powers = groups.keys.sort.reverse # put in descending order 30 | 31 | powers.each do |power| 32 | name = NumbersInWords::POWERS_OF_TEN[power] 33 | digits = groups[power] 34 | yield power, name, digits 35 | end 36 | end 37 | 38 | private 39 | 40 | def write_googols 41 | googols, remainder = NumberGroup.new(@that).split_googols 42 | output = '' 43 | 44 | output = output + ' ' + NumbersInWords.in_words(googols) + ' googol' 45 | if remainder.positive? 46 | prefix = ' ' 47 | prefix += 'and ' if remainder < 100 48 | output = output + prefix + NumbersInWords.in_words(remainder) 49 | end 50 | 51 | output 52 | end 53 | 54 | def write_groups(group) 55 | # e.g. 113 splits into "one hundred" and "thirteen" 56 | output = '' 57 | group_words(group) do |power, name, digits| 58 | output = output + ',' 59 | if digits.positive? 60 | prefix = ' ' 61 | # no and between thousands and hundreds 62 | prefix += 'and ' if power.zero? && (digits < 100) 63 | output = output + prefix + NumbersInWords.in_words(digits) 64 | output = output + prefix + name unless power.zero? 65 | end 66 | end 67 | output = output.gsub(/(?<=[a-zA-Z]), /, " ") 68 | output.gsub(" ,", ",") 69 | end 70 | end 71 | end 72 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.8.6) 5 | public_suffix (>= 2.0.2, < 6.0) 6 | colorator (1.1.0) 7 | concurrent-ruby (1.2.3) 8 | em-websocket (0.5.3) 9 | eventmachine (>= 0.12.9) 10 | http_parser.rb (~> 0) 11 | eventmachine (1.2.7) 12 | eventmachine (1.2.7-x64-mingw32) 13 | ffi (1.16.3) 14 | forwardable-extended (2.6.0) 15 | google-protobuf (3.25.2) 16 | http_parser.rb (0.8.0) 17 | i18n (1.14.1) 18 | concurrent-ruby (~> 1.0) 19 | jekyll (4.3.3) 20 | addressable (~> 2.4) 21 | colorator (~> 1.0) 22 | em-websocket (~> 0.5) 23 | i18n (~> 1.0) 24 | jekyll-sass-converter (>= 2.0, < 4.0) 25 | jekyll-watch (~> 2.0) 26 | kramdown (~> 2.3, >= 2.3.1) 27 | kramdown-parser-gfm (~> 1.0) 28 | liquid (~> 4.0) 29 | mercenary (>= 0.3.6, < 0.5) 30 | pathutil (~> 0.9) 31 | rouge (>= 3.0, < 5.0) 32 | safe_yaml (~> 1.0) 33 | terminal-table (>= 1.8, < 4.0) 34 | webrick (~> 1.7) 35 | jekyll-feed (0.17.0) 36 | jekyll (>= 3.7, < 5.0) 37 | jekyll-sass-converter (3.0.0) 38 | sass-embedded (~> 1.54) 39 | jekyll-seo-tag (2.8.0) 40 | jekyll (>= 3.8, < 5.0) 41 | jekyll-watch (2.2.1) 42 | listen (~> 3.0) 43 | kramdown (2.4.0) 44 | rexml 45 | kramdown-parser-gfm (1.1.0) 46 | kramdown (~> 2.0) 47 | liquid (4.0.4) 48 | listen (3.8.0) 49 | rb-fsevent (~> 0.10, >= 0.10.3) 50 | rb-inotify (~> 0.9, >= 0.9.10) 51 | mercenary (0.4.0) 52 | minima (2.5.1) 53 | jekyll (>= 3.5, < 5.0) 54 | jekyll-feed (~> 0.9) 55 | jekyll-seo-tag (~> 2.1) 56 | pathutil (0.16.2) 57 | forwardable-extended (~> 2.6) 58 | public_suffix (5.0.4) 59 | rb-fsevent (0.11.2) 60 | rb-inotify (0.10.1) 61 | ffi (~> 1.0) 62 | rexml (3.2.6) 63 | rouge (4.2.0) 64 | safe_yaml (1.0.5) 65 | sass-embedded (1.70.0-x64-mingw-ucrt) 66 | google-protobuf (~> 3.25) 67 | sass-embedded (1.70.0-x64-mingw32) 68 | google-protobuf (~> 3.25) 69 | sass-embedded (1.70.0-x86_64-linux-gnu) 70 | google-protobuf (~> 3.25) 71 | terminal-table (3.0.2) 72 | unicode-display_width (>= 1.1.1, < 3) 73 | tzinfo (2.0.6) 74 | concurrent-ruby (~> 1.0) 75 | tzinfo-data (1.2023.4) 76 | tzinfo (>= 1.0.0) 77 | unicode-display_width (2.5.0) 78 | wdm (0.1.1) 79 | webrick (1.8.1) 80 | 81 | PLATFORMS 82 | x64-mingw-ucrt 83 | x64-mingw32 84 | x86_64-linux 85 | 86 | DEPENDENCIES 87 | eventmachine 88 | jekyll 89 | minima 90 | tzinfo 91 | tzinfo-data 92 | wdm 93 | 94 | BUNDLED WITH 95 | 2.2.15 96 | -------------------------------------------------------------------------------- /_plugins/numbers_in_words/exceptional_numbers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'forwardable' 4 | 5 | require_relative 'fraction' 6 | require_relative 'powers_of_ten' 7 | 8 | module NumbersInWords 9 | class ExceptionalNumbers 10 | extend Forwardable 11 | 12 | DEFINITIONS = { 13 | 0 => { number: 'zero', ordinal: 'zeroth' }, 14 | 1 => { number: 'one', ordinal: 'first' }, 15 | 2 => { number: 'two', ordinal: 'second', fraction: { singular: 'half', plural: 'halves' } }, 16 | 3 => { number: 'three', ordinal: 'third' }, 17 | 4 => { number: 'four', ordinal: 'fourth', fraction: { singular: 'quarter', plural: 'quarters' } }, 18 | 5 => { number: 'five', ordinal: 'fifth' }, 19 | 6 => { number: 'six' }, 20 | 7 => { number: 'seven' }, 21 | 8 => { number: 'eight', ordinal: 'eighth' }, 22 | 9 => { number: 'nine', ordinal: 'ninth' }, 23 | 10 => { number: 'ten' }, 24 | 11 => { number: 'eleven' }, 25 | 12 => { number: 'twelve', ordinal: 'twelfth' }, 26 | 13 => { number: 'thirteen' }, 27 | 14 => { number: 'fourteen' }, 28 | 15 => { number: 'fifteen' }, 29 | 16 => { number: 'sixteen' }, 30 | 17 => { number: 'seventeen' }, 31 | 18 => { number: 'eighteen' }, 32 | 19 => { number: 'nineteen' }, 33 | 20 => { number: 'twenty', ordinal: 'twentieth' }, 34 | 30 => { number: 'thirty', ordinal: 'thirtieth' }, 35 | 40 => { number: 'forty', ordinal: 'fortieth' }, 36 | 50 => { number: 'fifty', ordinal: 'fiftieth' }, 37 | 60 => { number: 'sixty', ordinal: 'sixtieth' }, 38 | 70 => { number: 'seventy', ordinal: 'seventieth' }, 39 | 80 => { number: 'eighty', ordinal: 'eightieth' }, 40 | 90 => { number: 'ninety', ordinal: 'ninetieth' } 41 | }.freeze 42 | 43 | def fraction_names 44 | @fraction_names ||= determine_fraction_names 45 | end 46 | 47 | def lookup_fraction(words) 48 | fraction_lookup[words] 49 | end 50 | 51 | def fraction_lookup 52 | @fraction_lookup ||= generate_fraction_lookup 53 | end 54 | 55 | def lookup(number) 56 | to_h[number] 57 | end 58 | 59 | def fraction(denominator: nil, numerator: nil, word: nil) 60 | raise unless denominator || word 61 | 62 | numerator ||= 1 63 | 64 | denominator ||= NumbersInWords.in_numbers(word) 65 | 66 | Fraction.new(denominator: denominator, numerator: numerator, attributes: DEFINITIONS[denominator]) 67 | end 68 | 69 | def to_h 70 | @to_h ||= DEFINITIONS.transform_values do |h| 71 | h[:number] 72 | end 73 | end 74 | 75 | private 76 | 77 | def generate_fraction_lookup 78 | named_fractions.each_with_object({}) do |f, result| 79 | f.lookup_keys.each do |k| 80 | key = k.split(' ').last 81 | result[key] = 1.0 / f.denominator.to_f 82 | end 83 | end 84 | end 85 | 86 | def named_fractions 87 | @named_fractions ||= numbers.flat_map do |n| 88 | [ 89 | Fraction.new(denominator: n, numerator: 1), 90 | Fraction.new(denominator: n, numerator: 2) 91 | ] 92 | end 93 | end 94 | 95 | def numbers 96 | (2..100).to_a + powers_of_ten_skipping_googolplex.map { |p| 10**p } 97 | end 98 | 99 | def powers_of_ten_skipping_googolplex 100 | POWERS_OF_TEN.keys[0..-2] 101 | end 102 | 103 | def determine_fraction_names 104 | names = named_fractions.map(&:in_words) 105 | 106 | words = names.map(&:split).map(&:last) 107 | words += strip_punctuation(words) 108 | words.uniq 109 | end 110 | 111 | def strip_punctuation(words) 112 | words.map { |w| w.gsub(/^a-z/, ' ') } 113 | end 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /_plugins/numbers_in_words/fraction.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module NumbersInWords 4 | class Fraction 5 | attr_reader :denominator, :numerator, :attributes 6 | 7 | def self.in_words(that) 8 | r = that.rationalize(EPSILON) 9 | 10 | NumbersInWords 11 | .fraction(denominator: r.denominator, numerator: r.numerator) 12 | .in_words 13 | end 14 | 15 | def initialize(denominator:, numerator: 1, attributes: nil) 16 | @denominator = denominator 17 | @numerator = numerator 18 | @attributes = attributes || NumbersInWords::ExceptionalNumbers::DEFINITIONS[denominator] || {} 19 | end 20 | 21 | def lookup_keys 22 | key = in_words 23 | key2 = strip_punctuation(key.split(' ')).join(' ') 24 | 25 | key3 = "a #{key}" 26 | key4 = "an #{key}" 27 | key5 = "a #{key2}" 28 | key6 = "an #{key2}" 29 | [key, key2, key3, key4, key5, key6].uniq 30 | end 31 | 32 | def in_words 33 | NumbersInWords.in_words(numerator) + ' ' + fraction 34 | end 35 | 36 | def ordinal 37 | pluralize? ? pluralized_ordinal_in_words : singular_ordinal_in_words 38 | end 39 | 40 | def fraction 41 | if denominator == Float::INFINITY 42 | # We've reached the limits of ruby's number system 43 | # by the time we get to a googolplex (10 ** (10 ** 100)) 44 | return pluralize? ? 'infinitieths' : 'infinitieth' 45 | end 46 | 47 | pluralize? ? pluralized_fraction : singular_fraction 48 | end 49 | 50 | private 51 | 52 | def strip_punctuation(words) 53 | words.map { |w| w.gsub(/^a-z/, ' ') } 54 | end 55 | 56 | def pluralized_fraction 57 | fraction_plural || pluralized_ordinal_in_words 58 | end 59 | 60 | def singular_fraction 61 | fraction_singular || singular_ordinal_in_words 62 | end 63 | 64 | def pluralized_ordinal_in_words 65 | pluralized_ordinal || denominator_ordinal_in_words 66 | end 67 | 68 | def singular_ordinal_in_words 69 | singular_ordinal || denominator_ordinal_in_words 70 | end 71 | 72 | def singular_ordinal 73 | attributes[:ordinal] 74 | end 75 | 76 | def pluralized_ordinal 77 | singular_ordinal && singular_ordinal + 's' 78 | end 79 | 80 | def pluralize? 81 | numerator > 1 82 | end 83 | 84 | def denominator_ordinal_in_words 85 | if denominator > 100 86 | # one hundred and second 87 | with_remainder(100, ' and ') 88 | elsif denominator > 19 89 | # two thirty-fifths 90 | with_remainder(10, '-') 91 | else 92 | # one seventh 93 | singular = NumbersInWords.in_words(denominator) + 'th' 94 | pluralize? ? singular + 's' : singular 95 | end 96 | end 97 | 98 | def with_remainder(mod, join_word) 99 | rest = denominator % mod 100 | main = denominator - rest 101 | main = NumbersInWords.in_words(main) 102 | 103 | main = main.gsub(/^one /, '') if pluralize? 104 | 105 | rest_zero(rest, main) || joined(main, rest, join_word) 106 | end 107 | 108 | def joined(main, rest, join_word) 109 | main + 110 | join_word + 111 | self.class.new(numerator: numerator, denominator: rest).ordinal 112 | end 113 | 114 | def rest_zero(rest, main) 115 | return unless rest.zero? 116 | 117 | if pluralize? 118 | main + 'ths' 119 | else 120 | main + 'th' 121 | end 122 | end 123 | 124 | def exception 125 | attributes[:fraction] 126 | end 127 | 128 | def fraction_singular 129 | exception && exception[:singular] 130 | end 131 | 132 | def fraction_plural 133 | exception && exception[:plural] 134 | end 135 | end 136 | end 137 | -------------------------------------------------------------------------------- /_plugins/numbers_in_words/parsing/number_parser.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'forwardable' 4 | require_relative 'parse_fractions' 5 | require_relative 'parse_status' 6 | require_relative 'parse_individual_number' 7 | require_relative 'pair_parsing' 8 | 9 | module NumbersInWords 10 | class NumberParser 11 | # Example: 364,895,457,898 12 | # three hundred and sixty four billion eight hundred and ninety five million 13 | # four hundred and fifty seven thousand eight hundred and ninety eight 14 | # 15 | # 3 100 60 4 10^9, 8 100 90 5 10^6, 4 100 50 7 1000, 8 100 90 8 16 | # memory answer 17 | # x1. 3 add to memory because answer and memory are zero 3 0 18 | # x2. memory * 100 (because memory<100) 300 0 19 | # x3. 60 add to memory because memory > 60 360 0 20 | # x3. 4 add to memory because memory > 4 364 0 21 | # x4. multiply memory by 10^9 because memory < power of ten 364*10^9 0 22 | # x5. add memory to answer (and reset)memory > 8 (memory pow of ten > 2) 0 364*10^9 23 | # x6. 8 add to memory because not finished 8 '' 24 | # x7. multiply memory by 100 because memory < 100 800 '' 25 | # x8. add 90 to memory because memory > 90 890 '' 26 | # x9. add 5 to memory because memory > 5 895 '' 27 | # x10. multiply memory by 10^6 because memory < power of ten 895*10^6 '' 28 | # x11. add memory to answer (and reset) because memory power ten > 2 0 364895 * 10^6 29 | # x12. 4 add to memory because not finished 4 '' 30 | # x13. memory * 100 because memory < 100 400 '' 31 | # x14. memory + 50 because memory > 50 450 '' 32 | # x15. memory + 7 because memory > 7 457 '' 33 | # x16. memory * 1000 because memory < 1000 457000 '' 34 | # x17. add memory to answer (and reset)memory > 8 (memory pow of ten > 2) 0 364895457000 35 | # x18. 8 add to memory because not finished 8 '' 36 | # x19. memory * 100 because memory < 100 800 '' 37 | # x14. memory + 90 because memory > 90 890 '' 38 | # x15. memory + 8 because memory > 8 898 '' 39 | # 16. finished so add memory to answer 40 | 41 | # Example 42 | # 2001 43 | # two thousand and one 44 | # 2 1000 1 45 | # memory answer 46 | # 1. add 2 to memory because first 2 0 47 | # 2. multiply memory by 1000 because memory < 1000 2000 0 48 | # 3. add memory to answer,reset, because power of ten>2 0 2000 49 | # 4. add 1 to memory 1 2000 50 | # 5. finish - add memory to answer 0 2001 51 | 52 | SCALES_N = [10**2, 10**3, 10**6, 10**9, 10**12, 10**100].freeze 53 | 54 | def parse(nums, only_compress: false) 55 | fractions(nums) || 56 | small_numbers(nums, only_compress) || 57 | pair_parsing(nums, only_compress) || 58 | parse_each(nums) 59 | end 60 | 61 | private 62 | 63 | def fractions(nums) 64 | ParseFractions.new(nums).call 65 | end 66 | 67 | # 7 0.066666666666667 => 0.46666666666666 68 | 69 | # 15 => 15 70 | def small_numbers(nums, only_compress) 71 | return unless nums.length < 2 72 | return nums if only_compress 73 | 74 | nums.empty? ? 0 : nums[0] 75 | end 76 | 77 | # 15 75 => 1,575 78 | def pair_parsing(nums, only_compress) 79 | return if (SCALES_N & nums).any? 80 | 81 | pair_parse(nums, only_compress) 82 | end 83 | 84 | def parse_each(nums) 85 | status = ParseStatus.new 86 | 87 | nums.each do |num| 88 | ParseIndividualNumber.new(status, num).call 89 | end 90 | 91 | status.calculate 92 | end 93 | 94 | def pair_parse(nums, only_compress) 95 | PairParsing.new(nums, only_compress).pair_parse 96 | end 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /_data/debt.csv: -------------------------------------------------------------------------------- 1 | DebtChange,Date,Reason,ReasonMarkdown,Active 2 | 2500000000,"Nov. 8th, 2020",Missed Noita Shot.,Missed [Noita Shot.](https://www.twitch.tv/jerma985/clip/AdventurousMiniatureMageKevinTurtle),TRUE 3 | -375,"Nov. 8th, 2020",Community Subs,[Community Subs](https://youtu.be/YixWlAtvNhk?t=16440) (75),TRUE 4 | -272310,"Nov. 9th, 2020",Lego studs,"[Lego studs](https://youtu.be/USwGGyfczR8) (1 USD = 1 LS) 272,310 Lego Studs.",TRUE 5 | -100000000,"Nov. 21st, 2020",Showed Otto :),[Showed Otto](https://youtu.be/ZNqtvglRdcE?t=20014) :),TRUE 6 | -100,"Nov. 21st, 2020",Apology,"An [apology](https://youtu.be/MhycXVLPn7I?t=463) for calling Jerma a ""boring man"".",TRUE 7 | 250000000,"Nov. 25th, 2020",GAS bet,"Jerma bets 250 million that he [hasn't said ""GAS"" more than SAW's script.](https://youtu.be/BQaSlD_UFRU?t=7300)",TRUE 8 | 2649727215,"Nov. 25th, 2020",Deal or No Deal double,The worst [Deal or No Deal play of all time.](https://youtu.be/BQaSlD_UFRU?t=10675) Debt doubled.,TRUE 9 | -153000,"Nov. 27th, 2020",Pizza Sales,All the [Pizza sales.](https://youtu.be/QIuS5aLu1_M?t=19368),TRUE 10 | -30716,"Dec. 1st, 2020",Yakuza 0 ,"For [¥3,204,259 Yen in Yakuza 0](https://youtu.be/hy6DDkbKDFg?t=21185) (1 USD = 104.32 JPY, [exchange rate](https://www.exchange-rates.org/Rate/USD/JPY/12-1-2020)) on Dec. 1st 2020.",TRUE 11 | -250000000,"Dec. 3rd, 2020",Green eggs and Ham,For a flawless reading of [green eggs and ham.](https://youtu.be/a9uuyksatqE?t=9996),TRUE 12 | 1000000000,"Dec. 9th, 2020",Impossible Quiz,Jerma bets $1 billion that he will [finish The Impossible Quiz.](https://youtu.be/5RL0ZY2xpZs?t=6431) Added in error. Thanks for [auditing!](https://www.reddit.com/r/jerma985/comments/kfw0gd/audit_the_debt/) ;),FALSE 13 | 50000000000,"Dec. 14th, 2020",Bad self dare,[Bad self dare and bad hitbox.](https://clips.twitch.tv/SweetSecretiveLardPermaSmug) Reversed: Polls decided ([[1]](https://www.strawpoll.me/42283739/r) [[2]](https://www.strawpoll.me/42283673/r) [[3]](https://www.strawpoll.me/42283794/r)).,FALSE 14 | -1000000000,"Jan. 1st, 2021",Catboy Jerma,"Beating Catboy Jerma in the [2020 Jerma Rumble.](https://clips.twitch.tv/AmorphousHelpfulHyenaSeemsGood) There was a [double or nothing](https://www.strawpoll.me/42372421), Jerma now has to [dress up as Catboy Jerma](https://clips.twitch.tv/FantasticRenownedWaffleBuddhaBar) for two streams.",FALSE 15 | -500000000,"Jan. 4th, 2021",Simpsons impressions,Doing [Simpsons impressions.](https://youtu.be/_r4U50QYbxk?t=9730) [Good points are made.](https://www.reddit.com/r/jerma985/comments/kspvwa/discussion_of_the_contested_jerma_debt_removal/),TRUE 16 | -450000000,"Jan. 4th, 2021",JermaCraft CopyPasta,JermaCraft CopyPasta as the [Family Guy Characters.](https://youtu.be/_r4U50QYbxk?t=10052) (-$50m for bad impression).,TRUE 17 | -100000000,"Jan. 6th, 2021",Jerma tells his daily routine,Jerma tells his [daily routine.](https://youtu.be/s_PHGbYxQQo?t=16214),TRUE 18 | -250000000,"Jan. 24th, 2021",Bob Ross painting ,[Follow along](https://youtu.be/f6MeRTOEbFI?t=7693) with a Bob Ross painting video,TRUE 19 | 700000,"Jan. 26th, 2021",Be a good hitman,"50K for every minute he attempted the ""[Be a good hitman](https://youtu.be/azDfFoaFo-c?t=578)"" deal. (14 Minutes)",TRUE 20 | -1500000000,"Feb. 12th, 2021",20 minutes of only funny voices,Succeeded* at doing [20 minutes of only funny voices](https://youtu.be/xUhHk4h9ivc?t=8468). *kinda had a slip but had a compromise with chat,TRUE 21 | -500000000,"Apr. 18th, 2021",Seduced Princess Peach,"Made a deal to get with Lois, ended up scoring a victory royale with princess peach. Removal confirmed via [poll](https://strawpoll.com/ggowok79z)",TRUE 22 | 10000000000,"Apr. 24th, 2021",Cartoon Baseball,Jerma is mad Part ???: [Cartoon Baseball](https://youtu.be/VBiOK8dB9Og?t=21081) (Screamed this out in rage after losing two double or nothings and somehow pulled through),FALSE 23 | -27686449,"May. 8th, 2021",Insider Trading,What's a few crimes [in and out of the stock market](https://youtu.be/qg7JTQ0S5eU)?,TRUE 24 | -250000000,"Oct. 29th, 2021",MONKE,Went bananas for [monkey videos.](https://www.youtube.com/watch?v=RyBIsz5bfUw),TRUE 25 | -10000000,"Jan. 8th, 2022",Olive Man,[Edward Olivehands.](https://www.youtube.com/watch?v=pvjStBPiUH4),TRUE 26 | 1,"May. 2nd, 2022",Stanley Parable Achievement,[Bet $1](https://www.youtube.com/watch?v=Cbm3_HmU2NQ) that the narrator wouldn't let him get an achievement and then the narrator let him get the achievement.,FALSE 27 | 40000000,"May. 3rd, 2022",Infomercial Giveaway,Promised chat [$40 million](https://www.youtube.com/watch?v=AY6SdTHcLfk) via an infomercial giveaway which proceeded to never deliver.,TRUE 28 | -------------------------------------------------------------------------------- /scripts.js: -------------------------------------------------------------------------------- 1 | async function getChart(results) { 2 | let labels = [], allPaid = [], colors = []; 3 | let time = [], event = [], additiveDebt = [], eventReason = []; 4 | var count = 0; 5 | var ctxLine = document.getElementById('lineChart').getContext('2d'); 6 | 7 | var dynamicColors = function(test) { 8 | var r = 0; 9 | var g = 0 + test; 10 | var b = 245; 11 | return "rgb(" + r + "," + g + "," + b + ")"; 12 | }; 13 | 14 | var totalEvents = 0; 15 | //how much should we change each colour by? 16 | for(var item of results) { 17 | if(item.Active == "TRUE") { 18 | if(item.DebtChange < 0) { 19 | totalEvents++; 20 | } 21 | } 22 | } 23 | 24 | // Color of paid debts 25 | for(var item of results) { 26 | if(item.Active == "TRUE") { 27 | var paidDebt = Number(item.DebtChange); 28 | event.push(paidDebt); 29 | eventReason.push(item.Reason); 30 | event.reduce(function(a,b,i) { return additiveDebt[i] = a+b; }, 0); 31 | time.push(item.Date); 32 | if(item.DebtChange < 0) { 33 | allPaid.push(paidDebt); 34 | labels.push(item.Reason); 35 | colors.push(dynamicColors(count)); 36 | count += Math.round(((100 / totalEvents) * 2)); //change extremes between colors 37 | } 38 | } 39 | } 40 | 41 | allPaid.push(additiveDebt[additiveDebt.length - 1]); 42 | labels.push("Debt Left"); 43 | 44 | additiveDebt.push(additiveDebt[additiveDebt.length - 1]); 45 | event.push(0); 46 | eventReason.push("Current Day"); 47 | time.push(new Date()); 48 | 49 | new Chart(ctxLine, { 50 | type: 'line', 51 | data: { 52 | labels: time, 53 | datasets: [{ 54 | data: additiveDebt, 55 | pointBorderColor: '#c2394a', 56 | pointBackgroundColor: '#c2394a', 57 | borderColor: 'rgb(75, 192, 192)', 58 | backgroundColor: 'rgba(28,59,80,0.5)', 59 | fill: true, 60 | tension: 0 61 | }] 62 | }, 63 | 64 | options: { 65 | legend: { display: false }, 66 | responsive: true, 67 | onResize: function(chart, size) { 68 | chart.update(); 69 | chart.padding = { 70 | // top: 5 * (parseInt(getComputedStyle(document.getElementById("lineChart")).fontSize)), 71 | // bottom: 3 * (parseInt(getComputedStyle(document.getElementById("lineChart")).fontSize)), 72 | left: (parseInt(getComputedStyle(document.getElementById("div-wrapper")).paddingRight)), 73 | right: (parseInt(getComputedStyle(document.getElementById("div-wrapper")).paddingRight)), 74 | } 75 | }, 76 | layout: { 77 | padding: { 78 | // top: 5 * (parseInt(getComputedStyle(document.getElementById("lineChart")).fontSize)), 79 | // bottom: 3 * (parseInt(getComputedStyle(document.getElementById("lineChart")).fontSize)), 80 | left: (parseInt(getComputedStyle(document.getElementById("div-wrapper")).paddingRight)), 81 | right: (parseInt(getComputedStyle(document.getElementById("div-wrapper")).paddingRight)), 82 | } 83 | }, 84 | scales: { 85 | xAxes: [{ 86 | type: 'time', 87 | time: { 88 | parser: "MMM. Do, YYYY", 89 | }, 90 | ticks: { 91 | fontSize: 18, 92 | maxTicksLimit: 20 93 | }, 94 | gridLines: { 95 | color: "" 96 | } 97 | }], 98 | yAxes: [{ 99 | ticks: { 100 | fontSize: 18, 101 | callback: function(value, index, values) { 102 | return ('$' + value.toLocaleString()).replace("$-", "-$"); 103 | } 104 | }, 105 | gridLines: { 106 | color: "#FFFFFF" 107 | } 108 | }] 109 | }, 110 | tooltips: { 111 | enabled: true, 112 | mode: 'single', 113 | callbacks: { 114 | title: function(tooltipItems, data) { 115 | return data.labels[tooltipItems[0].index] 116 | // return new Intl.DateTimeFormat('en-US', { dateStyle: 'full', timeStyle: 'long' }).format(data.labels[tooltipItems[0].index]); 117 | }, 118 | label: function(tooltipItem) { 119 | return ('$' + additiveDebt[tooltipItem.index].toLocaleString()).replace("$-", "-$"); 120 | }, 121 | beforeFooter: function(tooltipItems, data) { 122 | return ("+$" + event[tooltipItems[0].index].toLocaleString()).replace("+$-", "-$"); 123 | }, 124 | footer: function(tooltipItems, data) { 125 | return eventReason[tooltipItems[0].index]; 126 | } 127 | } 128 | } 129 | }, 130 | }); 131 | } 132 | 133 | function randomInRange(min, max) { 134 | return Math.random() * (max - min) + min; 135 | } 136 | 137 | (async () => { 138 | Chart.defaults.global.defaultFontFamily = "Ubuntu"; 139 | Chart.defaults.global.defaultFontColor = "white"; 140 | Chart.defaults.global.fontSize = 18; 141 | d3.csv("_data/debt.csv").then(getChart); 142 | 143 | var debt = parseInt((document.getElementById("debtCounterNum").innerHTML.slice(1)).replace(/,/g, '')); 144 | 145 | if (debt == 0) { 146 | var duration = 15000; 147 | var animationEnd = Date.now() + duration; 148 | var colors = ["#a64ca6", "#4ca6a6"]; 149 | var defaults = {startVelocity: 30, spread: 360, ticks: 60, zIndex: 0, scalar: 1.5, colors: colors,}; 150 | 151 | (function frame() { 152 | 153 | var timeLeft = animationEnd - Date.now(); 154 | 155 | var particleCount = 4 * (timeLeft / duration); 156 | 157 | // since particles fall down, start a bit higher than random 158 | confetti(Object.assign({}, defaults, {particleCount, origin: {x: randomInRange(0.3, 0.5), y: Math.random() +.1 }})); 159 | confetti(Object.assign({}, defaults, {particleCount, origin: {x: randomInRange(0.5, 0.7), y: Math.random() +.1 }})); 160 | 161 | //side confetti 162 | 163 | confetti({ 164 | particleCount: 2, 165 | angle: 50, 166 | spread: 55, 167 | origin: {x: 0}, 168 | scalar: 1.5, 169 | ticks: 400, 170 | colors: colors, 171 | }); 172 | confetti({ 173 | particleCount: 2, 174 | angle: 130, 175 | spread: 55, 176 | origin: {x: 1}, 177 | scalar: 1.5, 178 | ticks: 400, 179 | colors: colors, 180 | }); 181 | 182 | if (Date.now() < animationEnd) { 183 | requestAnimationFrame(frame); 184 | } 185 | })(); 186 | } 187 | })(); 188 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | 5 | 6 | 7 | 8 | Jerma Debt Tracker 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 |

19 |
20 | 21 |
22 | Jerma Debt Tracker 23 |

24 |
25 |
26 | {% assign debtTotal = 0 %} 27 | {% for event in site.data.debt %} 28 | {%- if event.Active != "FALSE" -%} 29 | {% assign debtTotal = debtTotal | plus: event.DebtChange %} 30 | {%- endif -%} 31 | {% endfor %} 32 |

${{debtTotal | intcomma }}

33 |

34 | {% assign numwords = debtTotal | num2words %} 35 | {{ numwords | slice: 1, numwords.size | capitalize_all | replace: "And", "and"}} 36 |

37 |
38 |
39 |
40 | On the 8th of November 2020, at approximately 6pm, popular e-clown Jerma985 bet his chat, while playing the game Noita, that if he missed the next shot on the the next enemy, he would owe chat 500 million community subs.
41 | Jerma985 then proceeded miss the next shot he took. This brings his debt to chat to an original two and a half billion US dollars.
42 |
This website will track as Jerma slowly whittles away (or adds to) his debt to chat 43 |
This guy is fucked. 44 |
45 |
46 |
47 |

Debt Tracker

48 | 49 |
50 |

Date

51 |

Debt Change

52 |

Reason

53 |

Running Total

54 |
55 | 56 | {% assign debtTotal = 0 %} 57 | {% for event in site.data.debt %} 58 | 59 |
60 |

{{ event.Date }}

61 | {% assign classes = 'debtChange ' %} 62 | 63 | {%- if event.Active == "FALSE" -%} 64 | {% assign classes = classes | append: 'debtInvalid ' %} 65 | {%- else -%} 66 | {% assign debtTotal = debtTotal | plus: event.DebtChange %} 67 | {%- endif -%} 68 | 69 | {%- if event.DebtChange contains "-" -%} 70 | {% assign classes = classes | append: 'debtRemove' %} 71 | {%- else -%} 72 | {% assign classes = classes | append: 'debtAdd' %} 73 | {%- endif -%} 74 | 75 |

76 | {%- if event.DebtChange contains "-" -%} 77 | -${{ event.DebtChange | slice: 1,event.DebtChange.size | intcomma }} 78 | {%- else -%} 79 | +${{ event.DebtChange | intcomma }} 80 | {%- endif -%} 81 |

82 |

{{ event.ReasonMarkdown | markdownify | remove: '

' | remove: '

' }}

83 |

${{debtTotal | intcomma }}

84 |
85 | {% endfor %} 86 |
87 | 88 |
89 | 90 | 94 |
95 | 96 | 99 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /assets/css/styles.scss: -------------------------------------------------------------------------------- 1 | --- 2 | --- 3 | 4 | $purple-one: #190019; 5 | $purple-two: #110011; 6 | 7 | /* ubuntu-regular - latin */ 8 | @font-face { 9 | font-family: 'Ubuntu'; 10 | font-style: normal; 11 | font-weight: 400; 12 | src: 13 | url('../fonts/ubuntu-v15-latin-regular.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ 14 | url('../fonts/ubuntu-v15-latin-regular.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 15 | } 16 | 17 | /* ubuntu-700 - latin */ 18 | @font-face { 19 | font-family: 'Ubuntu'; 20 | font-style: normal; 21 | font-weight: 700; 22 | src: 23 | url('../fonts/ubuntu-v15-latin-700.woff2') format('woff2'), /* Chrome 26+, Opera 23+, Firefox 39+ */ 24 | url('../fonts/ubuntu-v15-latin-700.woff') format('woff'); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ 25 | } 26 | 27 | 28 | html, body { 29 | max-width: 100%; 30 | overflow-x: hidden; 31 | } 32 | 33 | body { 34 | margin: 0; 35 | } 36 | 37 | /* Header Bar and Text */ 38 | header { 39 | text-align: center; 40 | padding: 5px; 41 | font-weight: normal; 42 | background-color: #69bbc3; 43 | color: white; 44 | font-family: 'Ubuntu',sans-serif; 45 | height: 11vh; 46 | font-size: 3vh; 47 | display: flex; 48 | justify-content: center; 49 | align-items: center; 50 | } 51 | 52 | .data-display { 53 | height: 89vh; 54 | background-image:url("../images/jermass.png"); 55 | background-size: cover; 56 | background-attachment: fixed; 57 | background-position-x: center; 58 | background-position-y: center; 59 | display: flex; 60 | justify-content: center; 61 | align-items: center; 62 | flex-direction: column; 63 | color: #ffffff; 64 | font-family: 'Ubuntu',sans-serif; 65 | h1 { 66 | margin: 10px; 67 | font-size: 12.5vw; 68 | font-weight: 700; 69 | } 70 | /* Amount in Words */ 71 | h2 { 72 | text-align: center; 73 | line-height: 200%; 74 | margin: 5px; 75 | font-size: 150%; 76 | } 77 | } 78 | 79 | /* History of the debt text */ 80 | article { 81 | font-family: 'Ubuntu',sans-serif; 82 | font-size: 150%; 83 | line-height: 200%; 84 | text-align: center; 85 | background-color: #404040; 86 | color: white; 87 | .intro { 88 | padding-top: 50px; 89 | padding-bottom: 50px; 90 | padding-left: 10%; 91 | padding-right: 10%; 92 | text-align: center; 93 | color: #fff; 94 | p { 95 | font-size: 20px; 96 | } 97 | } 98 | /* Poll Zone */ 99 | .notice { 100 | font-family: ubuntu,sans-serif; 101 | font-size: 100%; 102 | padding: 15px; 103 | text-align: center; 104 | background-color: #2c2c2c; 105 | color: #fff; 106 | word-wrap: normal; 107 | h1 { 108 | text-decoration-style: dotted; 109 | a:link { 110 | color: #fff; 111 | background-color: transparent; 112 | text-decoration-style: dotted; 113 | } 114 | a:visited { 115 | color: #fff; 116 | } 117 | a:hover { 118 | color: #d3d3d3; 119 | background-color: transparent; 120 | text-decoration-style: solid; 121 | } 122 | } 123 | } 124 | } 125 | 126 | .head-link{ 127 | color: white; 128 | background-color: transparent; 129 | text-decoration: none; 130 | &:visited,&:active,&:hover,&:focus { 131 | color: white; 132 | background-color: transparent; 133 | text-decoration: none; 134 | } 135 | } 136 | 137 | .debt { 138 | font-size: 16px; 139 | background-color: $purple-one; 140 | a:link { 141 | color: #45c1ff; 142 | background-color: transparent; 143 | text-decoration: none; 144 | } 145 | a:visited { 146 | color: #82A3DD; 147 | background-color: transparent; 148 | text-decoration: none; 149 | } 150 | a:hover { 151 | color: #FFFFFF; 152 | background-color: transparent; 153 | text-decoration: underline; 154 | } 155 | a:active { 156 | color: #ff4545; 157 | background-color: transparent; 158 | text-decoration: underline; 159 | } 160 | h1 { 161 | padding-left: 8px; 162 | font-size: 30px; 163 | margin-top: 0px; 164 | padding-top: 30px; 165 | margin-bottom: 0px; 166 | padding-bottom: 20px; 167 | text-align: left; 168 | } 169 | } 170 | 171 | #div-wrapper { 172 | width: 80%; 173 | padding-left: 10%; 174 | padding-right: 10%; 175 | } 176 | 177 | .headerRow { 178 | width: 100%; 179 | float: left; 180 | border-top: thin solid #3D3D3D; 181 | background-color: #2f192f; 182 | } 183 | .runningTotal { 184 | margin: 0px; 185 | padding: 8px; 186 | width: 124px; 187 | float: left; 188 | text-align: right; 189 | font-weight: normal; 190 | } 191 | .row { 192 | width: 100%; 193 | float: left; 194 | border-bottom: thin solid #3D3D3D; 195 | } 196 | .row-alt { 197 | width: 100%; 198 | float: left; 199 | border-bottom: thin solid #3D3D3D; 200 | background-color: #2f192f; 201 | } 202 | .date { 203 | padding: 8px; 204 | margin: 0px; 205 | width: 110px; 206 | float: left; 207 | text-align: left; 208 | font-weight: normal; 209 | white-space: nowrap; 210 | } 211 | .debtChange { 212 | padding: 8px; 213 | margin: 0px; 214 | width: 159px; 215 | float: left; 216 | text-align: center; 217 | font-weight: normal; 218 | } 219 | 220 | .debtAdd { 221 | color: #c53929; 222 | } 223 | 224 | .debtRemove { 225 | color: #0b8043; 226 | } 227 | 228 | .debtInvalid { 229 | color: #f09300 !important; 230 | text-decoration: line-through !important; 231 | } 232 | 233 | .reason { 234 | width: calc(100% - 457px); 235 | float: left; 236 | padding: 8px; 237 | margin: 0px; 238 | text-align: left; 239 | font-weight: normal; 240 | } 241 | .web { 242 | width: 86%; 243 | padding-top: 30px; 244 | padding-bottom: 20px; 245 | padding-left: 7%; 246 | padding-right: 7%; 247 | } 248 | 249 | .mobile { 250 | display: none; 251 | } 252 | footer { 253 | padding-top: 15px; 254 | padding-bottom: 15px; 255 | text-decoration-style: dotted; 256 | a:link { 257 | color: #fff; 258 | background-color: transparent; 259 | text-decoration-style: dotted; 260 | } 261 | a:visited { 262 | color: #fff; 263 | } 264 | a:hover { 265 | color: #d3d3d3; 266 | background-color: transparent; 267 | text-decoration-style: solid; 268 | } 269 | 270 | span { 271 | display: inline-block; 272 | } 273 | } 274 | 275 | 276 | @media screen and (max-width: 1250px) { 277 | .web{ 278 | width: 100%; 279 | padding-left: 0%; 280 | padding-right: 0%; 281 | padding-top: 30px; 282 | padding-bottom: 20px; 283 | } 284 | #div-wrapper { 285 | width: 90%; 286 | padding-left: 5%; 287 | padding-right: 5%; 288 | line-height: 150%; 289 | } 290 | } 291 | 292 | @media screen and (max-width: 780px) { 293 | #div-wrapper { 294 | width: 90%; 295 | padding-left: 5%; 296 | padding-right: 5%; 297 | line-height: 125%; 298 | } 299 | .reason { 300 | width: calc(100% - 156px); 301 | } 302 | .date{ 303 | width: calc(50% - 16px); 304 | padding: 8px 8px 0px 8px; 305 | font-weight: bold; 306 | } 307 | .debtChange{ 308 | width: calc(50% - 16px); 309 | text-align: right; 310 | padding: 8px 8px 0px 8px; 311 | font-weight: bold; 312 | } 313 | .headerRow{ 314 | display: none; 315 | } 316 | .web{ 317 | display: none; 318 | } 319 | .mobile{ 320 | display: initial; 321 | width: 100%; 322 | padding-top: 30px; 323 | padding-bottom: 20px; 324 | } 325 | } 326 | 327 | /* Visualized Title */ 328 | 329 | .visualized { 330 | text-align: left; 331 | background-color: #550968; 332 | padding-bottom: 30px; 333 | padding-top: 20px; 334 | padding-left: 10%; 335 | padding-right: 10%; 336 | height: 75vh !important; 337 | .lh { 338 | text-align: left; 339 | text-align: center; 340 | font-size: 105%; 341 | width: 100vw; 342 | background-color: #530866; 343 | margin-left: -10.5vw; 344 | margin-top: -20px; 345 | margin-bottom: 5px; 346 | } 347 | } 348 | 349 | #lineChart { 350 | padding-top: 5rem; 351 | padding-bottom: 3rem; 352 | // padding-left: 10%; 353 | // padding-right: 10%; 354 | background-color: $purple-two; 355 | } 356 | -------------------------------------------------------------------------------- /_plugins/humanize.rb: -------------------------------------------------------------------------------- 1 | module Jekyll 2 | 3 | module Humanize 4 | ## 5 | # This is a port of the Django app `humanize` which adds a "human touch" 6 | # to data. Given that Jekyll produces static sites, some of the original 7 | # methods do not make logical sense (e.g. naturaltime). 8 | # 9 | # Source code can be viewed here: 10 | # https://github.com/django/django 11 | # 12 | # Copyright (c) Django Software Foundation and individual contributors. 13 | # All rights reserved. 14 | 15 | #################### 16 | # PUBLIC METHODS # 17 | #################### 18 | 19 | def ordinal(value, flag=nil) 20 | ## 21 | # Converts an integer to its ordinal as a string. 1 is '1st', 2 is '2nd', 22 | # 3 is '3rd', etc. Works for any integer. 23 | # 24 | # Usage: 25 | # {{ somenum }} >>> 3 26 | # {{ somenum | ordinal }} >>> '3rd' 27 | # {{ somenum | ordinal: "super" }} >>> '3rd' 28 | 29 | begin 30 | value = value.to_i 31 | flag.to_s.downcase! 32 | rescue Exception => e 33 | puts "#{e.class} #{e}" 34 | return value 35 | end 36 | 37 | suffix = "" 38 | suffixes = ["th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"] 39 | unless [11, 12, 13].include? value % 100 then 40 | suffix = suffixes[value % 10] 41 | else 42 | suffix = suffixes[0] 43 | end 44 | 45 | unless flag and flag == "super" 46 | return "#{value}%s" % suffix 47 | else 48 | return "#{value}%s" % suffix 49 | end 50 | 51 | end 52 | 53 | def intcomma(value, delimiter=",") 54 | ## 55 | # Converts an integer to a string containing commas every three digits. 56 | # For example, 3000 becomes '3,000' and 45000 becomes '45,000'. 57 | # Optionally supports a delimiter override for commas. 58 | # 59 | # Usage: 60 | # {{ post.content | number_of_words }} >>> 12345 61 | # {{ post.content | number_of_words | intcomma }} >>> '12,345' 62 | # {{ post.content | number_of_words | intcomma: '.' }} >>> '12.345' 63 | 64 | begin 65 | orig = value.to_s 66 | delimiter = delimiter.to_s 67 | rescue Exception => e 68 | puts "#{e.class} #{e}" 69 | return value 70 | end 71 | 72 | copy = orig.strip 73 | copy = orig.gsub(/^(-?\d+)(\d{3})/, "\\1#{delimiter}\\2") 74 | orig == copy ? copy : intcomma(copy, delimiter) 75 | end 76 | 77 | INTWORD_HELPERS = [ 78 | [6, "million"], 79 | [9, "billion"], 80 | [12, "trillion"], 81 | [15, "quadrillion"], 82 | [18, "quintillion"], 83 | [21, "sextillion"], 84 | [24, "septillion"], 85 | [27, "octillion"], 86 | [30, "nonillion"], 87 | [33, "decillion"], 88 | [100, "googol"], 89 | ] 90 | 91 | def intword(value) 92 | ## 93 | # Converts a large integer to a friendly text representation. Works best 94 | # for numbers over 1 million. For example, 1000000 becomes '1.0 million', 95 | # 1200000 becomes '1.2 million' and 1200000000 becomes '1.2 billion'. 96 | # 97 | # Usage: 98 | # {{ largenum }} >>> 1200000 99 | # {{ largenum | intword }} >>> '1.2 million' 100 | 101 | begin 102 | value = value.to_i 103 | rescue Exception => e 104 | puts "#{e.class} #{e}" 105 | return value 106 | end 107 | 108 | if value < 1000000 109 | return value 110 | end 111 | 112 | for exponent, text in INTWORD_HELPERS 113 | large_number = 10 ** exponent 114 | 115 | if value < large_number * 1000 116 | return "%#{value}.1f #{text}" % (value / large_number.to_f) 117 | end 118 | 119 | end 120 | 121 | return value 122 | end 123 | 124 | def apnumber(value) 125 | ## 126 | # For numbers 0-9, returns the number spelled out. Otherwise, returns the 127 | # number. This follows Associated Press style. 128 | # 129 | # Usage: 130 | # {{ num }} >>> 6 131 | # {{ num | apnumber }} >>> six 132 | 133 | begin 134 | value = value.to_i 135 | rescue Exception => e 136 | puts "#{e.class} #{e}" 137 | return value 138 | end 139 | 140 | unless value >= 0 and value < 10 then 141 | return value 142 | else 143 | return ["zero", "one", "two", "three", "four", "five", "six", 144 | "seven", "eight", "nine"][value] 145 | end 146 | 147 | end 148 | 149 | def naturalday(date) 150 | ## 151 | # For date values that are within a 9 day stretch from present day, this 152 | # will attempt to return the string representation in the format of today, 153 | # tomorrow, yesterday, "in # days" or "# days ago". Otherwise, returns a 154 | # string formatted according to the "date_format" setting in your 155 | # _config.yml file using strftime format (if not defined, it will default 156 | # to "%m/%d/%Y"). 157 | # 158 | # Usage: 159 | # TODAY == 01/26/2014 160 | # {{ post.updated }} >>> 01/25/2014 161 | # {{ post.updated | naturalday }} >>> 'yesterday' 162 | # {{ post.date }} >>> 01/19/2014 163 | # {{ post.date | naturalday }} >>> 'seven days ago' 164 | 165 | begin 166 | site = @context.registers[:site] 167 | date_format = site.config['humanize']['date_format'] 168 | date = time(date).to_date 169 | rescue Exception => e 170 | puts "#{e.class} #{e}" 171 | return date 172 | end 173 | 174 | unless date_format then 175 | date_format = "%m/%d/%Y" 176 | end 177 | 178 | today = time(Time.now).to_date 179 | delta = (date - today).to_i 180 | 181 | case delta 182 | when 0 183 | return "today" 184 | when 1 185 | return "tomorrow" 186 | when 2..9 187 | delta = apnumber(delta) 188 | return "in #{delta} days" 189 | when -1 190 | return "yesterday" 191 | when -9..-2 192 | delta = apnumber(delta * -1) 193 | return "#{delta} days ago" 194 | else 195 | return date.strftime("#{date_format}") 196 | end 197 | 198 | end 199 | 200 | def filesize(value) 201 | ## 202 | # For filesize values in bytes, returns the number rounded to 3 203 | # decimal places with the correct suffix. 204 | # 205 | # Usage: 206 | # {{ bytes }} >>> 123456789 207 | # {{ bytes | filesize }} >>> 117.738 MB 208 | filesize_tb = 1099511627776.0 209 | filesize_gb = 1073741824.0 210 | filesize_mb = 1048576.0 211 | filesize_kb = 1024.0 212 | 213 | begin 214 | value = value.to_f 215 | rescue Exception => e 216 | puts "#{e.class} #{e}" 217 | return value 218 | end 219 | 220 | if value >= filesize_tb 221 | return "%s TB" % (value / filesize_tb).to_f.round(3) 222 | elsif value >= filesize_gb 223 | return "%s GB" % (value / filesize_gb).to_f.round(3) 224 | elsif value >= filesize_mb 225 | return "%s MB" % (value / filesize_mb).to_f.round(3) 226 | elsif value >= filesize_kb 227 | return "%s KB" % (value / filesize_kb).to_f.round(0) 228 | elsif value == 1 229 | return "1 byte" 230 | else 231 | return "%s bytes" % value.to_f.round(0) 232 | end 233 | 234 | end 235 | 236 | ##################### 237 | # PRIVATE METHODS # 238 | ##################### 239 | 240 | private 241 | def time(input) 242 | case input 243 | when Time 244 | input 245 | when String 246 | Time.parse(input) 247 | else 248 | Jekyll.logger.error "Invalid Date:", "'#{input}' not valid datetime." 249 | exit(1) 250 | end 251 | end 252 | 253 | end 254 | 255 | end 256 | 257 | Liquid::Template.register_filter(Jekyll::Humanize) 258 | -------------------------------------------------------------------------------- /_includes/bundle.js: -------------------------------------------------------------------------------- 1 | (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;it.length)&&(e=t.length);for(var r=0,n=new Array(e);rn?r:n).toString().length)),this.setSelect(this.from),void 0!==u&&(l?setTimeout((function(){return i.flipTo({to:i.to})}),1e3*l):this.flipTo({to:this.to}))}var e,r,o;return e=t,(r=[{key:"_initHTML",value:function(t){var e,r=this;this.node.classList.add("number-flip"),this.node.style.position="relative",this.node.style.overflow="hidden";for(var o=0;o=0;r-=1){var n=this.afterArr[r]-this.beforeArr[r];e+=n,this._draw({digit:r,per:this.easeFn(t),alter:this.direct?n:e}),e*=10}}},{key:"flipTo",value:function(t){var e=this,r=t.to,n=t.duration,i=t.easeFn,o=t.direct;i&&(this.easeFn=i),void 0!==o&&(this.direct=o),this.setSelect(r);var a=this.ctnrArr.length;this.beforeArr=s(this.from,a),this.afterArr=s(r,a);var u=Date.now(),c=1e3*n||this.duration;requestAnimationFrame((function t(){var n=Date.now()-u;e.frame(n/c),n