<!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>[919] sites/trunk/wordcamp.org/public_html/wp-content/plugins: WordCamp.org Plugins: Add the new CampTix Attendance addon.</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/919">919</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/919","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>2014-10-21 15:23:09 +0000 (Tue, 21 Oct 2014)</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.org Plugins: Add the new CampTix Attendance addon.</pre>

<h3>Added Paths</h3>
<ul>
<li>sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/</li>
<li>sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/</li>
<li>sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/assets/</li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendanceaddonsassetsattendanceuicss">sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/assets/attendance-ui.css</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendanceaddonsassetsattendanceuijs">sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/assets/attendance-ui.js</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendanceaddonsassetsattendanceuiscss">sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/assets/attendance-ui.scss</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendanceaddonsassetsjqueryfastbuttonjs">sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/assets/jquery.fastbutton.js</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendanceaddonsattendanceuiphp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/attendance-ui.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendanceaddonsattendancephp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/attendance.php</a></li>
<li><a href="#sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendancecamptixattendancephp">sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/camptix-attendance.php</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendanceaddonsassetsattendanceuicss"></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/camptix-attendance/addons/assets/attendance-ui.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/camptix-attendance/addons/assets/attendance-ui.css                                (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/assets/attendance-ui.css  2014-10-21 15:23:09 UTC (rev 919)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,364 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+@import url(http://fonts.googleapis.com/css?family=Open+Sans:400,700);
+body {
+  background: #ccc;
+  font-family: 'Open Sans', Helvetica, sans-serif; }
+
+.overlay {
+  display: none;
+  position: absolute;
+  background: black;
+  opacity: 0.8;
+  top: 0;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  z-index: 200; }
+
+header {
+  display: block;
+  position: absolute;
+  background: #333;
+  height: 50px;
+  top: 0;
+  left: 0;
+  right: 0;
+  color: white;
+  padding-left: 16px;
+  z-index: 100; }
+
+header h1,
+.attendee-filter-view h1 {
+  display: block;
+  clear: none;
+  line-height: 50px;
+  margin: 0;
+  font-size: 16px;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  margin-right: 100px;
+  color: white; }
+
+.menu {
+  float: right;
+  height: 50px; }
+  .menu .dashicons {
+    color: white;
+    float: left;
+    width: 50px;
+    height: 50px;
+    font-size: 28px;
+    line-height: 50px;
+    text-align: center; }
+
+.loading {
+  line-height: 50px;
+  color: #999; }
+  .loading .spinner-container {
+    width: 50px;
+    height: 50px;
+    float: left;
+    overflow: hidden;
+    text-align: center; }
+  .loading .spinner {
+    display: inline-block;
+    margin-top: 14px;
+    border-top: 5px solid rgba(200, 200, 200, 0.3);
+    border-right: 5px solid rgba(200, 200, 200, 0.3);
+    border-bottom: 5px solid rgba(200, 200, 200, 0.3);
+    border-left: 5px solid #ccc;
+    -webkit-animation: load8 1.1s infinite linear;
+    animation: load8 1.1s infinite linear; }
+  .loading .spinner,
+  .loading .spinner:after {
+    border-radius: 50%;
+    width: 12px;
+    height: 12px; }
+
+li.item .spinner-container {
+  display: none;
+  width: 50px;
+  height: 50px;
+  background: white;
+  overflow: hidden;
+  position: absolute;
+  left: 0; }
+li.item.camptix-loading .spinner-container {
+  display: block; }
+li.item .spinner,
+li.item .spinner:before,
+li.item .spinner:after {
+  display: block;
+  background: #ccc;
+  -webkit-animation: load1 1s infinite ease-in-out;
+  animation: load1 1s infinite ease-in-out;
+  width: 5px;
+  height: 10px; }
+li.item .spinner:before,
+li.item .spinner:after {
+  position: absolute;
+  top: 0;
+  content: ''; }
+li.item .spinner:before {
+  left: 7px; }
+li.item .spinner {
+  margin-left: 22px;
+  margin-top: 20px;
+  position: relative;
+  font-size: 11px;
+  -webkit-animation-delay: -0.16s;
+  animation-delay: -0.16s; }
+li.item .spinner:after {
+  left: -7px;
+  -webkit-animation-delay: -0.32s;
+  animation-delay: -0.32s; }
+
+.menu .submenu {
+  display: none;
+  position: absolute;
+  top: 50px;
+  right: 0;
+  background: #444;
+  min-height: 50px; }
+  .menu .submenu a {
+    height: 51px;
+    line-height: 50px;
+    text-decoration: none;
+    color: white;
+    padding: 0 20px;
+    display: block;
+    min-width: 150px;
+    max-width: 200px;
+    overflow: hidden;
+    white-space: nowrap;
+    text-overflow: ellipsis; }
+
+.menu.dropdown .dashicons-menu {
+  background: #444; }
+
+.menu.dropdown .submenu {
+  display: block; }
+
+.attendee-search-view,
+.attendee-filter-view {
+  position: absolute;
+  top: 0;
+  right: 0;
+  left: 0;
+  height: 50px;
+  background: #444;
+  color: white; }
+  .attendee-search-view .wrapper:before,
+  .attendee-filter-view .wrapper:before {
+    content: "\f179";
+    font-family: 'dashicons';
+    -webkit-font-smoothing: antialiased;
+    font-size: 28px;
+    height: 50px;
+    width: 50px;
+    left: 0;
+    position: absolute;
+    line-height: 50px;
+    text-align: center; }
+  .attendee-search-view .close,
+  .attendee-filter-view .close {
+    float: right;
+    color: white;
+    width: 50px;
+    height: 50px;
+    font-size: 28px;
+    line-height: 50px;
+    text-align: center; }
+
+.attendee-search-view .wrapper {
+  height: 50px;
+  display: block;
+  margin-right: 50px;
+  margin-left: 50px; }
+
+.attendee-search-view input {
+  border: 0;
+  font-size: 16px;
+  font-family: 'Open Sans', Helvetica, sans-serif;
+  height: 35px;
+  margin-top: 7px;
+  padding: 0 8px;
+  width: 100%;
+  margin-left: -4px; }
+
+.attendee-filter-view {
+  z-index: 500;
+  height: auto;
+  bottom: 0;
+  overflow: scroll; }
+  .attendee-filter-view .wrapper:before {
+    content: "\f180"; }
+  .attendee-filter-view h1 {
+    padding-left: 50px; }
+  .attendee-filter-view h1.section-title {
+    padding-left: 18px;
+    border-bottom: solid 1px #555;
+    margin: 0; }
+  .attendee-filter-view ul.section-controls {
+    list-style: none;
+    margin: 0;
+    padding: 0; }
+    .attendee-filter-view ul.section-controls li {
+      height: 50px;
+      line-height: 50px;
+      margin: 0;
+      border-bottom: solid 1px #555;
+      padding-left: 50px;
+      padding-right: 50px;
+      cursor: pointer;
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis; }
+    .attendee-filter-view ul.section-controls li.selected:before {
+      content: "\f147";
+      font-family: 'dashicons';
+      -webkit-font-smoothing: antialiased;
+      font-size: 28px;
+      height: 50px;
+      width: 50px;
+      left: 0;
+      position: absolute;
+      line-height: 50px;
+      text-align: center; }
+
+.attendees-list {
+  margin: 0;
+  padding: 0;
+  background: white;
+  position: absolute;
+  top: 50px;
+  bottom: 0;
+  left: 0;
+  right: 0;
+  overflow-x: hidden;
+  overflow-y: scroll;
+  -webkit-overflow-scrolling: touch; }
+  .attendees-list li {
+    display: block;
+    float: left;
+    width: 100%;
+    height: 50px;
+    border-bottom: solid 1px #ddd;
+    -webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
+    .attendees-list li.item {
+      cursor: pointer; }
+  .attendees-list .toggle {
+    display: block;
+    float: left;
+    width: 50px;
+    height: 50px;
+    -webkit-tap-highlight-color: rgba(0, 0, 0, 0); }
+    .attendees-list .toggle .dashicons {
+      font-size: 24px;
+      line-height: 50px;
+      height: 50px;
+      width: 50px;
+      text-align: center;
+      text-decoration: none;
+      color: #ddd; }
+    .attendees-list .toggle.yes .dashicons {
+      color: #0acc00; }
+  .attendees-list .name {
+    display: block;
+    margin-left: 50px;
+    white-space: nowrap;
+    text-overflow: ellipsis;
+    overflow: hidden;
+    height: 50px;
+    line-height: 50px;
+    font-size: 16px;
+    padding-left: 2px;
+    padding-right: 16px; }
+
+.attendee-toggle-wrap {
+  margin: 0;
+  padding: 0;
+  background: white;
+  position: absolute;
+  top: 20px;
+  bottom: 20px;
+  left: 20px;
+  right: 20px;
+  overflow: scroll;
+  padding: 50px 40px;
+  text-align: center;
+  z-index: 300; }
+  .attendee-toggle-wrap .yes-no-container {
+    height: 50px;
+    display: block;
+    width: 200px;
+    margin: 40px auto 0 auto; }
+  .attendee-toggle-wrap a.yes,
+  .attendee-toggle-wrap a.no {
+    height: 50px;
+    display: block;
+    float: left;
+    width: 50%;
+    line-height: 50px;
+    font-size: 16px;
+    background: #333;
+    color: white;
+    text-decoration: none; }
+  .attendee-toggle-wrap a.yes {
+    background: #0acc00; }
+  .attendee-toggle-wrap .close {
+    font-size: 24px;
+    line-height: 50px;
+    height: 50px;
+    width: 50px;
+    text-align: center;
+    text-decoration: none;
+    color: #444;
+    position: absolute;
+    top: 0;
+    right: 0; }
+  .attendee-toggle-wrap img {
+    background: #ccc;
+    width: 150px;
+    height: 150px; }
+
+/** Animations, courtesy of https://github.com/lukehaas/css-loaders **/
+@-webkit-keyframes load1 {
+  0%,
+  80%,
+  100% {
+    box-shadow: 0 0 #ccc;
+    height: 10px; }
+
+  40% {
+    box-shadow: 0 -5px #ccc;
+    height: 18px; } }
+
+@keyframes load1 {
+  0%,
+  80%,
+  100% {
+    box-shadow: 0 0 #ccc;
+    height: 10px; }
+
+  40% {
+    box-shadow: 0 -5px #ccc;
+    height: 18px; } }
+
+@-webkit-keyframes load8 {
+  0% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg); }
+
+  100% {
+    -webkit-transform: rotate(360deg);
+    transform: rotate(360deg); } }
+
+@keyframes load8 {
+  0% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg); }
+
+  100% {
+    -webkit-transform: rotate(360deg);
+    transform: rotate(360deg); } }
</ins></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendanceaddonsassetsattendanceuijs"></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/camptix-attendance/addons/assets/attendance-ui.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/camptix-attendance/addons/assets/attendance-ui.js                         (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/assets/attendance-ui.js   2014-10-21 15:23:09 UTC (rev 919)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,591 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+var camptix = camptix || {};
+
+jQuery(document).ready(function($){
+
+       camptix.models = camptix.models || {};
+       camptix.views = camptix.views || {};
+       camptix.collections = camptix.collections || {};
+
+       /**
+        * Attendee Model
+        *
+        * This model represents an attendee and their attendance status.
+        */
+       camptix.models.Attendee = Backbone.Model.extend({
+               defaults: function() {
+                       return {
+                               status: false,
+                               avatar: '',
+                               name: '',
+                       }
+               },
+
+               /**
+                * Set the attendance status and save on server.
+                */
+               toggle: function( attended ) {
+                       this.save({ status: attended });
+               },
+
+               /**
+                * Sync attendance status with the server.
+                */
+               sync: function( method, model, options ) {
+                       var model = this;
+                       model.trigger( 'camptix:sync:start' );
+
+                       options = options || {};
+                       options.context = this;
+                       options.type = 'GET';
+
+                       options.data = _.extend( options.data || {}, {
+                               action: 'camptix-attendance',
+                               camptix_secret: _camptixAttendanceSecret
+                       });
+
+                       if ( method == 'read' ) {
+                               options.data = _.extend( options.data || {}, {
+                                       camptix_action: 'sync-model',
+                                       camptix_id: this.id
+                               });
+
+                               return wp.ajax.send( options ).done( function() { model.trigger( 'camptix:sync:end' ); } );
+
+                       } else if ( method == 'update' ) {
+                               options.data = _.extend( options.data || {}, {
+                                       camptix_action: 'sync-model',
+                                       camptix_set_attendance: this.get( 'status' ),
+                                       camptix_id: this.id
+                               });
+
+                               return wp.ajax.send( options ).done( function() { model.trigger( 'camptix:sync:end' ) } );
+                       }
+               }
+       });
+
+       /**
+        * Attendees Collection
+        *
+        * A collection to query and hold lists of attendees.
+        */
+       camptix.collections.AttendeesList = Backbone.Collection.extend({
+
+               model: camptix.models.Attendee,
+
+               initialize: function( models, options ) {
+                       this._hasMore = true;
+                       this.query = options.query;
+                       this.controller = options.controller;
+               },
+
+               /**
+                * Talk to the server for more items.
+                */
+               sync: function( method, model, options ) {
+                       if ( method == 'read' ) {
+                               options = options || {};
+                               options.context = this;
+                               options.type = 'GET';
+                               options.data = _.extend( options.data || {}, {
+                                       action: 'camptix-attendance',
+
+                                       camptix_action: 'sync-list',
+                                       camptix_paged: Math.floor( this.length / 50 ) + 1,
+                                       camptix_secret: _camptixAttendanceSecret
+                               });
+
+                               if ( this.query.search )
+                                       options.data.camptix_search = this.query.search;
+
+                               if ( this.query.filters )
+                                       options.data.camptix_filters = this.query.filters;
+
+                               return wp.ajax.send( options );
+                       }
+               },
+
+               /**
+                * Returns true if this collection (potentially) has more items.
+                */
+               hasMore: function() {
+                       return this._hasMore;
+               },
+
+               /**
+                * Get more items with this query.
+                */
+               more: function( options ) {
+                       var that = this;
+
+                       if ( ! this.hasMore() ) {
+                               return $.Deferred().resolveWith( this ).promise();
+                       }
+
+                       if ( this._more && 'pending' === this._more.state() ) {
+                               return this._more;
+                       }
+
+                       return this._more = this.fetch({ remove: false }).done( function( resp ) {
+                               if ( _.isEmpty( resp ) || resp.length < 50 ) {
+                                       that._hasMore = false;
+                                       this.controller.trigger( 'more:toggle', this._hasMore );
+                               }
+                       });
+               }
+       });
+
+       /**
+        * Attendee View
+        *
+        * A view of a single attendee in a list.
+        */
+       camptix.views.AttendeeView = Backbone.View.extend({
+               tagName: 'li',
+               className: 'item',
+
+               template: wp.template( 'attendee' ),
+
+               events: {
+                       'fastClick': 'toggle'
+               },
+
+               initialize: function( options ) {
+                       this.controller = options.controller;
+
+                       this.listenTo( this.model, 'change', this.render );
+                       this.listenTo( this.model, 'destroy', this.remove );
+                       this.listenTo( this.model, 'camptix:sync:start', this.syncStart );
+                       this.listenTo( this.model, 'camptix:sync:end', this.syncEnd );
+               },
+
+               /**
+                * Render the attendee list item.
+                */
+               render: function() {
+                       this.$el.html( this.template( this.model.toJSON() ) );
+                       return this;
+               },
+
+               /**
+                * Show a spinner.
+                */
+               syncStart: function() {
+                       this.$el.addClass( 'camptix-loading' );
+               },
+               
+               /**
+                * Hide the spinner.
+                */
+               syncEnd: function() {
+                       this.$el.removeClass( 'camptix-loading' );
+               },
+
+               /**
+                * Open the Attendee Toggle modal.
+                */
+               toggle: function() {
+                       // This touch was to stop a scroll.
+                       if ( +new Date() - this.controller.lastScroll < 200 )
+                               return;
+
+                       var toggleView = new camptix.views.AttendeeToggleView({ model: this.model, controller: this.controller });
+                       $(document.body).append( toggleView.render().el );
+               }
+       });
+
+       /**
+        * Attendee Toggle View
+        *
+        * The modal that pops up when an attendee is selected
+        * from the list. Here you can toggle their status.
+        */
+       camptix.views.AttendeeToggleView = Backbone.View.extend({
+               className: 'attendee-toggle-wrap',
+
+               template: wp.template( 'attendee-toggle' ),
+
+               events: {
+                       'fastClick .yes': 'yes',
+                       'fastClick .no': 'no',
+                       'fastClick .close': 'close'
+               },
+
+               initialize: function( options ) {
+                       this.controller = options.controller;
+                       this.$overlay = $('.overlay');
+               },
+
+               /**
+                * Render modal.
+                */
+               render: function() {
+                       this.$el.html( this.template( this.model.toJSON() ) );
+                       this.$overlay.show();
+                       return this;
+               },
+
+               /**
+                * Set to attending.
+                */
+               yes: function() {
+                       this.controller.trigger( 'flush' );
+                       this.model.toggle( true );
+                       return this.close();
+               },
+
+               /**
+                * Set to not attending.
+                */
+               no: function() {
+                       this.controller.trigger( 'flush' );
+                       this.model.toggle( false );
+                       return this.close();
+               },
+
+               /**
+                * Close modal without changing any settings.
+                */
+               close: function() {
+                       this.$overlay.hide();
+                       this.remove();
+                       return false;
+               }
+       });
+
+       /**
+        * Search View
+        *
+        * A search view invoked via Menu - Search.
+        */
+       camptix.views.AttendeeSearchView = Backbone.View.extend({
+               className: 'attendee-search-view',
+               template: wp.template( 'attendee-search' ),
+
+               events: {
+                       'input input':  'search',
+                       'keyup input':  'search',
+                       'change input': 'search',
+                       'search input': 'search',
+                       'fastClick .close': 'close'
+               },
+
+               initialize: function( options ) {
+                       if ( options && options.controller ) {
+                               this.controller = options.controller;
+                       }
+
+                       this.search = _.debounce( this.search, 500 );
+               },
+
+               /**
+                * Render Search view.
+                */
+               render: function() {
+                       this.$el.html( this.template() );
+                       return this;
+               },
+
+               /**
+                * Ask the controller to perform a new search.
+                */
+               search: function( event ) {
+                       if ( event.keyCode == 13 ) {
+                               this.$el.find( 'input' ).blur();
+                       }
+
+                       var keyword = event.target.value || '';
+                       this.controller.trigger( 'search', keyword );
+               },
+
+               /**
+                * Close the view and reset search.
+                */
+               close: function() {
+                       this.controller.trigger( 'search', '' );
+                       this.remove();
+               }
+       });
+
+       /**
+        * Filter View
+        *
+        * Invoked via Menu - Filters.
+        */
+       camptix.views.AttendeeFilterView = Backbone.View.extend({
+               className: 'attendee-filter-view',
+               template: wp.template( 'attendee-filter' ),
+
+               events: {
+                       'fastClick .close': 'close',
+                       'fastClick .filter-attendance li': 'toggleAttendance',
+                       'fastClick .filter-tickets li': 'toggleTickets'
+               },
+
+               initialize: function( options ) {
+                       this.controller = options.controller;
+                       this.filterSettings = options.filterSettings || {};
+               },
+
+               /**
+                * Render the filters menu.
+                */
+               render: function() {
+                       this.$el.html( this.template( this.filterSettings ) );
+                       return this;
+               },
+
+               /**
+                * Close the filter screen.
+                */
+               close: function() {
+                       this.remove();
+               },
+
+               /**
+                * Toggle items in the attendance status list.
+                */
+               toggleAttendance: function( event ) {
+                       var selection = $( event.target ).data( 'attendance' );
+                       this.filterSettings.attendance = selection;
+                       this.render();
+
+                       this.controller.trigger( 'filter', this.filterSettings );
+               },
+
+               /**
+                * Toggle items in the tickets list.
+                */
+               toggleTickets: function( event ) {
+                       var ticket_id = $( event.target ).data( 'ticket-id' );
+
+                       // Remove or append the ticket_id to the filter settings.
+                       if ( _.contains( this.filterSettings.tickets, ticket_id ) ) {
+                               this.filterSettings.tickets = _.without( this.filterSettings.tickets, ticket_id );
+                       } else {
+                               this.filterSettings.tickets.push( ticket_id );
+                       }
+
+                       this.render();
+                       this.controller.trigger( 'filter', this.filterSettings );
+               },
+       });
+
+       /**
+        * Main Application View and controller.
+        */
+       camptix.views.Application = Backbone.View.extend({
+               template: wp.template( 'application' ),
+
+               /**
+                * Main Application events/controls.
+                */
+               events: {
+                       'fastClick .dashicons-menu': 'menu',
+                       'fastClick .submenu .search': 'searchView',
+                       'fastClick .submenu .refresh': 'refresh',
+                       'fastClick .submenu .filter': 'filterView'
+               },
+
+               /**
+                * Initialize the application.
+                */
+               initialize: function() {
+                       this.cache = [];
+                       this.query = {};
+                       this.requests = [];
+                       this.lastScroll = 0;
+
+                       this.filterSettings = {
+                               'attendance': 'none',
+                               'tickets': _camptixAttendanceTickets,
+                               'search': ''
+                       };
+
+                       this.render();
+
+                       this.$header = this.$el.find( 'header' );
+                       this.$menu = this.$header.find( '.menu' );
+
+                       this.scroll = _.chain( this.scroll ).bind( this ).value();
+                       this.$list = this.$el.find( '.attendees-list' );
+                       this.$list.on( 'scroll', this.scroll );
+                       this.$loading = this.$list.find( '.loading' );
+
+                       this.on( 'search', this.search, this );
+                       this.on( 'flush', this.flush, this );
+                       this.on( 'more:toggle', this.moreToggle, this );
+                       this.on( 'filter', this.filter, this );
+
+                       this.setupCollection();
+               },
+
+               /**
+                * Runs when hasMore is toggled in the current collection.
+                */
+               moreToggle: function( hasMore ) {
+                       this.$loading.toggle( hasMore );
+               },
+
+               /**
+                * Setup a collection (or retrieve one from cache)
+                */
+               setupCollection: function( query ) {
+                       var collection,
+                               options = {};
+
+                       // Dispose of the current collection and cache it for later use.
+                       if ( 'undefined' != typeof this.collection ) {
+                               this.collection.off( null, null, this );
+                               this.cache.push( this.collection );
+                       }
+
+                       query = _.defaults( query || {}, {
+                               search: '',
+                               filters: _.clone( this.filterSettings )
+                       });
+
+                       options.query = query;
+                       options.controller = this;
+
+                       collection = _.find( this.cache, function( collection ) {
+                               return _.isEqual( collection.query, options.query );
+                       } );
+
+                       if ( ! collection ) {
+                               collection = new camptix.collections.AttendeesList( [], options );
+                       }
+
+                       this.query = query;
+                       this.collection = collection;
+                       this.collection.on( 'add', this.add, this );
+                       this.collection.on( 'reset', this.reset, this );
+
+                       // Clear the list before adding things back.
+                       this.$list.find( 'li.item' ).remove();
+
+                       if ( this.collection.length ) {
+                               this.collection.trigger( 'reset' );
+                       } else {
+                               this.collection.more().done( this.scroll );
+                       }
+
+                       this.trigger( 'more:toggle', collection.hasMore() );
+               },
+
+               /**
+                * Scroll event handler.
+                */
+               scroll: function() {
+                       var view = this,
+                               el = this.$list[0];
+
+                       this.lastScroll = +new Date();
+
+                       if ( ! this.collection.hasMore() )
+                               return;
+
+                       if ( el.scrollHeight < el.scrollTop + ( el.clientHeight * 3 ) ) {
+                               this.collection.more().done(function() {
+                                       view.scroll();
+                               });
+                       }
+               },
+
+               /**
+                * Render the application view.
+                */
+               render: function() {
+                       this.$el.html( this.template() );
+                       $(document.body).append( this.el );
+                       return this;
+               },
+
+               /**
+                * Append a single AttendeeView item (from a model) to the list.
+                */
+               add: function( item ) {
+                       var view = new camptix.views.AttendeeView({ model: item, controller: this });
+                       this.$loading.before( view.render().el );
+               },
+
+               /**
+                * A collection is reset. Make sure everything is added back to the view.
+                */
+               reset: function() {
+                       this.collection.each( this.add, this );
+               },
+
+               /**
+                * Toggle nav menu.
+                */
+               menu: function( event ) {
+                       this.$menu.toggleClass( 'dropdown' );
+               },
+
+               /**
+                * Show the Search view.
+                */
+               searchView: function() {
+                       this.$menu.removeClass( 'dropdown' );
+                       this.searchView = new camptix.views.AttendeeSearchView({ controller: this });
+                       this.$header.append( this.searchView.render().el );
+
+                       this.searchView.$el.find('input').focus();
+                       return false;
+               },
+
+               /**
+                * Show the Filter Settings view.
+                */
+               filterView: function() {
+                       this.$menu.removeClass( 'dropdown' );
+                       this.filterView = new camptix.views.AttendeeFilterView({ controller: this, filterSettings: this.filterSettings });
+                       this.$el.append( this.filterView.render().el );
+                       return false;
+               },
+
+               /**
+                * Remove everything from the list, flush all caches
+                * and setup a new collection with the current settings.
+                */
+               refresh: function() {
+                       this.$menu.removeClass( 'dropdown' );
+                       delete this.collection;
+                       this.flush();
+                       this.setupCollection();
+                       return false;
+               },
+
+               /**
+                * Re-initialize a calloction with a search term.
+                */
+               search: function( keyword ) {
+                       this.keyword = this.keyword || '';
+                       if ( keyword == this.keyword )
+                               return;
+
+                       this.keyword = keyword;
+                       this.setupCollection({ search: this.keyword });
+               },
+
+               /**
+                * Re-initialize a collection with (possibly) new filter settings.
+                */
+               filter: function( settings ) {
+                       this.filterSettings = settings;
+                       delete this.collection;
+                       this.flush();
+                       this.setupCollection();
+               },
+
+               /**
+                * Remove all queries from cache.
+                */
+               flush: function() {
+                       this.cache = [];
+               }
+       });
+
+       // Initialize application.
+       camptix.app = new camptix.views.Application();
+});
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendanceaddonsassetsattendanceuiscss"></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/camptix-attendance/addons/assets/attendance-ui.scss</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/camptix-attendance/addons/assets/attendance-ui.scss                               (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/assets/attendance-ui.scss 2014-10-21 15:23:09 UTC (rev 919)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,461 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+@import url(http://fonts.googleapis.com/css?family=Open+Sans:400,700);
+
+body {
+       background: #ccc;
+       font-family: 'Open Sans', Helvetica, sans-serif;
+}
+
+.overlay {
+       display: none;
+       position: absolute;
+       background: black;
+       opacity: 0.8;
+       top: 0;
+       bottom: 0;
+       left: 0;
+       right: 0;
+
+       z-index: 200;
+}
+
+header {
+       display: block;
+       position: absolute;
+       background: #333;
+       height: 50px;
+       top: 0;
+       left: 0;
+       right: 0;
+
+       color: white;
+       padding-left: 16px;
+
+       z-index: 100;
+}
+
+header h1,
+.attendee-filter-view h1 {
+       display: block;
+       clear: none;
+       line-height: 50px;
+       margin: 0;
+       font-size: 16px;
+       white-space: nowrap;
+       overflow: hidden;
+       text-overflow: ellipsis;
+       margin-right: 100px;
+       color: white;
+}
+
+.menu {
+       float: right;
+       height: 50px;
+
+       .dashicons {
+               color: white;
+               float: left;
+               width: 50px;
+               height: 50px;
+               font-size: 28px;
+               line-height: 50px;
+               text-align: center;
+       }
+}
+
+.loading {
+       line-height: 50px;
+       color: #999;
+
+       .spinner-container {
+               width: 50px;
+               height: 50px;
+               float: left;
+               overflow: hidden;
+               text-align: center;
+       }
+
+       .spinner {
+               display: inline-block;
+               margin-top: 14px;
+
+               border-top: 5px solid rgba(200, 200, 200, 0.3);
+               border-right: 5px solid rgba(200, 200, 200, 0.3);
+               border-bottom: 5px solid rgba(200, 200, 200, 0.3);
+               border-left: 5px solid #ccc;
+
+               -webkit-animation: load8 1.1s infinite linear;
+               animation: load8 1.1s infinite linear;
+       }
+
+       .spinner,
+       .spinner:after {
+               border-radius: 50%;
+               width: 12px;
+               height: 12px;
+       }
+}
+
+li.item {
+       .spinner-container {
+               display: none;
+               width: 50px;
+               height: 50px;
+               background: white;
+               overflow: hidden;
+               position: absolute;
+               left: 0;
+       }
+
+       &.camptix-loading .spinner-container {
+               display: block;
+       }
+
+       .spinner,
+       .spinner:before,
+       .spinner:after {
+               display: block;
+               background: #ccc;
+               -webkit-animation: load1 1s infinite ease-in-out;
+               animation: load1 1s infinite ease-in-out;
+               width: 5px;
+               height: 10px;
+       }
+
+       .spinner:before,
+       .spinner:after {
+               position: absolute;
+               top: 0;
+               content: '';
+       }
+
+       .spinner:before {
+               left: 7px;
+       }
+
+       .spinner {
+               margin-left: 22px;
+               margin-top: 20px;
+
+               position: relative;
+               font-size: 11px;
+               -webkit-animation-delay: -0.16s;
+               animation-delay: -0.16s;
+       }
+
+       .spinner:after {
+               left: -7px;
+               -webkit-animation-delay: -0.32s;
+               animation-delay: -0.32s;
+       }
+}
+
+.menu .submenu {
+       display: none;
+       position: absolute;
+       top: 50px;
+       right: 0;
+       background: #444;
+       min-height: 50px;
+
+       a {
+               height: 51px;
+               line-height: 50px;
+               text-decoration: none;
+               color: white;
+               padding: 0 20px;
+               display: block;
+
+               min-width: 150px;
+               max-width: 200px;
+
+               overflow: hidden;
+               white-space: nowrap;
+               text-overflow: ellipsis;
+       }
+}
+
+.menu.dropdown .dashicons-menu {
+       background: #444;
+}
+
+.menu.dropdown .submenu {
+       display: block;
+}
+
+.attendee-search-view,
+.attendee-filter-view {
+       position: absolute;
+       top: 0;
+       right: 0;
+       left: 0;
+       height: 50px;
+       background: #444;
+       color: white;
+
+       .wrapper:before {
+               content: "\f179";
+               font-family: 'dashicons';
+               -webkit-font-smoothing: antialiased;
+               font-size: 28px;
+               height: 50px;
+               width: 50px;
+               left: 0;
+               position: absolute;
+               line-height: 50px;
+               text-align: center;
+       }
+
+       .close {
+               float: right;
+               color: white;
+               width: 50px;
+               height: 50px;
+               font-size: 28px;
+               line-height: 50px;
+               text-align: center;
+       }
+}
+
+.attendee-search-view .wrapper {
+       height: 50px;
+       display: block;
+       margin-right: 50px;
+       margin-left: 50px;
+}
+
+.attendee-search-view input {
+       border: 0;
+       font-size: 16px;
+       font-family: 'Open Sans', Helvetica, sans-serif;
+       height: 35px;
+       margin-top: 7px;
+       padding: 0 8px;
+       width: 100%;
+
+       margin-left: -4px;
+}
+
+.attendee-filter-view {
+       z-index: 500;
+       height: auto;
+       bottom: 0;
+       overflow: scroll;
+
+       .wrapper:before {
+               content: "\f180";
+       }
+
+       h1 {
+               padding-left: 50px;
+       }
+
+       h1.section-title {
+               padding-left: 18px;
+               border-bottom: solid 1px #555;
+               margin: 0;
+       }
+
+       ul.section-controls {
+               list-style: none;
+               margin: 0;
+               padding: 0;
+
+               li {
+                       height: 50px;
+                       line-height: 50px;
+                       margin: 0;
+                       border-bottom: solid 1px #555;
+                       padding-left: 50px;
+                       padding-right: 50px;
+                       cursor: pointer;
+
+                       white-space: nowrap;
+                       overflow: hidden;
+                       text-overflow: ellipsis;
+               }
+
+               li.selected:before {
+                       content: "\f147";
+                       font-family: 'dashicons';
+                       -webkit-font-smoothing: antialiased;
+                       font-size: 28px;
+                       height: 50px;
+                       width: 50px;
+                       left: 0;
+                       position: absolute;
+                       line-height: 50px;
+                       text-align: center;
+               }
+       }
+}
+
+.attendees-list {
+       margin: 0;
+       padding: 0;
+       background: white;
+       position: absolute;
+       top: 50px;
+       bottom: 0;
+       left: 0;
+       right: 0;
+       overflow-x: hidden;
+       overflow-y: scroll;
+       -webkit-overflow-scrolling: touch;
+
+       li {
+               display: block;
+               float: left;
+               width: 100%;
+               height: 50px;
+               border-bottom: solid 1px #ddd;
+
+               -webkit-tap-highlight-color: rgba( 0,0,0,0 );
+
+               &.item {
+                       cursor: pointer;
+               }
+       }
+
+       .toggle {
+               display: block;
+               float: left;
+               width: 50px;
+               height: 50px;
+
+               -webkit-tap-highlight-color: rgba( 0,0,0,0 );
+
+               .dashicons {
+                       font-size: 24px;
+                       line-height: 50px;
+                       height: 50px;
+                       width: 50px;
+                       text-align: center;
+                       text-decoration: none;
+                       color: #ddd;
+               }
+
+               &.yes .dashicons {
+                       color: #0acc00;
+               }
+       }
+
+       .name {
+               display: block;
+               margin-left: 50px;
+               white-space: nowrap;
+               text-overflow: ellipsis;
+               overflow: hidden;
+               height: 50px;
+               line-height: 50px;
+               font-size: 16px;
+               padding-left: 2px;
+               padding-right: 16px;
+       }
+}
+
+.attendee-toggle-wrap {
+       margin: 0;
+       padding: 0;
+       background: white;
+       position: absolute;
+       top: 20px;
+       bottom: 20px;
+       left: 20px;
+       right: 20px;
+       overflow: scroll;
+       padding: 50px 40px;
+       text-align: center;
+
+       z-index: 300;
+
+       .yes-no-container {
+               height: 50px;
+               display: block;
+               width: 200px;
+               margin: 40px auto 0 auto;
+       }
+
+       a.yes,
+       a.no {
+               height: 50px;
+               display: block;
+               float: left;
+               width: 50%;
+               line-height: 50px;
+               font-size: 16px;
+               background: #333;
+               color: white;
+               text-decoration: none;
+       }
+
+       a.yes {
+               background: #0acc00;
+       }
+
+       .close {
+               font-size: 24px;
+               line-height: 50px;
+               height: 50px;
+               width: 50px;
+               text-align: center;
+               text-decoration: none;
+               color: #444;
+               position: absolute;
+               top: 0;
+               right: 0;
+       }
+
+       img {
+               background: #ccc;
+               width: 150px;
+               height: 150px;
+       }
+}
+
+/** Animations, courtesy of https://github.com/lukehaas/css-loaders **/
+
+@-webkit-keyframes load1 {
+  0%,
+  80%,
+  100% {
+    box-shadow: 0 0 #ccc;
+    height: 10px;
+  }
+  40% {
+    box-shadow: 0 -5px #ccc;
+    height: 18px;
+  }
+}
+@keyframes load1 {
+  0%,
+  80%,
+  100% {
+    box-shadow: 0 0 #ccc;
+    height: 10px;
+  }
+  40% {
+    box-shadow: 0 -5px #ccc;
+    height: 18px;
+  }
+}
+
+@-webkit-keyframes load8 {
+  0% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+  100% {
+    -webkit-transform: rotate(360deg);
+    transform: rotate(360deg);
+  }
+}
+@keyframes load8 {
+  0% {
+    -webkit-transform: rotate(0deg);
+    transform: rotate(0deg);
+  }
+  100% {
+    -webkit-transform: rotate(360deg);
+    transform: rotate(360deg);
+  }
+}
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendanceaddonsassetsjqueryfastbuttonjs"></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/camptix-attendance/addons/assets/jquery.fastbutton.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/camptix-attendance/addons/assets/jquery.fastbutton.js                             (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/assets/jquery.fastbutton.js       2014-10-21 15:23:09 UTC (rev 919)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,154 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+(function() {
+  /** 
+   * From: http://code.this.com/mobile/articles/fast_buttons.html
+   * Also see: http://stackoverflow.com/questions/6300136/trying-to-implement-googles-fast-button 
+   */
+   
+  /** For IE8 and earlier compatibility: https://developer.mozilla.org/en/DOM/element.addEventListener */
+  function addListener(el, type, listener, useCapture) {
+    if (el.addEventListener) { 
+      el.addEventListener(type, listener, useCapture);
+      return { 
+        destroy: function() { el.removeEventListener(type, listener, useCapture); } 
+      };
+    } else {      
+      // see: http://stackoverflow.com/questions/5198845/javascript-this-losing-context-in-ie
+      var handler = function(e) { listener.handleEvent(window.event, listener); }
+      el.attachEvent('on' + type, handler);
+      
+      return { 
+        destroy: function() { el.detachEvent('on' + type, handler); }
+      };
+    }   
+  }
+  
+  var isTouch = "ontouchstart" in window;
+
+  /* Construct the FastButton with a reference to the element and click handler. */
+  this.FastButton = function(element, handler, useCapture) {
+    // collect functions to call to cleanup events 
+    this.events = [];
+    this.touchEvents = [];
+    this.element = element;
+    this.handler = handler;
+    this.useCapture = useCapture;
+    if (isTouch) 
+      this.events.push(addListener(element, 'touchstart', this, this.useCapture));    
+    this.events.push(addListener(element, 'click', this, this.useCapture));
+  };
+  
+  /* Remove event handling when no longer needed for this button */
+  this.FastButton.prototype.destroy = function() {
+    for (i = this.events.length - 1; i >= 0; i -= 1)
+      this.events[i].destroy();    
+    this.events = this.touchEvents = this.element = this.handler = this.fastButton = null;
+  };
+  
+  /* acts as an event dispatcher */
+  this.FastButton.prototype.handleEvent = function(event) {
+    switch (event.type) {
+      case 'touchstart': this.onTouchStart(event); break;
+      case 'touchmove': this.onTouchMove(event); break;
+      case 'touchend': this.onClick(event); break;
+      case 'click': this.onClick(event); break;
+    }
+  };
+  
+  /* Save a reference to the touchstart coordinate and start listening to touchmove and
+   touchend events. Calling stopPropagation guarantees that other behaviors don’t get a
+   chance to handle the same click event. This is executed at the beginning of touch. */
+  this.FastButton.prototype.onTouchStart = function(event) {
+    event.stopPropagation ? event.stopPropagation() : (event.cancelBubble=true);
+    this.touchEvents.push(addListener(this.element, 'touchend', this, this.useCapture));
+    this.touchEvents.push(addListener(document.body, 'touchmove', this, this.useCapture));
+    this.startX = event.touches[0].clientX;
+    this.startY = event.touches[0].clientY;
+  };
+  
+  /* When /if touchmove event is invoked, check if the user has dragged past the threshold of 10px. */
+  this.FastButton.prototype.onTouchMove = function(event) {
+    if (Math.abs(event.touches[0].clientX - this.startX) > 10 || Math.abs(event.touches[0].clientY - this.startY) > 10) {
+      this.reset(); //if he did, then cancel the touch event
+    }
+  };
+  
+  /* Invoke the actual click handler and prevent ghost clicks if this was a touchend event. */
+  this.FastButton.prototype.onClick = function(event) {
+    event.stopPropagation ? event.stopPropagation() : (event.cancelBubble=true);
+    this.reset();
+    // Use .call to call the method so that we have the correct "this": https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Function/call
+    var result = this.handler.call(this.element, event);
+    if (event.type == 'touchend') 
+      clickbuster.preventGhostClick(this.startX, this.startY);    
+    return result;
+  };
+  
+  this.FastButton.prototype.reset = function() {
+    for (i = this.touchEvents.length - 1; i >= 0; i -= 1) 
+      this.touchEvents[i].destroy();    
+    this.touchEvents = [];
+  };
+  
+  this.clickbuster = function() {}
+  
+  /* Call preventGhostClick to bust all click events that happen within 25px of
+   the provided x, y coordinates in the next 2.5s. */
+  this.clickbuster.preventGhostClick = function(x, y) {
+    clickbuster.coordinates.push(x, y);
+    window.setTimeout(clickbuster.pop, 2500);
+  };
+  
+  this.clickbuster.pop = function() {
+    clickbuster.coordinates.splice(0, 2);
+  };
+  
+  /* If we catch a click event inside the given radius and time threshold then we call
+   stopPropagation and preventDefault. Calling preventDefault will stop links
+   from being activated. */
+  this.clickbuster.onClick = function(event) {
+    for (var i = 0; i < clickbuster.coordinates.length; i += 2) {
+      var x = clickbuster.coordinates[i];
+      var y = clickbuster.coordinates[i + 1];
+      if (Math.abs(event.clientX - x) < 25 && Math.abs(event.clientY - y) < 25) {
+        event.stopPropagation ? event.stopPropagation() : (event.cancelBubble=true);
+        event.preventDefault ? event.preventDefault() : (event.returnValue=false);
+      }
+    }
+  };
+    
+  if (isTouch) {
+    // Don't need to use our custom addListener function since we only bust clicks on touch devices
+    document.addEventListener('click', clickbuster.onClick, true);
+    clickbuster.coordinates = [];
+  }
+})(this);
+
+(function($) {
+  $.event.special.fastClick = {
+               setup: function () {
+                   $(this).data('fastClick', new FastButton(this, $.event.special.fastClick.handler));
+               },
+               teardown: function () {
+                  $(this).data('fastClick').destroy();
+                  $(this).removeData('fastClick');
+               },
+               handler: function (e) {
+                       // convert native event to jquery event
+                       e = $.event.fix(e);
+                       e.type = 'fastClick';
+               
+                       /*
+                       event.handle is deprecated and removed as of version 1.9
+                       use event.dispatch instead,
+                       $.event.handle.apply(this, arguments);
+                       */
+                       $.event.dispatch.apply(this, arguments);
+               }
+       };
+
+       $.fn.fastClick = function(fn) {
+               return $(this).each(function() {
+                       return fn ? $(this).bind("fastClick", fn) : $(this).trigger("fastClick");
+               });
+       };
+}(jQuery));
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendanceaddonsattendanceuiphp"></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/camptix-attendance/addons/attendance-ui.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/camptix-attendance/addons/attendance-ui.php                               (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/attendance-ui.php 2014-10-21 15:23:09 UTC (rev 919)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,98 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Something here
+ */
+
+global $camptix, $wp_scripts, $wp_styles;
+
+$camptix_tickets = $camptix->tmp( 'attendance_tickets' );
+$camptix_options = $camptix->get_options();
+?>
+<html>
+<head>
+       <title><?php printf( __( '%s Attendance', 'camptix' ), esc_html( $camptix_options['event_name'] ) ); ?></title>
+
+       <?php $wp_scripts->do_items( array( 'camptix-attendance-ui' ) ); ?>
+       <?php $wp_styles->do_items( array( 'camptix-attendance-ui' ) ); ?>
+       <script>
+               _camptixAttendanceSecret = '<?php echo esc_js( $_GET['camptix-attendance'] ); ?>';
+               _camptixAttendanceTickets = [ <?php echo esc_js( implode( ', ', array_map( 'absint', wp_list_pluck( $camptix_tickets, 'ID' ) ) ) ); ?> ];
+       </script>
+
+       <meta name="viewport" content="width=device-width; initial-scale=1.0; maximum-scale=1.0; user-scalable=0;" />
+       <meta name="referrer" content="never" />
+</head>
+<body>
+       <script id="tmpl-attendee" type="text/template">
+               <div class="spinner-container"><span class="spinner"></span></div>
+               <a href="#" class="status toggle <# if ( data.status ) { #> yes <# } #>"><div class="dashicons dashicons-admin-users"></div></a>
+               <span class="name">
+                       {{ data.name }}
+               </span>
+       </script>
+
+       <script id="tmpl-attendee-toggle" type="text/template">
+               <img src="{{ data.avatar }}" />
+               <p>Did <strong>{{ data.name }}</strong> attend <?php echo esc_html( $camptix_options['event_name'] ); ?>?</p>
+
+               <div class="yes-no-container">
+                       <a href="#" class="yes">Yes</a>
+                       <a href="#" class="no">No</a>
+               </div>
+
+               <a href="#" class="close dashicons dashicons-no"></a>
+       </script>
+
+       <script id="tmpl-application" type="text/template">
+               <div class="overlay"></div>
+
+               <header>
+                       <div class="menu">
+                               <a href="#" class="dashicons dashicons-menu"></a>
+                               <div class="submenu">
+                                       <a href="#" class="search">Search</a>
+                                       <a href="#" class="filter">Filter</a>
+                                       <a href="#" class="refresh">Refresh</a>
+                               </div>
+                       </div>
+                       <h1><?php echo esc_html( $camptix_options['event_name'] ); ?></h1>
+               </header>
+
+               <div id="attendees-list-wrapper">
+                       <ul class="attendees-list">
+                               <li class="loading">
+                                       <div class="spinner-container"><span class="spinner"></span></div>
+                                       <span>Loading...</span>
+                               </li>
+                       </ul>
+               </div>
+       </script>
+
+       <script id="tmpl-attendee-search" type="text/template">
+               <a href="#" class="close dashicons dashicons-no"></a>
+               <div class="wrapper">
+                       <input type="text" autocomplete="off" placeholder="Search" />
+               </div>
+       </script>
+
+       <script id="tmpl-attendee-filter" type="text/template">
+               <a href="#" class="close dashicons dashicons-no"></a>
+               <div class="wrapper">
+                       <h1>Filters</h1>
+
+                       <h1 class="section-title">Attendance</h1>
+                       <ul class="filter-attendance section-controls">
+                               <li data-attendance="none" <# if ( data.attendance == 'none' ) { #> class="selected" <# } #> >All</li>
+                               <li data-attendance="attending" <# if ( data.attendance == 'attending' ) { #> class="selected" <# } #> >Attending</li>
+                               <li data-attendance="not-attending" <# if ( data.attendance == 'not-attending' ) { #> class="selected" <# } #> >Not Attending</li>
+                       </ul>
+
+                       <h1 class="section-title">Tickets</h1>
+                       <ul class="filter-tickets section-controls">
+                               <?php foreach ( $camptix_tickets as $ticket ) : ?>
+                               <li data-ticket-id="<?php echo absint( $ticket->ID ); ?>" <# if ( _.contains( data.tickets, <?php echo absint( $ticket->ID ); ?> ) ) { #> class="selected" <# } #> ><?php echo esc_html( $ticket->post_title ); ?></li>
+                               <?php endforeach; ?>
+                       </ul>
+               </div>
+       </script>
+</body>
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendanceaddonsattendancephp"></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/camptix-attendance/addons/attendance.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/camptix-attendance/addons/attendance.php                          (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/addons/attendance.php    2014-10-21 15:23:09 UTC (rev 919)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,349 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Allows event organizers to track which attendees showed up to the event.
+ */
+class CampTix_Attendance extends CampTix_Addon {
+       /**
+        * Runs during CampTix init.
+        */
+       public function camptix_init() {
+               global $camptix;
+
+               // Admin Settings UI.
+               if ( current_user_can( $camptix->caps['manage_options'] ) ) {
+                       add_filter( 'camptix_setup_sections', array( $this, 'setup_sections' ) );
+                       add_action( 'camptix_menu_setup_controls', array( $this, 'setup_controls' ), 10, 1 );
+                       add_filter( 'camptix_validate_options', array( $this, 'validate_options' ), 10, 2 );
+               }
+
+               $camptix_options = $camptix->get_options();
+               if ( empty( $camptix_options['attendance-secret'] ) )
+                       return;
+
+               $this->secret = $camptix_options['attendance-secret'];
+
+               if ( empty( $camptix_options['attendance-enabled'] ) )
+                       return;
+
+               add_filter( 'wp_ajax_camptix-attendance', array( $this, 'ajax_callback' ) );
+               add_filter( 'wp_ajax_nopriv_camptix-attendance', array( $this, 'ajax_callback' ) );
+
+               if ( ! empty( $_GET['camptix-attendance'] ) && $_GET['camptix-attendance'] == $this->secret ) {
+                       add_filter( 'template_include', array( $this, 'setup_attendance_ui' ) );
+               }
+       }
+
+       /**
+        * Initialize the Attendance UI.
+        *
+        * Enqueue all necessary scripts and styles, pass any needed data
+        * via $camptix->tmp(). Note that previously enqueued scripts and
+        * styles will not be loaded.
+        */
+       public function setup_attendance_ui( $template ) {
+               global $camptix;
+
+               wp_enqueue_script( 'jquery-fastbutton', plugins_url( '/assets/jquery.fastbutton.js', __FILE__ ), array( 'jquery' ) );
+               wp_enqueue_script( 'camptix-attendance-ui', plugins_url( '/assets/attendance-ui.js' , __FILE__ ), array( 'backbone', 'jquery', 'wp-util', 'jquery-fastbutton' ) );
+               wp_enqueue_style( 'camptix-attendance-ui', plugins_url( '/assets/attendance-ui.css', __FILE__ ), array( 'dashicons' ) );
+
+               $camptix->tmp( 'attendance_tickets', $this->get_tickets() );
+               return dirname( __FILE__ ) . '/attendance-ui.php';
+       }
+
+       /**
+        * Callback/router for an AJAX Request.
+        *
+        * Routes to the appropriate callback method depending
+        * on the requested CampTix action. Also validates keys.
+        */
+       public function ajax_callback() {
+               if ( empty( $_REQUEST['camptix_secret'] ) || $_REQUEST['camptix_secret'] != $this->secret )
+                       return;
+
+               $action = $_REQUEST['camptix_action'];
+               if ( 'sync-model' == $action ) {
+                       return $this->_ajax_sync_model();
+               } elseif ( 'sync-list' == $action ) {
+                       return $this->_ajax_sync_list();
+               }
+       }
+
+       /**
+        * Synchronize an attendee model.
+        *
+        * Sets are removes the attended flag for a given camptix_id.
+        */
+       public function _ajax_sync_model() {
+               if ( empty( $_REQUEST['camptix_id'] ) )
+                       return;
+
+               $attendee_id = absint( $_REQUEST['camptix_id'] );
+               $attendee = get_post( $attendee_id );
+
+               if ( ! $attendee || 'tix_attendee' != $attendee->post_type || 'publish' != $attendee->post_status )
+                       return;
+
+               if ( isset( $_REQUEST['camptix_set_attendance'] ) ) {
+                       if ( 'true' == $_REQUEST['camptix_set_attendance'] ) {
+                               $this->log( 'Marked attendee as attended.', $attendee->ID );
+                               update_post_meta( $attendee->ID, 'tix_attended', true );
+                       } else {
+                               $this->log( 'Marked attendee as did not attended.', $attendee->ID );
+                               delete_post_meta( $attendee->ID, 'tix_attended' );
+                       }
+               }
+
+               return wp_send_json_success( array( $this->_make_object( $attendee ) ) );
+       }
+
+       /**
+        * Synchronize an attendee list.
+        *
+        * Queries the database for attendees given a query and
+        * returns a batch back to Backbone.sync.
+        */
+       public function _ajax_sync_list() {
+               global $wpdb;
+
+               $paged = 1;
+               if ( ! empty( $_REQUEST['camptix_paged'] ) )
+                       $paged = absint( $_REQUEST['camptix_paged'] );
+
+               $ticket_ids = wp_list_pluck( $this->get_tickets(), 'ID' );
+
+               $query_args = array(
+                       'post_type' => 'tix_attendee',
+                       'post_status' => 'publish',
+                       'orderby' => 'title',
+                       'order' => 'ASC',
+                       'paged' => $paged,
+                       'posts_per_page' => 50,
+                       'meta_query' => '',
+               );
+
+               $filters = array();
+               if ( ! empty( $_REQUEST['camptix_filters'] ) )
+                       $filters = (array) $_REQUEST['camptix_filters'];
+
+               $filters = wp_parse_args( (array) $_REQUEST['camptix_filters'], array(
+                       'attendance' => 'none',
+                       'tickets' => array(),
+               ) );
+
+               $filters['search'] = ! empty( $_REQUEST['camptix_search'] ) ? $_REQUEST['camptix_search'] : '';
+
+               // Filter by attendance.
+               if ( in_array( $filters['attendance'], array( 'attending', 'not-attending' ) ) )
+                       $this->_filter_query_attendance( $filters['attendance'] );
+
+               // Filter by ticket type.
+               $filters['tickets'] = array_intersect( $filters['tickets'], $ticket_ids );
+               if ( count( array_diff( $ticket_ids, $filters['tickets'] ) ) > 0 ) {
+
+                       // No tickets selected.
+                       if ( empty( $filters['tickets'] ) )
+                               return wp_send_json_success( array() );
+
+                       $this->_filter_query_tickets( $filters['tickets'] );
+               }
+
+               // Filter by search query.
+               if ( ! empty( $filters['search'] ) )
+                       $this->_filter_query_search( $filters['search'] );
+
+               $query_args['suppress_filters'] = false;
+               $attendees = get_posts( $query_args );
+
+               $output = array();
+               foreach ( $attendees as $attendee ) {
+                       $output[] = $this->_make_object( $attendee );
+               }
+
+               return wp_send_json_success( $output );
+       }
+
+       /**
+        * Helper method to make an Attendee object.
+        *
+        * Use this helper to return only the necessary data back
+        * with an AJAX method.
+        */
+       public function _make_object( $attendee ) {
+               $attendee = get_post( $attendee );
+
+               $first_name = get_post_meta( $attendee->ID, 'tix_first_name', true );
+               $last_name = get_post_meta( $attendee->ID, 'tix_last_name', true );
+               $avatar_url = sprintf( 'https://secure.gravatar.com/avatar/%s?s=160', md5( get_post_meta( $attendee->ID, 'tix_email', true ) ) );
+               $avatar_url = add_query_arg( 'd', 'https://secure.gravatar.com/avatar/ad516503a11cd5ca435acc9bb6523536?s=160', $avatar_url );
+
+               $status = (bool) get_post_meta( $attendee->ID, 'tix_attended', true );
+
+               return array(
+                       'id' => $attendee->ID,
+                       'name' => sprintf( '%s %s', $first_name, $last_name ),
+                       'avatar' => esc_url_raw( $avatar_url ),
+                       'status' => $status,
+               );
+       }
+
+       /**
+        * Filter the SQL in WP_Query for Search.
+        *
+        * Prior to 4.1 WordPress didn't have nested meta queries, so
+        * we're left with our own JOINs and WHEREs to look for a search
+        * query under various meta keys.
+        */
+       public function _filter_query_search( $search ) {
+               add_filter( 'posts_clauses', function( $clauses ) use ( $search ) {
+                       global $wpdb;
+
+                       $search = $wpdb->esc_like( $search );
+
+                       $clauses['join'] .= "
+                               INNER JOIN $wpdb->postmeta tix_first_name ON ( ID = tix_first_name.post_id AND tix_first_name.meta_key = 'tix_first_name' )
+                               INNER JOIN $wpdb->postmeta tix_last_name ON ( ID = tix_last_name.post_id AND tix_last_name.meta_key = 'tix_last_name' )
+                       ";
+
+                       $clauses['where'] .= $wpdb->prepare( "
+                               AND ( tix_first_name.meta_value LIKE '%%%s%%' OR tix_last_name.meta_value LIKE '%%%s%%' )
+                       ", $search, $search );
+
+                       return $clauses;
+               } );
+       }
+
+       /**
+        * Filter WP_Query to include only specific tickets.
+        */
+       public function _filter_query_tickets( $ticket_ids ) {
+               add_filter( 'posts_clauses', function( $clauses ) use ( $ticket_ids ) {
+                       global $wpdb;
+
+                       $clauses['join'] .= " INNER JOIN $wpdb->postmeta tix_ticket_id ON ( ID = tix_ticket_id.post_id AND tix_ticket_id.meta_key = 'tix_ticket_id' ) ";
+                       $clauses['where'] .= sprintf( " AND ( tix_ticket_id.meta_value IN ( %s ) ) ", implode( ', ', array_map( 'absint', $ticket_ids ) ) );
+                       return $clauses;
+               } );
+       }
+
+       /**
+        * Filter WP_Query to include only attending or non-attending attendees.
+        */
+       public function _filter_query_attendance( $attendance ) {
+               add_filter( 'posts_clauses', function( $clauses ) use ( $attendance ) {
+                       global $wpdb;
+
+                       $clauses['join'] .= " LEFT JOIN $wpdb->postmeta tix_attended ON ( ID = tix_attended.post_id AND tix_attended.meta_key = 'tix_attended' ) ";
+
+                       if ( 'attending' == $attendance )
+                               $clauses['where'] .=  " AND ( tix_attended.meta_value = 1 ) ";
+                       else
+                               $clauses['where'] .= " AND ( tix_attended.meta_value IS NULL ) ";
+
+                       return $clauses;
+               } );
+       }
+
+       /**
+        * Add a new section to the Setup screen.
+        */
+       public function setup_sections( $sections ) {
+               $sections['attendance-ui'] = __( 'Attendance UI', 'camptix' );
+               return $sections;
+       }
+
+       /**
+        * Add some controls to our Setup section.
+        */
+       public function setup_controls( $section ) {
+               global $camptix;
+
+               if ( 'attendance-ui' != $section )
+                       return;
+
+               add_settings_section( 'general', __( 'Attendance UI', 'camptix' ), array( $this, 'setup_controls_section' ), 'camptix_options' );
+
+               // Fields
+               $camptix->add_settings_field_helper( 'attendance-enabled', __( 'Enabled', 'camptix' ), 'field_yesno', 'general',
+                       __( "Don't forget to disable the UI after the event is over.", 'camptix' )
+               );
+
+               add_settings_field( 'attendance-secret', __( 'Secret Link' ), array( $this, 'field_secret' ), 'camptix_options', 'general' );
+       }
+
+       /**
+        * Secret Link Field
+        *
+        * This is a field that only shows the secret URL, and also has
+        * a "generate" checkbox that allows users to generate a new secret.
+        */
+       public function field_secret() {
+               $secret_url = ! empty( $this->secret ) ? add_query_arg( 'camptix-attendance', $this->secret, home_url() ) : '';
+               ?>
+               <input type="hidden" name="camptix_options[attendance-secret]" value="1" />
+               <textarea class="large-text" rows="4" disabled="disabled"><?php echo esc_textarea( $secret_url ); ?></textarea>
+
+               <input id="camptix-attendance-generate" type="checkbox" name="camptix_options[attendance-generate]" value="1" />
+               <label for="camptix-attendance-generate"><?php _e( 'Generate a new secret link (old links will expire)', 'camptix' ); ?></label>
+               <?php
+       }
+
+       /**
+        * Setup section description.
+        */
+       public function setup_controls_section() {
+               ?>
+               <p>The Attendance UI addon is useful for tracking attendance at the event. It allows registration volunteers to access a mobile-friendly UI during the event, and mark attendees as "attended" or "did not attend" as they register. The UI also offers live search and filters for your convenience.</p>
+
+               <p><strong>Note</strong>: Anyone with the secret link can access the attendance UI and change attendance data. Please keep this URL secret and change it if necessary.</p>
+               <?php
+       }
+
+       /**
+        * Runs whenever the CampTix option is updated.
+        */
+       public function validate_options( $output, $input ) {
+               if ( isset( $input['attendance-enabled'] ) )
+                       $output['attendance-enabled'] = (bool) $input['attendance-enabled'];
+
+               if ( ! empty( $input['attendance-generate'] ) )
+                       $output['attendance-secret'] = wp_generate_password( 32, false, false );
+
+               return $output;
+       }
+
+       /**
+        * Get CampTix Tickets
+        *
+        * Returns an array of published tickets registered with CampTix.
+        */
+       public function get_tickets() {
+               if ( isset( $this->tickets ) )
+                       return $this->tickets;
+
+               $this->tickets = get_posts( array(
+                       'post_type' => 'tix_ticket',
+                       'post_status' => 'publish',
+                       'posts_per_page' => -1,
+               ) );
+
+               return $this->tickets;
+       }
+
+       /**
+        * Write a log entry to CampTix.
+        */
+       public function log( $message, $post_id = 0, $data = null ) {
+               global $camptix;
+               $camptix->log( $message, $post_id, $data, 'attendance' );
+       }
+
+       /**
+        * Register self as a CampTix addon.
+        */
+       public static function register_addon() {
+               camptix_register_addon( __CLASS__ );
+       }
+}
+
+CampTix_Attendance::register_addon();
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre></div>
<a id="sitestrunkwordcamporgpublic_htmlwpcontentpluginscamptixattendancecamptixattendancephp"></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/camptix-attendance/camptix-attendance.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/camptix-attendance/camptix-attendance.php                         (rev 0)
+++ sites/trunk/wordcamp.org/public_html/wp-content/plugins/camptix-attendance/camptix-attendance.php   2014-10-21 15:23:09 UTC (rev 919)
</span><span class="lines" style="display: block; padding: 0 10px; color: #888">@@ -0,0 +1,13 @@
</span><ins style="background-color: #dfd; text-decoration:none; display:block; padding: 0 10px">+<?php
+/**
+ * Plugin Name: CampTix - Attendance UI
+ * Description: An addon for CampTix that allows admins and volunteers to track whether a ticket holder attended the event via a mobile UI.
+ * Version: 0.1
+ * Author: Konstantin Kovshenin
+ * Author URI: http://kovshenin.com
+ */
+
+add_action( 'camptix_load_addons', 'camptix_attendance_register' );
+function camptix_attendance_register() {
+       require_once( plugin_dir_path( __FILE__ ) . 'addons/attendance.php' );
+}
</ins><span class="cx" style="display: block; padding: 0 10px">\ No newline at end of file
</span></span></pre>
</div>
</div>

</body>
</html>