<!DOCTYPE html>
<html>

<head>
  <link rel="stylesheet" href="http://netdna.bootstrapcdn.com/bootstrap/3.1.1/css/bootstrap.min.css">
  <link rel="stylesheet" href="style.css">
  <script src="http://code.jquery.com/jquery-2.0.3.min.js"></script>
  <script src="jquery.total-storage.min.js"></script>
  <script src="jquery.scrollTo.min.js"></script>
  <script src="script.js"></script>
</head>

<body>

  <div class="container">
    <table class="roller">
      <tr>
        <th>
          Roll
        </th>
        <th>
          Add to current results
        </th>
      </tr>
      <tr class="controls">
        <td class="main">
          <div class="js-roll-settings">
            <input type="number" class="js-rolls number" min="1" max="15" value="1" /> <span>k</span> 
            <input type="number" class="js-keeps number" min="1" max="15" value="1" />
            <br>            
            <span>reroll 1's (R1):</span> 
            <input type="checkbox" class="js-reroll-ones" />
            <br>
            <span>explode on 9 (E9):</span> 
            <input type="checkbox" class="js-explode-nine" />
          </div>
          <button class="js-roll roll btn btn-default">Roll</button>
          <button class="js-remember btn btn-default">Remember</button>
        </td>

        <td class="extra">
          <div class="js-extra js-extra-1" number=1>
            <input type="number" class="js-rolls number" min="0" max="15" value="1" />
            <span>k</span> 
            <input type="number" class="js-keeps number" min="0" max="15" value="0" />
            <button class="js-roll-extra btn btn-default">Add</button>
          </div>
          <div class="js-extra js-extra-2" number=2>
            <input type="number" class="js-rolls number" min="0" max="15" value="2" />
            <span>k</span> 
            <input type="number" class="js-keeps number" min="0" max="15" value="0" />
            <button class="js-roll-extra btn btn-default">Add</button>
          </div>
          <div class="js-extra js-extra-3" number=3>
            <input type="number" class="js-rolls number" min="0" max="15" value="3" />
            <span>k</span> 
            <input type="number" class="js-keeps number" min="0" max="15" value="0" />
            <button class="js-roll-extra btn btn-default">Add</button>
          </div>
        </td>
      </tr>
      <tr>
        <td>
          <div>
            <span>Sum:</span>  <span class="js-sum">0</span>
          </div>
      
          <div>
            <span>Sum (explodes not kept):</span>  <span class="js-explodeless-sum">0</span>
          </div>
          
          <div>
            <span>Number of ones:</span>  <span class="js-ones">0</span>
          </div>
          
          <div>
            <span>All rolls:</span>  <span class="js-all"></span>
          </div>
      
        </td>
        <td>
          <div class="js-extra-1">Added: <span class="js-extra-values"></span></div>
          <div class="js-extra-2">Added: <span class="js-extra-values"></span></div>
          <div class="js-extra-3">Added: <span class="js-extra-values"></span></div>
        </td>
      </tr>
    </table>
    
    <a href="#" class="js-show-extra">Show legend...</a>
    <a href="#" class="js-hide-extra" style="display: none">Hide legend...</a>
    
    <div class="js-extra-info" style="display: none">
      <div>
        <span>Legend:</span>
        <span class="roll-result explode">Kept Exploded</span>
        <span class="roll-result kept">Kept</span>
        <span class="roll-result kept-explodeless">Kept when explodes are ignored</span>
        <span class="roll-result not-kept">not kept</span>,
        <span class="roll-result explode not-kept">not kept</span>
      </div>
    </div>

    <h3>Presets:</h3>
    <div class="js-presets">
      <div class="js-template js-preset" style="display:none;">
        <input type="text" placeholder="label" class="js-roll-label" />
        <span class="js-roll-settings">
          <input class="js-rolls number" /> k
          <input class="js-keeps number" />
          <span>R1:</span>
          <input type="checkbox" class="js-reroll-ones" />,
          <span>E9:</span>
          <input type="checkbox" class="js-explode-nine" />
        </span>

        <button class="js-roll btn btn-default">Roll</button>
        <button class="js-forget btn btn-default">Forget</button>
      </div>
    </div>
  </div>
</body>

</html>
// Code goes here

function d10(rerollOnes, explodeNines) {
  var result = Math.floor((Math.random() * 10) + 1);
  if (result == 10 || (result == 9 && explodeNines)) {
    result += d10(rerollOnes, explodeNines);
  }
  if (result == 1 && rerollOnes) {
    result = d10(rerollOnes, explodeNines);
  }
  return result;
}


function plainRoll(rolls, keeps, rerollOnes, explodeNine) {
  var allRolls = [];
  for (var i = 0; i < rolls; i++) {
    allRolls.push(d10(rerollOnes, explodeNine));
  }
  return allRolls;
}

function getResult(rolls, keeps, rerollOnes, explodeNine){
  var sum = 0;
  var explodelessSum = 0;
  var ones = 0;
  var explodes = 0;

  window.originalRolls = window.originalRolls.sort(function(a, b) {
    return b - a;
  });
  
  $('.js-all').empty();
  
  for (var i = 0; i < window.originalRolls.length; i++) {
    var $valueElement = $('<span>', {class: 'roll-result'});
    var $allRollsElement = $('.js-all');
    var value = window.originalRolls[i];
    var rInfo = {value: value};
    
    $valueElement.text(value);
    if (value>9) {
      rInfo.explode = true;
      explodes += 1;
      $valueElement.addClass('explode');
    }
    if (i < keeps) {
      rInfo.kept = true;
      sum += value;
      $valueElement.addClass('kept');
    }
    if ((i - explodes < keeps) && !rInfo.explode){
      rInfo.keptForExplodeless = true;
      explodelessSum += value;
      if (!rInfo.kept) {
        $valueElement.addClass('kept-explodeless');
      }
    }
    if (value == 1){
      ones += 1;
    }
    if (!rInfo.kept && !rInfo.keptForExplodeless) {
      $valueElement.addClass('not-kept');
    }
    $allRollsElement.append($valueElement);
    
  }
  $('.js-sum').text(sum);
  $('.js-explodeless-sum').text(explodelessSum);
  $('.js-ones').text(ones);

  $('.controls .main input.js-rolls').val(rolls);
  $('.controls .main input.js-keeps').val(keeps);
  $('.controls .main input.js-reroll-ones').prop('checked', rerollOnes);
  $('.controls .main input.js-explode-nine').prop('checked', explodeNine);
  
  $('body').scrollTo($('.main .js-roll-settings'));
}

function addPreset(rollsNumber, keepsNumber, rerollOnes, explodeNine, label) {
  var $preset = $('.js-template').clone();
  $preset.removeClass('js-template');
  $('.js-presets').append($preset);
  $preset.find('.js-rolls').val(rollsNumber);
  $preset.find('.js-keeps').val(keepsNumber);
  $preset.find('.js-reroll-ones').prop('checked', rerollOnes);
  $preset.find('.js-explode-nine').prop('checked', explodeNine);
  $preset.find('.js-roll-label').val(label);
  $preset.show();
  rememberPresets();
}

function rememberPresets() {
  results = [];
  $.each($('.js-presets .js-preset:not(.js-template)'), function(index, value){
    var $e = $(value);
    var item = {};
    item.label = $e.find('.js-roll-label').val();
    item.rolls = $e.find('.js-rolls').val();
    item.keeps = $e.find('.js-keeps').val();
    item.rerollOnes = $e.find('.js-reroll-ones').is(":checked");
    item.explodeNine = $e.find('.js-explode-nine').is(":checked");
    results.push(item);
  });
  $.totalStorage(window.storageName, results);
  return results;
}

function restorePresets() {
  var diceMemory = $.totalStorage(window.storageName);
  if (diceMemory) {
    $.each(diceMemory, function(index, item){
      addPreset(item.rolls, item.keeps, item.rerollOnes, item.explodeNine, item.label);
    });
  }
}

$(document).ready(function() {
  window.originalRolls = [];
  window.storageName = 'dtd-dice-memory-v6';
  $('.container').on('click', '.js-roll', function() {
    var $settings = $(this).siblings('.js-roll-settings');
    var rollsNumber = $settings.find('input.js-rolls').val();
    var keepsNumber = $settings.find('input.js-keeps').val();
    var rerollOnes = $settings.find('input.js-reroll-ones').is(":checked");
    var explodeNine = $settings.find('input.js-explode-nine').is(":checked");
    var results = plainRoll(
      rollsNumber,
      keepsNumber,
      rerollOnes,
      explodeNine
    );
    window.originalRolls = results;
    getResult(
      rollsNumber,
      keepsNumber,
      rerollOnes,
      explodeNine
    );
    $('.js-extra-values').text('');
  });
  
  $('.container').on('click', '.js-roll-extra', function() {
    var $e = $(this);
    var extraRollsNumber = parseInt($e.siblings('input.js-rolls').val());
    var extraKeepsNumber = parseInt($e.siblings('input.js-keeps').val());
    var extraNumber = $e.parent().attr('number');
    $settings = $('.controls .main .js-roll-settings');
    var rollsNumber = parseInt($settings.find('input.js-rolls').val());
    var keepsNumber = parseInt($settings.find('input.js-keeps').val());
    var rerollOnes = $settings.find('input.js-reroll-ones').is(":checked");
    var explodeNine = $settings.find('input.js-explode-nine').is(":checked");
    
    rollsNumber += extraRollsNumber;
    keepsNumber += extraKeepsNumber;
    
    var results = plainRoll(
      extraRollsNumber,
      extraKeepsNumber,
      rerollOnes,
      explodeNine
    );
    console.log('.js-extra-'+extraNumber+' .js-extra-values');
    console.log($e.parent());
    $('.js-extra-'+extraNumber+' .js-extra-values').text(results);
    $.merge(window.originalRolls, results);
    getResult(
      rollsNumber,
      keepsNumber,
      rerollOnes,
      explodeNine
    );
  });
  
  $('.container').on('click', '.js-remember', function() {
    var $settings = $(this).siblings('.js-roll-settings');
    var rollsNumber = $settings.find('input.js-rolls').val();
    var keepsNumber = $settings.find('input.js-keeps').val();
    var rerollOnes = $settings.find('input.js-reroll-ones').is(":checked");
    var explodeNine = $settings.find('input.js-explode-nine').is(":checked");
    addPreset(rollsNumber, keepsNumber, rerollOnes, explodeNine);
  });
  
  $('.container').on('click', '.js-forget', function() {
    $(this).parent().remove();
    rememberPresets();
  });
  
  $('.container .js-presets').on('change', 'input', function() {
    rememberPresets();
  });
  
  $('.js-show-extra, .js-hide-extra').click(function() {
    $('.js-show-extra').fadeToggle();
    $('.js-hide-extra').fadeToggle();
    $('.js-extra-info').slideToggle();
  });
  
  restorePresets();
  $('.main .js-roll').click();
});
/* Styles go here */

body {
  font-size: 1.5em
}
input {
  padding: 0 0 0 0.2em;
  text-align: center;
}

.container {
  margin-top: 2em;
}
.roller td {
  padding: 0 7px;
  vertical-align: top;
}
th {
  text-align: center;
}
.js-extra {
  padding: 5px 0;
}
.js-extra-1,
.js-extra-1 input,
.js-extra-1 button {
  background: #ffe5ac;
}
.js-extra-2,
.js-extra-2 input,
.js-extra-2 button {
  background: #acffe1;
}
.js-extra-3,
.js-extra-3 input,
.js-extra-3 button {
  background: #f2ffbd;
}
.controls td {
  text-align: center;
}

input.number {
  width: 3em;
}
.controls .extra {
  min-width: 10em;
}
.controls .main {
  width: 15em;
}

table.roller{
  max-width: 35em;
}

span.roll-result+span.roll-result:before
{
  content: ', ';
}

.roll-result.explode {
  color: red;
}
.roll-result.kept:not(.explode) {
  color: green;
}
.roll-result.kept-explodeless {
  color: #d4db6b;
}
.roll-result.not-kept {
  text-decoration:line-through;
}

.js-show-extra {
  position: absolute;
}

.js-preset * {
  vertical-align: top;
}
.js-roll-label {
  font-size: 0.7em;
  width: 18em;
  padding: 0.3em;
}
/*
 * TotalStorage
 *
 * Copyright (c) 2012 Jared Novack & Upstatement (upstatement.com)
 * Dual licensed under the MIT and GPL licenses:
 * http://www.opensource.org/licenses/mit-license.php
 * http://www.gnu.org/licenses/gpl.html
 *
 * Total Storage is the conceptual the love child of jStorage by Andris Reinman,
 * and Cookie by Klaus Hartl -- though this is not connected to either project.
 *
 * @name $.totalStorage
 * @cat Plugins/Cookie
 * @author Jared Novack/jared@upstatement.com
 * @version 1.1.2
 * @url http://upstatement.com/blog/2012/01/jquery-local-storage-done-right-and-easy/
 */
 
(function(c,h){var e,d;if("localStorage"in window)try{d="undefined"===typeof window.localStorage?h:window.localStorage,e="undefined"==typeof d||"undefined"==typeof window.JSON?!1:!0}catch(j){e=!1}c.totalStorage=function(b,a){return c.totalStorage.impl.init(b,a)};c.totalStorage.setItem=function(b,a){return c.totalStorage.impl.setItem(b,a)};c.totalStorage.getItem=function(b){return c.totalStorage.impl.getItem(b)};c.totalStorage.getAll=function(){return c.totalStorage.impl.getAll()};c.totalStorage.deleteItem=
function(b){return c.totalStorage.impl.deleteItem(b)};c.totalStorage.impl={init:function(b,a){return"undefined"!=typeof a?this.setItem(b,a):this.getItem(b)},setItem:function(b,a){if(!e)try{return c.cookie(b,a),a}catch(g){console.log("Local Storage not supported by this browser. Install the cookie plugin on your site to take advantage of the same functionality. You can get it at https://github.com/carhartl/jquery-cookie")}var f=JSON.stringify(a);d.setItem(b,f);return this.parseResult(f)},getItem:function(b){if(!e)try{return this.parseResult(c.cookie(b))}catch(a){return null}b=
d.getItem(b);return this.parseResult(b)},deleteItem:function(b){if(!e)try{return c.cookie(b,null),!0}catch(a){return!1}d.removeItem(b);return!0},getAll:function(){var b=[];if(e)for(var a in d)a.length&&b.push({key:a,value:this.parseResult(d.getItem(a))});else try{var g=document.cookie.split(";");for(a=0;a<g.length;a++){var f=g[a].split("=")[0];b.push({key:f,value:this.parseResult(c.cookie(f))})}}catch(h){return null}return b},parseResult:function(b){var a;try{a=JSON.parse(b),"undefined"==typeof a&&
(a=b),"true"==a&&(a=!0),"false"==a&&(a=!1),parseFloat(a)==a&&"object"!=typeof a&&(a=parseFloat(a))}catch(c){a=b}return a}}})(jQuery);
=========
Changelog
=========

2013-05-17
==========

* presets are now remembered in localstorage

2013-05-31
==========

* presets are now editable.
* different lists of rolls (kept, kept without explodes, explodes) are now removed.
  Color-coded list of all rolls replaces them all.
* You can add extra dices after initial roll with "add" button.
  These rolls are added to totals and are also tracked in separate field.
  This allows to track "ghost dices", add "close-range bonus", "point-blank bonus"
  to ranged attacks, etc.
  This is useful together with presets: you do not have to modify your preset,
  just click roll and add modifiers after it's rolled.
* explode on 9 checkbox added (for volatile weapons).
* presets are now stored as list of ojects instead of html. This will allow me to
  update layout of presets in dice roller without resetting your saved presets.
/**
 * Copyright (c) 2007-2014 Ariel Flesler - aflesler<a>gmail<d>com | http://flesler.blogspot.com
 * Licensed under MIT
 * @author Ariel Flesler
 * @version 1.4.12
 */
;(function(a){if(typeof define==='function'&&define.amd){define(['jquery'],a)}else{a(jQuery)}}(function($){var j=$.scrollTo=function(a,b,c){return $(window).scrollTo(a,b,c)};j.defaults={axis:'xy',duration:parseFloat($.fn.jquery)>=1.3?0:1,limit:true};j.window=function(a){return $(window)._scrollable()};$.fn._scrollable=function(){return this.map(function(){var a=this,isWin=!a.nodeName||$.inArray(a.nodeName.toLowerCase(),['iframe','#document','html','body'])!=-1;if(!isWin)return a;var b=(a.contentWindow||a).document||a.ownerDocument||a;return/webkit/i.test(navigator.userAgent)||b.compatMode=='BackCompat'?b.body:b.documentElement})};$.fn.scrollTo=function(f,g,h){if(typeof g=='object'){h=g;g=0}if(typeof h=='function')h={onAfter:h};if(f=='max')f=9e9;h=$.extend({},j.defaults,h);g=g||h.duration;h.queue=h.queue&&h.axis.length>1;if(h.queue)g/=2;h.offset=both(h.offset);h.over=both(h.over);return this._scrollable().each(function(){if(f==null)return;var d=this,$elem=$(d),targ=f,toff,attr={},win=$elem.is('html,body');switch(typeof targ){case'number':case'string':if(/^([+-]=?)?\d+(\.\d+)?(px|%)?$/.test(targ)){targ=both(targ);break}targ=win?$(targ):$(targ,this);if(!targ.length)return;case'object':if(targ.is||targ.style)toff=(targ=$(targ)).offset()}var e=$.isFunction(h.offset)&&h.offset(d,targ)||h.offset;$.each(h.axis.split(''),function(i,a){var b=a=='x'?'Left':'Top',pos=b.toLowerCase(),key='scroll'+b,old=d[key],max=j.max(d,a);if(toff){attr[key]=toff[pos]+(win?0:old-$elem.offset()[pos]);if(h.margin){attr[key]-=parseInt(targ.css('margin'+b))||0;attr[key]-=parseInt(targ.css('border'+b+'Width'))||0}attr[key]+=e[pos]||0;if(h.over[pos])attr[key]+=targ[a=='x'?'width':'height']()*h.over[pos]}else{var c=targ[pos];attr[key]=c.slice&&c.slice(-1)=='%'?parseFloat(c)/100*max:c}if(h.limit&&/^\d+$/.test(attr[key]))attr[key]=attr[key]<=0?0:Math.min(attr[key],max);if(!i&&h.queue){if(old!=attr[key])animate(h.onAfterFirst);delete attr[key]}});animate(h.onAfter);function animate(a){$elem.animate(attr,g,h.easing,a&&function(){a.call(this,targ,h)})}}).end()};j.max=function(a,b){var c=b=='x'?'Width':'Height',scroll='scroll'+c;if(!$(a).is('html,body'))return a[scroll]-$(a)[c.toLowerCase()]();var d='client'+c,html=a.ownerDocument.documentElement,body=a.ownerDocument.body;return Math.max(html[scroll],body[scroll])-Math.min(html[d],body[d])};function both(a){return $.isFunction(a)||typeof a=='object'?a:{top:a,left:a}};return j}));
====
TODO
====

* Allow to rearrange remembered items.