<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[3699] sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments: WordCamp Budgets: Another pass at the new budget tool.</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta" style="font-size: 105%">
<dt style="float: left; width: 6em; font-weight: bold">Revision</dt> <dd><a style="font-weight: bold" href="http://meta.trac.wordpress.org/changeset/3699">3699</a><script type="application/ld+json">{"@context":"http://schema.org","@type":"EmailMessage","description":"Review this Commit","action":{"@type":"ViewAction","url":"http://meta.trac.wordpress.org/changeset/3699","name":"Review Commit"}}</script></dd>
<dt style="float: left; width: 6em; font-weight: bold">Author</dt> <dd>kovshenin</dd>
<dt style="float: left; width: 6em; font-weight: bold">Date</dt> <dd>2016-07-20 16:51:48 +0000 (Wed, 20 Jul 2016)</dd>
</dl>

<pre style='padding-left: 1em; margin: 2em 0; border-left: 2px solid #ccc; line-height: 1.25; font-size: 105%; font-family: sans-serif'>WordCamp Budgets: Another pass at the new budget tool.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentscsswordcampbudgetscss">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/css/wordcamp-budgets.css</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentsincludesbudgettoolphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/includes/budget-tool.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentsincludeswordcampbudgetsphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/includes/wordcamp-budgets.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentsviewsbudgettoolmainphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/views/budget-tool/main.php</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentsjavascriptbudgettooljs">sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/javascript/budget-tool.js</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentscsswordcampbudgetscss"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/css/wordcamp-budgets.css</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/css/wordcamp-budgets.css        2016-07-20 16:41:16 UTC (rev 3698)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/css/wordcamp-budgets.css  2016-07-20 16:51:48 UTC (rev 3699)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -181,3 +181,166 @@
</span><span class="cx" style="display: block; padding: 0 10px">                font-weight: bold;
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+/* Budget Tool */
+.wcb-budget-tool .left {
+       width: 50%;
+       float: left;
+}
+
+.wcb-budget-tool .right {
+       width: 50%;
+       float: left;
+}
+
+.wcb-budget-container {
+    width: 100%;
+    table-layout: fixed;
+    white-space:nowrap;
+    text-align: left;
+    border-collapse: collapse;
+    background: white;
+    margin: 12px 0;
+}
+
+.wcb-budget-container,
+.wcb-budget-container td,
+.wcb-budget-container th {
+    overflow: hidden;
+    text-overflow: ellipsis;
+    border: solid 1px #ccc;
+    height: 30px;
+    line-height: 30px;
+}
+
+.wcb-budget-container th {
+    background: #f8f8f8;
+}
+
+.wcb-budget-container td,
+.wcb-budget-container th {
+    vertical-align: top;
+    padding: 0 4px;
+}
+
+.wcb-budget-container tr.has-changed td {
+    background: #f7ecdc;
+}
+
+.wcb-budget-container tr.is-new td {
+    background: #dcf7e0;
+}
+
+.wcb-budget-container .wcb-entry td.editable {
+    padding: 0;
+}
+
+.wcb-budget-container .wcb-entry input,
+.wcb-budget-container .wcb-entry select {
+    float: left;
+    padding: 0 4px;
+    width: 100%;
+    border: 0;
+    margin: 0;
+    background: transparent;
+    -webkit-appearance: none;
+    -moz-appearance: none;
+    appearance: none;
+    border: none;
+    box-shadow: none;
+    border-radius: 0;
+    height: 30px;
+    line-height: 30px;
+    font-size: inherit;
+    cursor: default;
+}
+
+.wcb-budget-container .wcb-entry td.focused {
+    background: #e8f1f7;
+    border: double 1px #999;
+}
+
+.wcb-budget-container tr.is-new td.focused {
+    background: #ecf7ee;
+}
+
+.wcb-budget-container .wcb-entry input:focus {
+    cursor: text;
+}
+
+.wcb-budget-container .dashicons {
+    color: inherit;
+    text-decoration: none;
+    line-height: 30px;
+    color: #aaa;
+}
+
+.wcb-budget-container .actions {
+    text-align: right;
+}
+
+.wcb-budget-container tr:hover .dashicons {
+    color: #444;
+}
+
+.wcb-budget-container .amount {
+    text-align: right;
+}
+
+.wcb-budget-container .link {
+    float: right;
+    margin: -4px 4px 4px 0;
+    text-align: right;
+    font-size: 10px;
+    color: #aaa;
+    line-height: 10px;
+}
+
+.wcb-budget-container .wcb-entry td.focused .link-toggle {
+    opacity: 1;
+}
+
+.wcb-budget-container .wcb-entry td.focused .link span {
+    opacity: 0;
+}
+
+.wcb-budget-container .link-toggle {
+    opacity: 0;
+    position: absolute;
+    text-decoration: none;
+    color: #444;
+}
+
+.wcb-budget-container .link-toggle .dashicons {
+    font-size: 16px;
+    padding-left: 2px;
+    padding-right: 2px;
+    padding-top: 1px;
+}
+
+.wcb-budget-container .link-toggle .link-value {
+    position: absolute;
+    background: red;
+    width: 30px;
+    opacity: 0;
+}
+
+.wcb-expense-placeholder,
+.wcb-income-placeholder {
+    color: #aaa;
+    font-style: italic;
+    cursor: pointer;
+}
+
+.wcb-budget-summary,
+.wcb-budget-summary td,
+.wcb-budget-summary th {
+    border-left: none;
+}
+
+.wcb-negative {
+    color: red;
+}
+
+.wcb-budget-summary .inspire {
+    color: #aaa;
+}
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentsincludesbudgettoolphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/includes/budget-tool.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/includes/budget-tool.php        2016-07-20 16:41:16 UTC (rev 3698)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/includes/budget-tool.php  2016-07-20 16:51:48 UTC (rev 3699)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -8,16 +8,101 @@
</span><span class="cx" style="display: block; padding: 0 10px">     public static function admin_menu() {
</span><span class="cx" style="display: block; padding: 0 10px">                add_submenu_page( 'wordcamp-budget', __( 'WordCamp Budget', 'wordcamporg' ), __( 'Budget', 'wordcamporg' ), 'manage_options', 'wordcamp-budget' );
</span><span class="cx" style="display: block; padding: 0 10px">         add_action( 'wcb_render_budget_page', array( __CLASS__, 'render' ) );
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        register_setting( 'wcb_budget_noop', 'wcb_budget_noop', array( __CLASS__, 'validate' ) );
</ins><span class="cx" style="display: block; padding: 0 10px">     }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    public static function validate( $noop ) {
+        if ( empty( $_POST['_wcb_budget_data'] ) )
+            return;
+
+        if ( empty( $_REQUEST['_wpnonce'] ) || ! wp_verify_nonce( $_REQUEST['_wpnonce'], 'wcb_budget_noop-options' ) )
+            return;
+
+        $budget = get_option( 'wcb_budget', array() );
+
+        // TODO: Add prelim, approved, current logic here.
+        $data = json_decode( wp_unslash( $_POST['_wcb_budget_data'] ), true );
+        $valid_attributes = array( 'type', 'category', 'amount', 'note', 'link', 'name', 'value' );
+        foreach ( $data as &$item ) {
+            $_item = array();
+            foreach ( $item as $key => $value ) {
+                if ( ! in_array( $key, $valid_attributes ) )
+                    continue;
+
+                if ( $key == 'amount' )
+                    $value = round( floatval( $value ), 2 );
+
+                $_item[ $key ] = $value;
+            }
+
+            $item = $_item;
+            print_r( $item );
+        }
+
+        $budget['current'] = $data;
+        $budget['updated'] = time();
+        update_option( 'wcb_budget', $budget, 'no' );
+        return;
+    }
+
</ins><span class="cx" style="display: block; padding: 0 10px">     public static function enqueue_scripts() {
</span><span class="cx" style="display: block; padding: 0 10px">         $screen = get_current_screen();
</span><span class="cx" style="display: block; padding: 0 10px">         if ( $screen->id == 'toplevel_page_wordcamp-budget' ) {
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-            wp_enqueue_script( 'backbone' );
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+            wp_enqueue_script( 'wcb-budget-tool',
+                plugins_url( 'javascript/budget-tool.js', __DIR__ ),
+                array( 'backbone', 'jquery', 'underscore' ), 1 , true );
</ins><span class="cx" style="display: block; padding: 0 10px">         }
</span><span class="cx" style="display: block; padding: 0 10px">     }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    private static function _get_default_budget() {
+        return array(
+            array( 'type' => 'meta', 'name' => 'attendees', 'value' => 300 ),
+            array( 'type' => 'meta', 'name' => 'days', 'value' => 2 ),
+            array( 'type' => 'meta', 'name' => 'tracks', 'value' => 4 ),
+            array( 'type' => 'meta', 'name' => 'speakers', 'value' => 25 ),
+            array( 'type' => 'meta', 'name' => 'volunteers', 'value' => 10 ),
+            array( 'type' => 'meta', 'name' => 'currency', 'value' => 'USD' ),
+            array( 'type' => 'meta', 'name' => 'ticket-price', 'value' => 20.00 ),
+
+            array( 'type' => 'income', 'category' => 'other', 'note' => 'Tickets Income', 'amount' => 0, 'link' => 'ticket-price-x-attendees' ),
+            array( 'type' => 'income', 'category' => 'other', 'note' => 'Community Sponsorships', 'amount' => 4300 ),
+            array( 'type' => 'income', 'category' => 'other', 'note' => 'Local Sponsorships', 'amount' => 7000 ),
+            array( 'type' => 'income', 'category' => 'other', 'note' => 'Microsponsors', 'amount' => 500 ),
+
+            array( 'type' => 'expense', 'category' => 'venue', 'note' => 'Venue', 'amount' => 7500 ),
+            array( 'type' => 'expense', 'category' => 'venue', 'note' => 'Wifi Costs', 'amount' => 300, 'link' => 'per-day' ),
+            array( 'type' => 'expense', 'category' => 'other', 'note' => 'Comped Tickets', 'amount' => 300 ),
+            array( 'type' => 'expense', 'category' => 'audio-visual', 'note' => 'Video recording', 'amount' => 500 ),
+            array( 'type' => 'expense', 'category' => 'audio-visual', 'note' => 'Projector rental', 'amount' => 300 ),
+            array( 'type' => 'expense', 'category' => 'audio-visual', 'note' => 'Livestream', 'amount' => 200 ),
+            array( 'type' => 'expense', 'category' => 'signage-badges', 'note' => 'Printing', 'amount' => 800 ),
+            array( 'type' => 'expense', 'category' => 'signage-badges', 'note' => 'Badges', 'amount' => 8.21, 'link' => 'per-attendee' ),
+            array( 'type' => 'expense', 'category' => 'food-beverage', 'note' => 'Snacks', 'amount' => 300 ),
+            array( 'type' => 'expense', 'category' => 'food-beverage', 'note' => 'Lunch', 'amount' => 2350 ),
+            array( 'type' => 'expense', 'category' => 'food-beverage', 'note' => 'Coffee', 'amount' => 500 ),
+            array( 'type' => 'expense', 'category' => 'swag', 'note' => 'T-shirts', 'amount' => 780 ),
+            array( 'type' => 'expense', 'category' => 'speaker-event', 'note' => 'Speakers Dinner', 'amount' => 20, 'link' => 'per-speaker' ),
+        );
+    }
+
</ins><span class="cx" style="display: block; padding: 0 10px">     public static function render() {
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        $budget = get_option( 'wcb_budget' );
+        $budget = ! empty( $budget['current'] ) ? $budget['current'] : self::_get_default_budget();
+
+        if ( ! $inspire_urls = get_site_transient( 'wcb-inspire-urls' ) ) {
+            $urls = array( 'https://jawordpressorg.github.io/wapuu/wapuu-archive/original-wapuu.png' );
+            $r = wp_remote_get( 'https://jawordpressorg.github.io/wapuu-api/v1/wapuu.json' );
+            if ( ! is_wp_error( $r ) && wp_remote_retrieve_response_code( $r ) == 200 ) {
+                $body = json_decode( wp_remote_retrieve_body( $r ), true );
+                $maybe_urls = wp_list_pluck( wp_list_pluck( $body, 'wapuu' ), 'src' );
+                if ( count( $maybe_urls ) > 0 ) {
+                    $inspire_urls = $maybe_urls;
+                }
+            }
+
+            set_site_transient( 'wcb-inspire-urls', $inspire_urls, 30 * DAY_IN_SECONDS );
+        }
+
</ins><span class="cx" style="display: block; padding: 0 10px">         require( dirname( __DIR__ ) . '/views/budget-tool/main.php' );
</span><span class="cx" style="display: block; padding: 0 10px">     }
</span><span class="cx" style="display: block; padding: 0 10px"> }
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentsincludeswordcampbudgetsphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/includes/wordcamp-budgets.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/includes/wordcamp-budgets.php   2016-07-20 16:41:16 UTC (rev 3698)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/includes/wordcamp-budgets.php     2016-07-20 16:51:48 UTC (rev 3699)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -160,7 +160,7 @@
</span><span class="cx" style="display: block; padding: 0 10px">                        'wordcamp-budgets',
</span><span class="cx" style="display: block; padding: 0 10px">                        plugins_url( 'css/wordcamp-budgets.css', __DIR__ ),
</span><span class="cx" style="display: block; padding: 0 10px">                        $soft_deps,
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                        4
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                 5
</ins><span class="cx" style="display: block; padding: 0 10px">                 );
</span><span class="cx" style="display: block; padding: 0 10px">        }
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentsjavascriptbudgettooljs"></a>
<div class="addfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Added: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/javascript/budget-tool.js</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/javascript/budget-tool.js                               (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/javascript/budget-tool.js 2016-07-20 16:51:48 UTC (rev 3699)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,423 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+window.wcb = window.wcb || {models:{}, input:[]};
+
+(function($){
+    var $container = $('.wcb-budget-container tbody'),
+        $income = $container.find('.wcb-income-placeholder'),
+        $expense = $container.find('.wcb-expense-placeholder'),
+        $meta = $container.find('.wcb-meta-placeholder'),
+        $summary = $('.wcb-summary-placeholder'),
+        $form = $('.wcb-submit-form');
+
+    var template_options = {
+               evaluate:    /<#([\s\S]+?)#>/g,
+               interpolate: /\{\{\{([\s\S]+?)\}\}\}/g,
+               escape:      /\{\{([^\}]+?)\}\}(?!\})/g,
+               variable:    'data'
+    };
+
+    var Entry = Backbone.Model.extend({
+               defaults: {
+                       type: 'expense',
+            category: 'other',
+            amount: 0,
+            note: '',
+            new: false,
+            link: null,
+
+            // metadata
+            name: '',
+            value: null
+               },
+
+        initialize: function() {
+            this._realAmount = this.getRealAmount();
+            this._attr = _.clone(this.attributes);
+        },
+
+        getRealAmount: function() {
+            if (!this.get('link'))
+                return this.get('amount');
+
+            if (this.get('link') in wcb.linkData) {
+                var link = wcb.linkData[this.get('link')]
+                return link.callback(this.get('amount'));
+            }
+
+            return 0;
+        },
+
+        getLinkLabel: function() {
+            if (!this.get('link'))
+                return '';
+
+            if (this.get('link') in wcb.linkData)
+                return wcb.linkData[this.get('link')].label;
+
+            return '';
+        },
+
+        linkHasValue: function() {
+            if (!this.get('link'))
+                return false;
+
+            if (this.get('link') in wcb.linkData)
+                return wcb.linkData[this.get('link')].hasValue;
+
+            return false;
+        },
+
+        hasChanged: function() {
+            // console.log(this._attr);
+            // console.log(this.attributes);
+            // console.log(this._realAmount)
+
+            var _stringify = function(v) {
+                if (!v) return v;
+                return v.toString();
+            }
+
+            var changed = _.isEqual(
+                _.map(this._attr, _stringify),
+                _.map(this.attributes, _stringify)
+            ) && this._realAmount == this.getRealAmount();
+            return !changed;
+        },
+
+        editStart: function() {
+            this.trigger('edit-start.wordcamp-budgets');
+        }
+    });
+
+    var SummaryView = Backbone.View.extend({
+        className: 'wcb-budget-container wcb-budget-summary',
+        tagName: 'table',
+        urls: [],
+
+        events: {
+            'click .inspire': 'inspire'
+        },
+
+        render: function() {
+            var data = {
+                'income': 0,
+                'expenses': 0,
+                'variance': 0,
+                'per_person': 0
+            };
+
+            _.each(wcb.table.collection.where({type: 'income'}), function(item) {
+                data['income'] += item.getRealAmount();
+            });
+
+            _.each(wcb.table.collection.where({type: 'expense'}), function(item) {
+                data['expenses'] += item.getRealAmount();
+            });
+
+            data['variance'] = data['income'] - data['expenses'];
+            var attendees = wcb.table.collection.findWhere({type: 'meta', name: 'attendees'});
+            data['per_person'] = attendees ? data['expenses'] / attendees.get('value') : 0;
+
+            this.template = _.template($('#wcb-tmpl-summary').html(), null, template_options);
+            this.$el.html(this.template(data));
+            return this;
+        },
+
+        initialize: function() {
+            $summary.append(this.render().el);
+            return this;
+        },
+
+        inspire: function(e) {
+            e.target.href = this.urls[Math.floor(Math.random()*this.urls.length)];
+            return true;
+        }
+    });
+
+    var EntryView = Backbone.View.extend({
+        className: 'wcb-entry',
+        tagName: 'tr',
+        cancel: false,
+
+        events: {
+            'keyup': 'keyup',
+            'click .delete': 'delete',
+            'change input': 'editSave',
+            'change select.category': 'editSave',
+            'change select.link-value': 'linkChange',
+
+            'focus input, select': 'focus',
+            'blur input, select': 'blur'
+        },
+
+        initialize: function() {
+            this.model.bind('change', this.render, this);
+            this.model.bind('destroy', this.remove, this);
+        },
+
+        linkChange: function() {
+            this.model.set('link', this.$el.find('.link-value').val() || null);
+            return this;
+        },
+
+        focus: function(e) {
+            var $target = $(e.target);
+            $target.parents('td').addClass('focused');
+
+            if (($target.hasClass('amount') || $target.hasClass('link-value')) && this.model.get('link') && this.model.linkHasValue())
+                this.$el.find('.amount').val(this.model.get('amount').toFixed(2));
+
+            return this;
+        },
+
+        blur: function(e) {
+            var $target = $(e.target);
+            $target.parents('td').removeClass('focused');
+
+            if (($target.hasClass('amount') || $target.hasClass('link-value')) && this.model.get('link'))
+                this.$el.find('.amount').val(this.model.getRealAmount().toFixed(2));
+
+            return this;
+        },
+
+        render: function() {
+            var data = this.model.toJSON();
+            data.realAmount = this.model.getRealAmount();
+            data.linkLabel = this.model.getLinkLabel();
+            data.linkHasValue = this.model.linkHasValue();
+
+            this.template = _.template($('#wcb-tmpl-entry').html(), null, template_options);
+            this.$el.html(this.template(data));
+            this.$el.toggleClass('has-changed', this.model.hasChanged() && ! this.model.get('new'));
+            this.$el.toggleClass('is-new', this.model.get('new'));
+            return this;
+        },
+
+        keyup: function(e) {
+            if (e.keyCode == 27) {
+                return this.editCancel.apply(this, arguments);
+            } else if (e.keyCode == 13) {
+                return this.editSave.apply(this, arguments);
+            }
+
+            return this;
+        },
+
+        editSave: function(e) {
+            if (this.model.get('type') == 'meta') {
+                this.model.set('value', this.$el.find('.value').val());
+            } else {
+                this.model.set('note', this.$el.find('.note').val());
+                this.model.set('category', this.$el.find('.category').val());
+
+                var $target = $(e.target);
+                if ($target.hasClass('amount') || $target.hasClass('link-value'))
+                    this.model.set('amount', parseFloat(this.$el.find('.amount').val()));
+            }
+
+            this.clearSelection.apply(this);
+            this.render.apply(this);
+            return false;
+        },
+
+        editCancel: function(e) {
+            this.clearSelection.apply(this);
+
+            if (this.model.get('type') == 'meta') {
+                this.$el.find('.value').val(this.model.get('value'));
+            } else {
+                this.$el.find('.amount').val(this.model.get('amount'));
+                this.$el.find('.note').val(this.model.get('note'));
+            }
+
+            this.editSave.apply(this, arguments);
+            return false;
+        },
+
+        clearSelection: function() {
+            if (window.getSelection) {
+                if (window.getSelection().empty) {
+                    window.getSelection().empty();
+                } else if (window.getSelection().removeAllRanges) {
+                    window.getSelection().removeAllRanges();
+                }
+            } else if (document.selection) {
+                document.selection.empty();
+            }
+        },
+
+        delete: function() {
+            this.model.destroy();
+            wcb.summary.render.apply(wcb.summary);
+            return false;
+        }
+    });
+
+    var Entries = Backbone.Collection.extend({
+        model: Entry
+    });
+
+    var EntriesView = Backbone.View.extend({
+        tagName: 'table',
+        className: 'wcb-table',
+
+        initialize: function() {
+            this.collection.bind('add', this.addOne, this);
+            this.collection.bind('change reset', this.refresh, this);
+        },
+
+        refresh: function(model) {
+            if (model.get('type') == 'meta') {
+                this.render.apply(this);
+            }
+
+            wcb.summary.render.apply(wcb.summary);
+            return this;
+        },
+
+        addOne: function(item) {
+            var view = new EntryView({model: item});
+            switch (view.model.get('type')) {
+                case 'expense':
+                    var $c = $expense;
+                    break;
+                case 'income':
+                    var $c = $income;
+                    break;
+                case 'meta':
+                default:
+                    var $c = $meta;
+            }
+
+            $c.before(view.render().el);
+        },
+
+        render: function() {
+            $container.find('.wcb-entry').remove();
+            this.collection.each(this.addOne, this);
+            return this;
+        }
+    });
+
+    wcb.categories = {
+        'venue': 'Venue',
+        'audio-visual': 'Audio Visual',
+        'after-party': 'After Party',
+        'camera-shipping': 'Camera Shipping',
+        'food-beverage': 'Food & Beverage',
+        'office-supplies': 'Office Supplies',
+        'signage-badges': 'Signage & Badges',
+        'speaker-event': 'Speaker Event',
+        'swag': 'Swag',
+        'other': 'Other'
+    };
+
+    wcb.metaLabels = {
+        'attendees': 'Attendees',
+        'days': 'Days',
+        'tracks': 'Tracks',
+        'speakers': 'Speakers',
+        'volunteers': 'Volunteers',
+        'currency': 'Currency',
+        'ticket-price': 'Ticket Price'
+    };
+
+    wcb.linkData = {
+        'per-speaker': {
+            'label': 'per speaker',
+            'hasValue': true,
+            'callback': function(value) {
+                return value * wcb.table.collection.findWhere({type: 'meta', name: 'speakers'}).get('value');
+            }
+        },
+
+        'per-volunteer': {
+            'label': 'per volunteer',
+            'hasValue': true,
+            'callback': function(value) {
+                return value * wcb.table.collection.findWhere({type: 'meta', name: 'volunteers'}).get('value');
+            }
+        },
+
+        'per-attendee': {
+            'label': 'per attendee',
+            'hasValue': true,
+            'callback': function(value) {
+                return value * wcb.table.collection.findWhere({type: 'meta', name: 'attendees'}).get('value');
+            }
+        },
+
+        'per-day': {
+            'label': 'per day',
+            'hasValue': true,
+            'callback': function(value) {
+                return value * wcb.table.collection.findWhere({type: 'meta', name: 'days'}).get('value');
+            }
+        },
+
+        'per-track': {
+            'label': 'per track',
+            'hasValue': true,
+            'callback': function(value) {
+                return value * wcb.table.collection.findWhere({type: 'meta', name: 'tracks'}).get('value');
+            }
+        },
+
+        'ticket-price-x-attendees': {
+            'label': 'ticket price &times; attendees',
+            'hasValue': false,
+            'callback': function(value) {
+                var attendees = wcb.table.collection.findWhere({type: 'meta', name: 'attendees'}).get('value');
+                var price = wcb.table.collection.findWhere({type: 'meta', name: 'ticket-price'}).get('value');
+                return attendees * price;
+            }
+        },
+
+        'random': {
+            'label': 'random',
+            'hasValue': false,
+            'callback': function(value) {
+                return Math.floor((Math.random() * 500) + 1);
+            }
+        }
+    };
+
+    var table = new EntriesView({collection: new Entries()});
+
+    $income.on('click', function() {
+        table.collection.add(new wcb.models.Entry({
+            type: 'income',
+            amount: 0,
+            note: 'New Income Item',
+            category: 'other',
+            new: true
+        })).editStart();
+        return false;
+    });
+
+    $expense.on('click', function() {
+        table.collection.add(new wcb.models.Entry({
+            type: 'expense',
+            amount: 0,
+            note: 'New Expense Item',
+            category: 'other',
+            new: true
+        })).editStart();
+        return false;
+    });
+
+    $form.on('submit', function() {
+        $form.find('[name="_wcb_budget_data"]').val(JSON.stringify(wcb.table.collection));
+        return true;
+    });
+
+    wcb.models.Entry = Entry;
+    wcb.table = table;
+    wcb.summary = new SummaryView();
+
+    _.each(wcb.input, function(i){
+        wcb.table.collection.add(new wcb.models.Entry(i));
+    });
+
+    wcb.summary.urls = wcb.urls;
+    wcb.summary.render();
+}(jQuery));
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginswordcamppaymentsviewsbudgettoolmainphp"></a>
<div class="modfile"><h4 style="background-color: #eee; color: inherit; margin: 1em 0; padding: 1.3em; font-size: 115%">Modified: sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/views/budget-tool/main.php</h4>
<pre class="diff"><span>
<span class="info" style="display: block; padding: 0 10px; color: #888">--- sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/views/budget-tool/main.php      2016-07-20 16:41:16 UTC (rev 3698)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/wordcamp-payments/views/budget-tool/main.php        2016-07-20 16:51:48 UTC (rev 3699)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -1,224 +1,130 @@
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<?php
-    $budget = array(
-        array( 'type' => 'meta', 'name' => 'attendees', 'value' => 300 ),
-        array( 'type' => 'meta', 'name' => 'days', 'value' => 2 ),
-        array( 'type' => 'meta', 'name' => 'tracks', 'value' => 4 ),
-        array( 'type' => 'meta', 'name' => 'speakers', 'value' => 25 ),
-        array( 'type' => 'meta', 'name' => 'volunteers', 'value' => 10 ),
-        array( 'type' => 'meta', 'name' => 'currency', 'value' => 'USD' ),
-        array( 'type' => 'meta', 'name' => 'ticket-price', 'value' => 20.00 ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<script>
+window.wcb = window.wcb || {models:{}, input:[]};
+wcb.input = <?php echo json_encode( $budget ); ?>;
+wcb.urls = <?php echo json_encode( $inspire_urls ); ?>;
+</script>
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        array( 'type' => 'income', 'category' => 'other', 'note' => 'Tickets Income', 'amount' => 3500 ),
-        array( 'type' => 'income', 'category' => 'other', 'note' => 'Community Sponsorships', 'amount' => 4300 ),
-        array( 'type' => 'income', 'category' => 'other', 'note' => 'Local Sponsorships', 'amount' => 7000 ),
-        array( 'type' => 'income', 'category' => 'other', 'note' => 'Microsponsors', 'amount' => 500 ),
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<div class="wrap wcb-budget-tool">
+    <h2 class="nav-tab-wrapper wp-clearfix">
+               <a href="#" class="nav-tab nav-tab-active">Preliminary Budget</a>
+               <!--<a href="#" class="nav-tab">Working Budget</a>-->
+       </h2>
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        array( 'type' => 'expense', 'category' => 'venue', 'note' => 'Venue', 'amount' => 7500 ),
-        array( 'type' => 'expense', 'category' => 'venue', 'note' => 'Wifi Costs', 'amount' => 300, 'link' => 'per-day' ),
-        array( 'type' => 'expense', 'category' => 'other', 'note' => 'Comped Tickets', 'amount' => 300 ),
-        array( 'type' => 'expense', 'category' => 'audio-visual', 'note' => 'Video recording', 'amount' => 500 ),
-        array( 'type' => 'expense', 'category' => 'audio-visual', 'note' => 'Projector rental', 'amount' => 300 ),
-        array( 'type' => 'expense', 'category' => 'audio-visual', 'note' => 'Livestream', 'amount' => 200 ),
-        array( 'type' => 'expense', 'category' => 'signage-badges', 'note' => 'Printing', 'amount' => 800 ),
-        array( 'type' => 'expense', 'category' => 'signage-badges', 'note' => 'Badges', 'amount' => 8.21, 'link' => 'per-attendee' ),
-        array( 'type' => 'expense', 'category' => 'food-beverage', 'note' => 'Snacks', 'amount' => 300 ),
-        array( 'type' => 'expense', 'category' => 'food-beverage', 'note' => 'Lunch', 'amount' => 2350 ),
-        array( 'type' => 'expense', 'category' => 'food-beverage', 'note' => 'Coffee', 'amount' => 500 ),
-        array( 'type' => 'expense', 'category' => 'swag', 'note' => 'T-shirts', 'amount' => 780 ),
-        array( 'type' => 'expense', 'category' => 'speaker-event', 'note' => 'Speakers Dinner', 'amount' => 20, 'link' => 'per-speaker' ),
-    );
-?>
-<script>var wcb_data = <?php echo json_encode( $budget ); ?>;</script>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    <p style="max-width: 800px;">Welcome to your WordCamp budget, it's time to crunch some numbers! When you're done with the preliminary budget, hit the "Submit for Approval" button below â€“ a WordCamp deputy will be notified and will review your work. If you're having trouble with these numbers, or if you have any questions, don't hesitate to reach out to your mentor or Central.</p>
</ins><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<div class="wrap">
-    <h1>WordCamp Budget</h1>
-    <table id="wcb-budget-container">
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    <div class="left">
+        <h2>Event Data</h2>
+        <table class="wcb-budget-container">
+            <tbody>
+                <tr class="wcb-group-header">
+                    <th style="width: 50%;">Name</th>
+                    <th style="width: 50%;">Value</th>
+                </tr>
+                <tr class="wcb-meta-placeholder" style="display: none;">
+                    <td colspan="2"></td>
+                </tr>
+            </tbody>
+        </table>
+    </div>
+    <div class="right">
+        <h2>Summary</h2>
+        <div class="wcb-summary-placeholder"></div>
+    </div>
+
+    <div class="clear"></div>
+
+    <h2>Expenses</h2>
+    <table class="wcb-budget-container">
</ins><span class="cx" style="display: block; padding: 0 10px">         <tbody>
</span><span class="cx" style="display: block; padding: 0 10px">             <tr class="wcb-group-header">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <th colspan="4">Event Data</th>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                <th style="width: 25%;">Category</th>
+                <th style="width: 25%;">Detail</th>
+                <th style="width: 25%;" class="amount">Amount</th>
+                <th style="width: 25%;"></th>
</ins><span class="cx" style="display: block; padding: 0 10px">             </tr>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-            <tr class="wcb-meta-placeholder" style="display: none;">
-                <td colspan="4"></td>
-            </tr>
-            <tr class="wcb-group-header">
-                <th style="width: 200px;">Category</th>
-                <th>Detail</th>
-                <th style="width: 200px;" class="amount">Amount</th>
-                <th style="width: 100px;"></th>
-            </tr>
-            <tr class="wcb-group-header">
-                <th colspan="4">Expenses</th>
-            </tr>
</del><span class="cx" style="display: block; padding: 0 10px">             <tr class="wcb-expense-placeholder">
</span><span class="cx" style="display: block; padding: 0 10px">                 <td colspan="4">New Expense Item</td>
</span><span class="cx" style="display: block; padding: 0 10px">             </tr>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        </tbody>
+    </table>
+
+    <h2>Income</h2>
+    <table class="wcb-budget-container">
+        <tbody>
</ins><span class="cx" style="display: block; padding: 0 10px">             <tr class="wcb-group-header">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-                <th colspan="4">Income</th>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+                <th style="width: 25%;">Category</th>
+                <th style="width: 25%;">Detail</th>
+                <th style="width: 25%;" class="amount">Amount</th>
+                <th style="width: 25%;"></th>
</ins><span class="cx" style="display: block; padding: 0 10px">             </tr>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
</ins><span class="cx" style="display: block; padding: 0 10px">             <tr class="wcb-income-placeholder">
</span><span class="cx" style="display: block; padding: 0 10px">                 <td colspan="4">New Income Item</td>
</span><span class="cx" style="display: block; padding: 0 10px">             </tr>
</span><span class="cx" style="display: block; padding: 0 10px">         </tbody>
</span><span class="cx" style="display: block; padding: 0 10px">     </table>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-    <p class="submit">
-        <?php submit_button( 'Save Budget', 'primary', 'submit', false ); ?>
-        <a href="#" class="button">Cancel Changes</a>
-    </p>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+    <form class="wcb-submit-form" action="options.php" method="post">
+        <?php settings_fields( 'wcb_budget_noop' ); ?>
+        <input type="hidden" name="_wcb_budget_data" value="<?php echo esc_attr( json_encode( $budget ) ); ?>" />
+
+        <p class="submit">
+            <?php submit_button( 'Save Draft', 'secondary', 'wcb-budget-save-draft', false ); ?>
+            <a href="<?php echo admin_url( 'admin.php?page=wordcamp-budget' ); ?>" class="button">Cancel Changes</a>
+            <?php submit_button( 'Submit for Approval', 'primary', 'wcb-budget-submit', false ); ?>
+        </p>
+    </form>
</ins><span class="cx" style="display: block; padding: 0 10px"> </div>
</span><span class="cx" style="display: block; padding: 0 10px"> 
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-<style>
-#wcb-budget-container {
-    width: 100%;
-    table-layout: fixed;
-    white-space:nowrap;
-    text-align: left;
-    border-collapse: collapse;
-    background: white;
-    margin: 12px 0;
-}
-
-#wcb-budget-container,
-#wcb-budget-container td,
-#wcb-budget-container th {
-    overflow: hidden;
-    text-overflow: ellipsis;
-    border: solid 1px #ccc;
-    height: 30px;
-    line-height: 30px;
-}
-
-#wcb-budget-container th {
-    background: #f8f8f8;
-}
-
-#wcb-budget-container td,
-#wcb-budget-container th {
-    vertical-align: top;
-    padding: 0 4px;
-}
-
-#wcb-budget-container tr.has-changed td {
-    background: #f7ecdc;
-}
-
-#wcb-budget-container tr.is-new td {
-    background: #dcf7e0;
-}
-
-#wcb-budget-container .wcb-entry-editor td {
-    background: #d5e7f4;
-}
-
-#wcb-budget-container .wcb-entry-editor td.editable {
-    padding: 0;
-}
-
-#wcb-budget-container .wcb-entry-editor input,
-#wcb-budget-container .wcb-entry-editor select {
-    float: left;
-    padding: 0 4px;
-    width: 100%;
-    border: 0;
-    margin: 0;
-    background: transparent;
-    -webkit-appearance: none;
-    -moz-appearance: none;
-    appearance: none;
-    border: none;
-    box-shadow: none;
-    border-radius: 0;
-    height: 30px;
-    line-height: 30px;
-    font-size: inherit;
-}
-
-#wcb-budget-container .wcb-entry-editor input:focus {
-    background: #e8f1f7;
-}
-
-#wcb-budget-container .dashicons {
-    color: inherit;
-    text-decoration: none;
-    line-height: 30px;
-    color: #aaa;
-}
-
-#wcb-budget-container .actions {
-    text-align: right;
-}
-
-#wcb-budget-container tr:hover .dashicons {
-    color: #444;
-}
-
-#wcb-budget-container .amount {
-    text-align: right;
-}
-
-#wcb-budget-container .link-value {
-    display: block;
-    clear: left;
-    color: #aaa;
-    margin-top: -12px;
-}
-
-#wcb-budget-container .link-toggle {
-    position: absolute;
-    text-decoration: none;
-    color: #444;
-}
-
-#wcb-budget-container .link-toggle .dashicons {
-    font-size: 16px;
-    padding-left: 2px;
-    padding-right: 2px;
-    padding-top: 1px;
-}
-
-.wcb-expense-placeholder,
-.wcb-income-placeholder {
-    color: #aaa;
-    font-style: italic;
-    cursor: pointer;
-}
-</style>
-
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<script type="text/template" id="wcb-tmpl-summary">
+    <tbody>
+        <tr class="wcb-group-header">
+            <th style="width: 50%;"></th>
+            <th style="width: 50%;"></th>
+        </tr>
+        <tr>
+            <td>Income</td>
+            <td class="amount">{{data.income.toFixed(2)}}</td>
+        </tr>
+        <tr>
+            <td>Expenses</td>
+            <td class="amount">{{data.expenses.toFixed(2)}}</td>
+        </tr>
+        <tr>
+            <td>Variance</td>
+            <td class="amount <# if (data.variance < 0) { #>wcb-negative<# } #>">{{data.variance.toFixed(2)}}</td>
+        </tr>
+        <tr>
+            <td>Cost Per Person Per Day</td>
+            <td class="amount">{{data.per_person.toFixed(2)}}</td>
+        </tr>
+        <tr>
+            <td></td>
+            <td></td>
+        </tr>
+        <tr>
+            <td></td>
+            <td></td>
+        </tr>
+        <tr>
+            <td></td>
+            <td class="amount">
+                <# if (data.variance < 0) { #>
+                <a href="#" target="_blank" class="inspire">inspire me</a>
+                <# } #>
+            </td>
+        </tr>
+    </tbody>
+</script>
</ins><span class="cx" style="display: block; padding: 0 10px"> <script type="text/template" id="wcb-tmpl-entry">
</span><span class="cx" style="display: block; padding: 0 10px">     <# if (data.type == 'meta' ) { #>
</span><span class="cx" style="display: block; padding: 0 10px">         <td>{{wcb.metaLabels[data.name]}}</td>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <td>{{data.value}}</td>
-        <td></td>
-        <td class="actions">
-            <a href="#" class="edit"><span class="dashicons dashicons-edit"></span></a>
-        </td>
-    <# } else { #>
-        <td>{{wcb.categories[data.category]}}</td>
-        <td>{{data.note}}</td>
-        <td class="amount">
-            <span>{{data.realAmount.toFixed(2)}}</span>
-            <# if (data.link) { #>
-            <!--<span class="link-value">{{data.amount.toFixed(2)}} per attendee</span>-->
-            <# } #>
-        </td>
-        <td class="actions">
-            <a href="#" class="edit"><span class="dashicons dashicons-edit"></span></a>
-            <a href="#" class="delete"><span class="dashicons dashicons-trash"></span></a>
-        </td>
-    <# } #>
-</script>
-<script type="text/template" id="wcb-tmpl-entry-editor">
-    <# if (data.type == 'meta' ) { #>
-        <td>{{wcb.metaLabels[data.name]}}</td>
</del><span class="cx" style="display: block; padding: 0 10px">         <td class="editable">
</span><span class="cx" style="display: block; padding: 0 10px">             <input class="value" type="text" value="{{data.value}}" />
</span><span class="cx" style="display: block; padding: 0 10px">         </td>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-        <td></td>
-        <td class="actions">
-            <a href="#" class="save"><span class="dashicons dashicons-yes"></span></a>
-            <a href="#" class="cancel"><span class="dashicons dashicons-no-alt"></span></a>
-        </td>
</del><span class="cx" style="display: block; padding: 0 10px">     <# } else { #>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+
+        <# if (data.type == 'expense') { #>
</ins><span class="cx" style="display: block; padding: 0 10px">         <td class="editable">
</span><span class="cx" style="display: block; padding: 0 10px">             <select class="category">
</span><span class="cx" style="display: block; padding: 0 10px">                 <# _.each(wcb.categories, function(label,key){ #>
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -226,366 +132,40 @@
</span><span class="cx" style="display: block; padding: 0 10px">                 <#}); #>
</span><span class="cx" style="display: block; padding: 0 10px">             </select>
</span><span class="cx" style="display: block; padding: 0 10px">         </td>
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+        <# } else { #>
+        <td>
+            Income
+            <input type="hidden" class="category" value="{{data.category}}" />
+        </td>
+        <# } #>
+
</ins><span class="cx" style="display: block; padding: 0 10px">         <td class="editable">
</span><span class="cx" style="display: block; padding: 0 10px">             <input class="note" type="text" value="{{data.note}}" />
</span><span class="cx" style="display: block; padding: 0 10px">         </td>
</span><span class="cx" style="display: block; padding: 0 10px">         <td class="editable">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-            <a href="#" class="link-toggle"><span class="dashicons dashicons-admin-links"></span> {{data.link}}</a>
-            <input class="amount" type="text" value="{{data.amount.toFixed(2)}}" />
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+            <div class="link-toggle">
+                <select class="link-value">
+                    <option value="" <#if(!data.link){#>selected<#}#>>none</option>
+                    <# _.each(wcb.linkData, function(item, k) { #>
+                    <option value="{{k}}" <# if (data.link==k) { #>selected<# } #>>{{{item.label}}}</option>
+                    <# }); #>
+                </select>
+                <span class="dashicons dashicons-admin-links"></span>
+            </div>
+            <input class="amount" type="text" value="{{data.realAmount.toFixed(2)}}" />
+
+            <# if (data.link) { #>
+                <div class="link">
+                    <# if (data.linkHasValue) { #>
+                    <span>{{data.amount.toFixed(2)}}</span>
+                    <# } #>
+
+                    {{{data.linkLabel}}}
+                </div>
+            <# } #>
</ins><span class="cx" style="display: block; padding: 0 10px">         </td>
</span><span class="cx" style="display: block; padding: 0 10px">         <td class="actions">
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-            <a href="#" class="save"><span class="dashicons dashicons-yes"></span></a>
-            <a href="#" class="cancel"><span class="dashicons dashicons-no-alt"></span></a>
</del><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+            <a href="#" class="delete"><span class="dashicons dashicons-trash"></span></a>
</ins><span class="cx" style="display: block; padding: 0 10px">         </td>
</span><span class="cx" style="display: block; padding: 0 10px">     <# } #>
</span><del style="background-color: #fdd; text-decoration:none; display:block; padding: 0 10px">-</script>
-<script type="text/template" id="wcb-tmpl-entry-link">
-    <td colspan="2"></td>
-    <td class="editable">
-        <select class="link-value">
-            <option value="" <#if(!data.link){#>selected<#}#>>None</option>
-            <option value="per-attendee" <#if(data.link=='per-attendee'){#>selected<#}#>>Per Attendee</option>
-            <option value="per-speaker" <#if(data.link=='per-speaker'){#>selected<#}#>>Per Speaker</option>
-            <option value="per-day" <#if(data.link=='per-day'){#>selected<#}#>>Per Day</option>
-        </select>
-    </td>
-    <td></td>
-</script>
-
-<script>
-window.wcb = window.wcb || {models:{}};
-
-(function($){
-    $(document).on('budget-tool-render.wordcamp-budgets', function() {
-        var $container = $('#wcb-budget-container tbody'),
-            $income = $container.find('.wcb-income-placeholder'),
-            $expense = $container.find('.wcb-expense-placeholder'),
-            $meta = $container.find('.wcb-meta-placeholder');
-
-        var template_options = {
-               evaluate:    /<#([\s\S]+?)#>/g,
-               interpolate: /\{\{\{([\s\S]+?)\}\}\}/g,
-               escape:      /\{\{([^\}]+?)\}\}(?!\})/g,
-               variable:    'data'
-        };
-
-        var Category = Backbone.Model.extend({
-            defaults: {
-                label: 'Category',
-                value: 'category'
-            }
-        });
-
-        var Categories = Backbone.Collection.extend({
-            model: Category
-        });
-
-        var Entry = Backbone.Model.extend({
-                       defaults: {
-                               type: 'expense',
-                category: 'other',
-                amount: 0,
-                note: '',
-                new: false,
-                link: null,
-
-                // metadata
-                name: '',
-                value: null
-                       },
-
-            initialize: function() {
-                this._realAmount = this.getRealAmount();
-                this._attr = _.clone(this.attributes);
-            },
-
-            getRealAmount: function() {
-                if (!this.get('link'))
-                    return this.get('amount');
-
-                if (this.get('link') === 'per-attendee') {
-                    var attendees = wcb.table.collection.findWhere({type: 'meta', name: 'attendees'}).get('value');
-                    return this.get('amount') * attendees;
-                }
-
-                if (this.get('link') === 'per-speaker') {
-                    var speakers = wcb.table.collection.findWhere({type: 'meta', name: 'speakers'}).get('value');
-                    return this.get('amount') * speakers;
-                }
-
-                if (this.get('link') === 'per-day') {
-                    var days = wcb.table.collection.findWhere({type: 'meta', name: 'days'}).get('value');
-                    return this.get('amount') * days;
-                }
-
-                return 0;
-            },
-
-            hasChanged: function() {
-                // console.log(this._attr);
-                // console.log(this.attributes);
-                // console.log(this._realAmount)
-                var changed = _.isEqual(this._attr, this.attributes) && this._realAmount == this.getRealAmount()
-                return !changed;
-            },
-
-            editStart: function() {
-                this.trigger('edit-start.wordcamp-budgets');
-            }
-        });
-
-        var EntryView = Backbone.View.extend({
-            className: 'wcb-entry',
-            tagName: 'tr',
-
-            events: {
-                'dblclick td': 'editStart',
-                'click .edit': 'editStart',
-                'click .delete': 'delete',
-            },
-
-            initialize: function() {
-                this.model.bind('change', this.render, this);
-                this.model.bind('destroy', this.remove, this);
-                this.model.bind('edit-start.wordcamp-budgets', this.editStart, this);
-            },
-
-            render: function() {
-                var data = this.model.toJSON();
-                data.realAmount = this.model.getRealAmount();
-
-                this.template = _.template($('#wcb-tmpl-entry').html(), null, template_options);
-                this.$el.html(this.template(data));
-                this.$el.toggleClass('has-changed', this.model.hasChanged() && ! this.model.get('new'));
-                this.$el.toggleClass('is-new', this.model.get('new'));
-                return this;
-            },
-
-            editStart: function() {
-                if (this.editing)
-                    return false;
-
-                this.editing = true;
-                this.editor = new EntryEditorView({model: this.model});
-                this.editor.on('edit-save.wordcamp-budgets', this.editSave, this);
-                this.editor.on('edit-cancel.wordcamp-budgets', this.editCancel, this);
-
-                this.$el.after(this.editor.render().el);
-                this.$el.hide();
-                return false;
-            },
-
-            editSave: function() {
-                this.clearSelection.apply(this);
-                this.model = this.editor.model;
-                this.editor.remove();
-                this.$el.show();
-                this.editing = false;
-            },
-
-            editCancel: function() {
-                this.clearSelection.apply(this);
-                this.editor.remove();
-                this.$el.show();
-                this.editing = false;
-            },
-
-            clearSelection: function() {
-                if (window.getSelection) {
-                    if (window.getSelection().empty) {
-                        window.getSelection().empty();
-                    } else if (window.getSelection().removeAllRanges) {
-                        window.getSelection().removeAllRanges();
-                    }
-                } else if (document.selection) {
-                    document.selection.empty();
-                }
-            },
-
-            delete: function() {
-                this.model.destroy();
-                return false;
-            }
-        });
-
-        var EntryEditorView = Backbone.View.extend({
-            className: 'wcb-entry wcb-entry-editor',
-            tagName: 'tr',
-
-            events: {
-                'click .save': 'editSave',
-                'click .cancel': 'editCancel',
-                'click .link-toggle': 'linkToggle',
-                'keyup': 'keyup'
-            },
-
-            render: function() {
-                this.template = _.template($('#wcb-tmpl-entry-editor').html(), null, template_options);
-                this.$el.html(this.template(this.model.toJSON()));
-                return this;
-            },
-
-            editSave: function() {
-                this.model.set('amount', parseFloat(this.$el.find('.amount').val()));
-                this.model.set('note', this.$el.find('.note').val());
-                this.model.set('category', this.$el.find('.category').val());
-                this.model.set('value', this.$el.find('.value').val() || null);
-
-                if (this.linkView)
-                    this.model.set('link', this.linkView.$el.find('.link-value').val() || null);
-
-                this.trigger('edit-save.wordcamp-budgets', this);
-                if (this.linkView)
-                    this.linkView.remove();
-
-                return false;
-            },
-
-            editCancel: function() {
-                this.trigger('edit-cancel.wordcamp-budgets', this);
-                if (this.linkView)
-                    this.linkView.remove();
-
-                return false;
-            },
-
-            keyup: function(e) {
-                if (e.keyCode == 27) {
-                    return this.editCancel.apply(this);
-                } else if (e.keyCode == 13) {
-                    return this.editSave.apply(this);
-                }
-            },
-
-            linkToggle: function(e) {
-                if (!this.linkView) {
-                    this.linkView = new EntryLinkView({model: this.model});
-                    this.$el.after(this.linkView.render().el);
-                } else {
-                    this.linkView.$el.toggle();
-                }
-
-                return false;
-            }
-        });
-
-        var EntryLinkView = Backbone.View.extend({
-            className: 'wcb-entry wcb-entry-editor wcb-entry-link',
-            tagName: 'tr',
-
-            events: {
-
-            },
-
-            render: function() {
-                this.template = _.template($('#wcb-tmpl-entry-link').html(), null, template_options);
-                this.$el.html(this.template(this.model.toJSON()));
-                return this;
-            }
-        })
-
-        var Entries = Backbone.Collection.extend({
-            model: Entry
-        });
-
-        var EntriesView = Backbone.View.extend({
-            tagName: 'table',
-            className: 'wcb-table',
-
-            initialize: function() {
-                this.collection.bind('add', this.addOne, this);
-                this.collection.bind('change reset', this.refresh, this);
-            },
-
-            refresh: function(model) {
-                if (model.get('type') == 'meta') {
-                    this.render.apply(this);
-                }
-
-                return this;
-            },
-
-            addOne: function(item) {
-                var view = new EntryView({model: item});
-                switch (view.model.get('type')) {
-                    case 'expense':
-                        var $c = $expense;
-                        break;
-                    case 'income':
-                        var $c = $income;
-                        break;
-                    case 'meta':
-                    default:
-                        var $c = $meta;
-                }
-
-                $c.before(view.render().el);
-            },
-
-            render: function() {
-                $container.find('.wcb-entry').remove();
-                this.collection.each(this.addOne, this);
-                return this;
-            }
-        });
-
-        wcb.categories = {
-            'venue': 'Venue',
-            'audio-visual': 'Audio Visual',
-            'after-party': 'After Party',
-            'camera-shipping': 'Camera Shipping',
-            'food-beverage': 'Food & Beverage',
-            'office-supplies': 'Office Supplies',
-            'signage-badges': 'Signage & Badges',
-            'speaker-event': 'Speaker Event',
-            'swag': 'Swag',
-            'other': 'Other'
-        };
-
-        wcb.metaLabels = {
-            'attendees': 'Attendees',
-            'days': 'Days',
-            'tracks': 'Tracks',
-            'speakers': 'Speakers',
-            'volunteers': 'Volunteers',
-            'currency': 'Currency',
-            'ticket-price': 'Ticket Price'
-        };
-
-        var table = new EntriesView({collection: new Entries()});
-
-        $income.on('click', function() {
-            table.collection.add(new wcb.models.Entry({
-                type: 'income',
-                amount: 0,
-                note: 'New Income Item',
-                category: 'other',
-                new: true
-            })).editStart();
-            return false;
-        });
-
-        $expense.on('click', function() {
-            table.collection.add(new wcb.models.Entry({
-                type: 'expense',
-                amount: 0,
-                note: 'New Expense Item',
-                category: 'other',
-                new: true
-            })).editStart();
-            return false;
-        });
-
-        wcb.models.Entry = Entry;
-        wcb.table = table;
-    });
-}(jQuery));
-
-jQuery(document).ready(function(){
-    jQuery(document).trigger('budget-tool-render.wordcamp-budgets');
-
-    _.each(wcb_data, function(i){
-        wcb.table.collection.add(new wcb.models.Entry(i));
-    });
-});
</del><span class="cx" style="display: block; padding: 0 10px"> </script>
</span><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre>
</div>
</div>

</body>
</html>