diff --git a/_layouts/default.html b/_layouts/default.html index 15b46c4..336a86c 100644 --- a/_layouts/default.html +++ b/_layouts/default.html @@ -31,7 +31,9 @@ View all + +
@@ -47,8 +49,11 @@ + + + {% for script in page.js %} {% endfor %} diff --git a/css/site.css b/css/site.css index 6197f6b..b981674 100644 --- a/css/site.css +++ b/css/site.css @@ -305,3 +305,55 @@ a.fill-darken0:hover { background-color:rgba(0,0,0,0.15); } -moz-border-radius: 5px; border-radius: 5px; } + +/* Events Sidebar */ +#events { + position:relative; +} +.event p, +.date-cal p { + margin:0; + line-height:1em; +} + +.event { + width:75%; + margin-left:25%; + padding:7px; + background-color:rgba(255,255,255,0.5); + margin-bottom:.5em; +} +.event-chapter { + font-size:.8em; + font-weight:900; +} +.event-time { + float:right; + font-weight:300; +} +.event-location { + font-size:.8em; + padding-top:.4em; +} + +.date-cal { + position:absolute; + left:0; + width:20%; + text-align:center; + border-radius:4px; + overflow:hidden; +} +.date-num { + background-color:#ffffff; + font-size:1.4em; + font-weight:900; + padding:.2em 0; +} +.date-month { + text-transform: uppercase; + font-size: .6em; + color:#e5e5e5; + padding:.3em; + font-weight:900; +} \ No newline at end of file diff --git a/img/loading.gif b/img/loading.gif new file mode 100644 index 0000000..b08576a Binary files /dev/null and b/img/loading.gif differ diff --git a/js/events.js b/js/events.js new file mode 100644 index 0000000..0e5dc96 --- /dev/null +++ b/js/events.js @@ -0,0 +1,76 @@ +var events = document.getElementById("events") ? document.getElementById("events") : false; + +jQuery(document).ready(function($){ + // test spreadsheet key 1kswO9zq5UgQCdPwndBmQGtKhQtbQw8xSQhqHLcQHxKU + if(events) { + events.innerHTML="

Upcoming Events

"; + Tabletop.init({ + key: '1kswO9zq5UgQCdPwndBmQGtKhQtbQw8xSQhqHLcQHxKU', + orderby: 'datetime', // response comes in date order! SO AWESOME. + callback: buildEvents, + simpleSheet: true } ); + } +}); + +function buildEvents(data, tabletop) { + + var today = new Date(); // today's date + events.innerHTML=""; // removes loading.gif + var dates = []; // used to make sure we only print a date once + + // loop through returned data to build events + for (var i=0; i

"+date.month+"

"; + events.appendChild(dateItem); + dates.push(date.day); // push into array for next check + } + + // create event element + var item = document.createElement("li"); + item.className="event"; + item.innerHTML+="

"+data[i].whichchapter+"" + date.time + "

"; + item.innerHTML+="

"+data[i].locationname+"

"; + events.appendChild(item); + + } + +} + +// date and time functions for returning nice names +function time(time) { + var d = new Date(time); + return { + d: d, + dateNum: d.getDate(), + month: month(d.getMonth()), + day: weekday(d.getDay()) + ", " + month(d.getMonth()) + " " + ordinalsuffix(d.getDay()), + time: meetingTime(d) + } +} +function weekday(d) { + days = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; + return days[d]; +} +function month(d) { + months = ["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"]; + return months[d]; +} +function ordinalsuffix(i) { + var j = i % 10, + k = i % 100; + if (j == 1 && k != 11) { return i + "st"; } + if (j == 2 && k != 12) { return i + "nd"; } + if (j == 3 && k != 13) { return i + "rd"; } + return i + "th"; +} +function meetingTime(t) { + return ((t.getHours() < 10)?"0":"") + ((t.getHours()>12)?(t.getHours()-12):t.getHours()) + ":" + ((t.getMinutes() < 10)?"0":"") + t.getMinutes() + ((t.getHours()>12)?('PM'):'AM'); +} \ No newline at end of file diff --git a/js/tabletop.js b/js/tabletop.js new file mode 100644 index 0000000..6f27aba --- /dev/null +++ b/js/tabletop.js @@ -0,0 +1,474 @@ +(function(global) { + "use strict"; + + var inNodeJS = false; + if (typeof module !== 'undefined' && module.exports) { + inNodeJS = true; + var request = require('request'); + } + + var supportsCORS = false; + var inLegacyIE = false; + try { + var testXHR = new XMLHttpRequest(); + if (typeof testXHR.withCredentials !== 'undefined') { + supportsCORS = true; + } else { + if ("XDomainRequest" in window) { + supportsCORS = true; + inLegacyIE = true; + } + } + } catch (e) { } + + // Create a simple indexOf function for support + // of older browsers. Uses native indexOf if + // available. Code similar to underscores. + // By making a separate function, instead of adding + // to the prototype, we will not break bad for loops + // in older browsers + var indexOfProto = Array.prototype.indexOf; + var ttIndexOf = function(array, item) { + var i = 0, l = array.length; + + if (indexOfProto && array.indexOf === indexOfProto) return array.indexOf(item); + for (; i < l; i++) if (array[i] === item) return i; + return -1; + }; + + /* + Initialize with Tabletop.init( { key: '0AjAPaAU9MeLFdHUxTlJiVVRYNGRJQnRmSnQwTlpoUXc' } ) + OR! + Initialize with Tabletop.init( { key: 'https://docs.google.com/spreadsheet/pub?hl=en_US&hl=en_US&key=0AjAPaAU9MeLFdHUxTlJiVVRYNGRJQnRmSnQwTlpoUXc&output=html&widget=true' } ) + OR! + Initialize with Tabletop.init('0AjAPaAU9MeLFdHUxTlJiVVRYNGRJQnRmSnQwTlpoUXc') + */ + + var Tabletop = function(options) { + // Make sure Tabletop is being used as a constructor no matter what. + if(!this || !(this instanceof Tabletop)) { + return new Tabletop(options); + } + + if(typeof(options) === 'string') { + options = { key : options }; + } + + this.callback = options.callback; + this.wanted = options.wanted || []; + this.key = options.key; + this.simpleSheet = !!options.simpleSheet; + this.parseNumbers = !!options.parseNumbers; + this.wait = !!options.wait; + this.reverse = !!options.reverse; + this.postProcess = options.postProcess; + this.debug = !!options.debug; + this.query = options.query || ''; + this.orderby = options.orderby; + this.endpoint = options.endpoint || "https://spreadsheets.google.com"; + this.singleton = !!options.singleton; + this.simple_url = !!options.simple_url; + this.callbackContext = options.callbackContext; + + if(typeof(options.proxy) !== 'undefined') { + // Remove trailing slash, it will break the app + this.endpoint = options.proxy.replace(/\/$/,''); + this.simple_url = true; + this.singleton = true; + // Let's only use CORS (straight JSON request) when + // fetching straight from Google + supportsCORS = false + } + + this.parameterize = options.parameterize || false; + + if(this.singleton) { + if(typeof(Tabletop.singleton) !== 'undefined') { + this.log("WARNING! Tabletop singleton already defined"); + } + Tabletop.singleton = this; + } + + /* Be friendly about what you accept */ + if(/key=/.test(this.key)) { + this.log("You passed an old Google Docs url as the key! Attempting to parse."); + this.key = this.key.match("key=(.*?)(&|#|$)")[1]; + } + + if(/pubhtml/.test(this.key)) { + this.log("You passed a new Google Spreadsheets url as the key! Attempting to parse."); + this.key = this.key.match("d\\/(.*?)\\/pubhtml")[1]; + } + + if(!this.key) { + this.log("You need to pass Tabletop a key!"); + return; + } + + this.log("Initializing with key " + this.key); + + this.models = {}; + this.model_names = []; + + this.base_json_path = "/feeds/worksheets/" + this.key + "/public/basic?alt="; + + if (inNodeJS || supportsCORS) { + this.base_json_path += 'json'; + } else { + this.base_json_path += 'json-in-script'; + } + + if(!this.wait) { + this.fetch(); + } + }; + + // A global storage for callbacks. + Tabletop.callbacks = {}; + + // Backwards compatibility. + Tabletop.init = function(options) { + return new Tabletop(options); + }; + + Tabletop.sheets = function() { + this.log("Times have changed! You'll want to use var tabletop = Tabletop.init(...); tabletop.sheets(...); instead of Tabletop.sheets(...)"); + }; + + Tabletop.prototype = { + + fetch: function(callback) { + if(typeof(callback) !== "undefined") { + this.callback = callback; + } + this.requestData(this.base_json_path, this.loadSheets); + }, + + /* + This will call the environment appropriate request method. + + In browser it will use JSON-P, in node it will use request() + */ + requestData: function(path, callback) { + if (inNodeJS) { + this.serverSideFetch(path, callback); + } else { + //CORS only works in IE8/9 across the same protocol + //You must have your server on HTTPS to talk to Google, or it'll fall back on injection + var protocol = this.endpoint.split("//").shift() || "http"; + if (supportsCORS && (!inLegacyIE || protocol === location.protocol)) { + this.xhrFetch(path, callback); + } else { + this.injectScript(path, callback); + } + } + }, + + /* + Use Cross-Origin XMLHttpRequest to get the data in browsers that support it. + */ + xhrFetch: function(path, callback) { + //support IE8's separate cross-domain object + var xhr = inLegacyIE ? new XDomainRequest() : new XMLHttpRequest(); + xhr.open("GET", this.endpoint + path); + var self = this; + xhr.onload = function() { + try { + var json = JSON.parse(xhr.responseText); + } catch (e) { + console.error(e); + } + callback.call(self, json); + }; + xhr.send(); + }, + + /* + Insert the URL into the page as a script tag. Once it's loaded the spreadsheet data + it triggers the callback. This helps you avoid cross-domain errors + http://code.google.com/apis/gdata/samples/spreadsheet_sample.html + + Let's be plain-Jane and not use jQuery or anything. + */ + injectScript: function(path, callback) { + var script = document.createElement('script'); + var callbackName; + + if(this.singleton) { + if(callback === this.loadSheets) { + callbackName = 'Tabletop.singleton.loadSheets'; + } else if (callback === this.loadSheet) { + callbackName = 'Tabletop.singleton.loadSheet'; + } + } else { + var self = this; + callbackName = 'tt' + (+new Date()) + (Math.floor(Math.random()*100000)); + // Create a temp callback which will get removed once it has executed, + // this allows multiple instances of Tabletop to coexist. + Tabletop.callbacks[ callbackName ] = function () { + var args = Array.prototype.slice.call( arguments, 0 ); + callback.apply(self, args); + script.parentNode.removeChild(script); + delete Tabletop.callbacks[callbackName]; + }; + callbackName = 'Tabletop.callbacks.' + callbackName; + } + + var url = path + "&callback=" + callbackName; + + if(this.simple_url) { + // We've gone down a rabbit hole of passing injectScript the path, so let's + // just pull the sheet_id out of the path like the least efficient worker bees + if(path.indexOf("/list/") !== -1) { + script.src = this.endpoint + "/" + this.key + "-" + path.split("/")[4]; + } else { + script.src = this.endpoint + "/" + this.key; + } + } else { + script.src = this.endpoint + url; + } + + if (this.parameterize) { + script.src = this.parameterize + encodeURIComponent(script.src); + } + + document.getElementsByTagName('script')[0].parentNode.appendChild(script); + }, + + /* + This will only run if tabletop is being run in node.js + */ + serverSideFetch: function(path, callback) { + var self = this + request({url: this.endpoint + path, json: true}, function(err, resp, body) { + if (err) { + return console.error(err); + } + callback.call(self, body); + }); + }, + + /* + Is this a sheet you want to pull? + If { wanted: ["Sheet1"] } has been specified, only Sheet1 is imported + Pulls all sheets if none are specified + */ + isWanted: function(sheetName) { + if(this.wanted.length === 0) { + return true; + } else { + return (ttIndexOf(this.wanted, sheetName) !== -1); + } + }, + + /* + What gets send to the callback + if simpleSheet === true, then don't return an array of Tabletop.this.models, + only return the first one's elements + */ + data: function() { + // If the instance is being queried before the data's been fetched + // then return undefined. + if(this.model_names.length === 0) { + return undefined; + } + if(this.simpleSheet) { + if(this.model_names.length > 1 && this.debug) { + this.log("WARNING You have more than one sheet but are using simple sheet mode! Don't blame me when something goes wrong."); + } + return this.models[ this.model_names[0] ].all(); + } else { + return this.models; + } + }, + + /* + Add another sheet to the wanted list + */ + addWanted: function(sheet) { + if(ttIndexOf(this.wanted, sheet) === -1) { + this.wanted.push(sheet); + } + }, + + /* + Load all worksheets of the spreadsheet, turning each into a Tabletop Model. + Need to use injectScript because the worksheet view that you're working from + doesn't actually include the data. The list-based feed (/feeds/list/key..) does, though. + Calls back to loadSheet in order to get the real work done. + + Used as a callback for the worksheet-based JSON + */ + loadSheets: function(data) { + var i, ilen; + var toLoad = []; + this.foundSheetNames = []; + + for(i = 0, ilen = data.feed.entry.length; i < ilen ; i++) { + this.foundSheetNames.push(data.feed.entry[i].title.$t); + // Only pull in desired sheets to reduce loading + if( this.isWanted(data.feed.entry[i].content.$t) ) { + var linkIdx = data.feed.entry[i].link.length-1; + var sheet_id = data.feed.entry[i].link[linkIdx].href.split('/').pop(); + var json_path = "/feeds/list/" + this.key + "/" + sheet_id + "/public/values?alt=" + if (inNodeJS || supportsCORS) { + json_path += 'json'; + } else { + json_path += 'json-in-script'; + } + if(this.query) { + json_path += "&sq=" + this.query; + } + if(this.orderby) { + json_path += "&orderby=column:" + this.orderby.toLowerCase(); + } + if(this.reverse) { + json_path += "&reverse=true"; + } + toLoad.push(json_path); + } + } + + this.sheetsToLoad = toLoad.length; + for(i = 0, ilen = toLoad.length; i < ilen; i++) { + this.requestData(toLoad[i], this.loadSheet); + } + }, + + /* + Access layer for the this.models + .sheets() gets you all of the sheets + .sheets('Sheet1') gets you the sheet named Sheet1 + */ + sheets: function(sheetName) { + if(typeof sheetName === "undefined") { + return this.models; + } else { + if(typeof(this.models[ sheetName ]) === "undefined") { + // alert( "Can't find " + sheetName ); + return; + } else { + return this.models[ sheetName ]; + } + } + }, + + /* + Parse a single list-based worksheet, turning it into a Tabletop Model + + Used as a callback for the list-based JSON + */ + loadSheet: function(data) { + var model = new Tabletop.Model( { data: data, + parseNumbers: this.parseNumbers, + postProcess: this.postProcess, + tabletop: this } ); + this.models[ model.name ] = model; + if(ttIndexOf(this.model_names, model.name) === -1) { + this.model_names.push(model.name); + } + this.sheetsToLoad--; + if(this.sheetsToLoad === 0) + this.doCallback(); + }, + + /* + Execute the callback upon loading! Rely on this.data() because you might + only request certain pieces of data (i.e. simpleSheet mode) + Tests this.sheetsToLoad just in case a race condition happens to show up + */ + doCallback: function() { + if(this.sheetsToLoad === 0) { + this.callback.apply(this.callbackContext || this, [this.data(), this]); + } + }, + + log: function(msg) { + if(this.debug) { + if(typeof console !== "undefined" && typeof console.log !== "undefined") { + Function.prototype.apply.apply(console.log, [console, arguments]); + } + } + } + + }; + + /* + Tabletop.Model stores the attribute names and parses the worksheet data + to turn it into something worthwhile + + Options should be in the format { data: XXX }, with XXX being the list-based worksheet + */ + Tabletop.Model = function(options) { + var i, j, ilen, jlen; + this.column_names = []; + this.name = options.data.feed.title.$t; + this.elements = []; + this.raw = options.data; // A copy of the sheet's raw data, for accessing minutiae + + if(typeof(options.data.feed.entry) === 'undefined') { + options.tabletop.log("Missing data for " + this.name + ", make sure you didn't forget column headers"); + this.elements = []; + return; + } + + for(var key in options.data.feed.entry[0]){ + if(/^gsx/.test(key)) + this.column_names.push( key.replace("gsx$","") ); + } + + for(i = 0, ilen = options.data.feed.entry.length ; i < ilen; i++) { + var source = options.data.feed.entry[i]; + var element = {}; + for(var j = 0, jlen = this.column_names.length; j < jlen ; j++) { + var cell = source[ "gsx$" + this.column_names[j] ]; + if (typeof(cell) !== 'undefined') { + if(options.parseNumbers && cell.$t !== '' && !isNaN(cell.$t)) + element[ this.column_names[j] ] = +cell.$t; + else + element[ this.column_names[j] ] = cell.$t; + } else { + element[ this.column_names[j] ] = ''; + } + } + if(element.rowNumber === undefined) + element.rowNumber = i + 1; + if( options.postProcess ) + options.postProcess(element); + this.elements.push(element); + } + + }; + + Tabletop.Model.prototype = { + /* + Returns all of the elements (rows) of the worksheet as objects + */ + all: function() { + return this.elements; + }, + + /* + Return the elements as an array of arrays, instead of an array of objects + */ + toArray: function() { + var array = [], + i, j, ilen, jlen; + for(i = 0, ilen = this.elements.length; i < ilen; i++) { + var row = []; + for(j = 0, jlen = this.column_names.length; j < jlen ; j++) { + row.push( this.elements[i][ this.column_names[j] ] ); + } + array.push(row); + } + return array; + } + }; + + if(inNodeJS) { + module.exports = Tabletop; + } else { + global.Tabletop = Tabletop; + } + +})(this); \ No newline at end of file diff --git a/js/underscore.min.js b/js/underscore.min.js index 3434d6c..ab5fbd8 100644 --- a/js/underscore.min.js +++ b/js/underscore.min.js @@ -3,4 +3,3 @@ // (c) 2009-2014 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors // Underscore may be freely distributed under the MIT license. (function(){var n=this,t=n._,r={},e=Array.prototype,u=Object.prototype,i=Function.prototype,a=e.push,o=e.slice,c=e.concat,l=u.toString,f=u.hasOwnProperty,s=e.forEach,p=e.map,h=e.reduce,v=e.reduceRight,g=e.filter,d=e.every,m=e.some,y=e.indexOf,b=e.lastIndexOf,x=Array.isArray,w=Object.keys,_=i.bind,j=function(n){return n instanceof j?n:this instanceof j?void(this._wrapped=n):new j(n)};"undefined"!=typeof exports?("undefined"!=typeof module&&module.exports&&(exports=module.exports=j),exports._=j):n._=j,j.VERSION="1.6.0";var A=j.each=j.forEach=function(n,t,e){if(null==n)return n;if(s&&n.forEach===s)n.forEach(t,e);else if(n.length===+n.length){for(var u=0,i=n.length;i>u;u++)if(t.call(e,n[u],u,n)===r)return}else for(var a=j.keys(n),u=0,i=a.length;i>u;u++)if(t.call(e,n[a[u]],a[u],n)===r)return;return n};j.map=j.collect=function(n,t,r){var e=[];return null==n?e:p&&n.map===p?n.map(t,r):(A(n,function(n,u,i){e.push(t.call(r,n,u,i))}),e)};var O="Reduce of empty array with no initial value";j.reduce=j.foldl=j.inject=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),h&&n.reduce===h)return e&&(t=j.bind(t,e)),u?n.reduce(t,r):n.reduce(t);if(A(n,function(n,i,a){u?r=t.call(e,r,n,i,a):(r=n,u=!0)}),!u)throw new TypeError(O);return r},j.reduceRight=j.foldr=function(n,t,r,e){var u=arguments.length>2;if(null==n&&(n=[]),v&&n.reduceRight===v)return e&&(t=j.bind(t,e)),u?n.reduceRight(t,r):n.reduceRight(t);var i=n.length;if(i!==+i){var a=j.keys(n);i=a.length}if(A(n,function(o,c,l){c=a?a[--i]:--i,u?r=t.call(e,r,n[c],c,l):(r=n[c],u=!0)}),!u)throw new TypeError(O);return r},j.find=j.detect=function(n,t,r){var e;return k(n,function(n,u,i){return t.call(r,n,u,i)?(e=n,!0):void 0}),e},j.filter=j.select=function(n,t,r){var e=[];return null==n?e:g&&n.filter===g?n.filter(t,r):(A(n,function(n,u,i){t.call(r,n,u,i)&&e.push(n)}),e)},j.reject=function(n,t,r){return j.filter(n,function(n,e,u){return!t.call(r,n,e,u)},r)},j.every=j.all=function(n,t,e){t||(t=j.identity);var u=!0;return null==n?u:d&&n.every===d?n.every(t,e):(A(n,function(n,i,a){return(u=u&&t.call(e,n,i,a))?void 0:r}),!!u)};var k=j.some=j.any=function(n,t,e){t||(t=j.identity);var u=!1;return null==n?u:m&&n.some===m?n.some(t,e):(A(n,function(n,i,a){return u||(u=t.call(e,n,i,a))?r:void 0}),!!u)};j.contains=j.include=function(n,t){return null==n?!1:y&&n.indexOf===y?n.indexOf(t)!=-1:k(n,function(n){return n===t})},j.invoke=function(n,t){var r=o.call(arguments,2),e=j.isFunction(t);return j.map(n,function(n){return(e?t:n[t]).apply(n,r)})},j.pluck=function(n,t){return j.map(n,j.property(t))},j.where=function(n,t){return j.filter(n,j.matches(t))},j.findWhere=function(n,t){return j.find(n,j.matches(t))},j.max=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.max.apply(Math,n);var e=-1/0,u=-1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;o>u&&(e=n,u=o)}),e},j.min=function(n,t,r){if(!t&&j.isArray(n)&&n[0]===+n[0]&&n.length<65535)return Math.min.apply(Math,n);var e=1/0,u=1/0;return A(n,function(n,i,a){var o=t?t.call(r,n,i,a):n;u>o&&(e=n,u=o)}),e},j.shuffle=function(n){var t,r=0,e=[];return A(n,function(n){t=j.random(r++),e[r-1]=e[t],e[t]=n}),e},j.sample=function(n,t,r){return null==t||r?(n.length!==+n.length&&(n=j.values(n)),n[j.random(n.length-1)]):j.shuffle(n).slice(0,Math.max(0,t))};var E=function(n){return null==n?j.identity:j.isFunction(n)?n:j.property(n)};j.sortBy=function(n,t,r){return t=E(t),j.pluck(j.map(n,function(n,e,u){return{value:n,index:e,criteria:t.call(r,n,e,u)}}).sort(function(n,t){var r=n.criteria,e=t.criteria;if(r!==e){if(r>e||r===void 0)return 1;if(e>r||e===void 0)return-1}return n.index-t.index}),"value")};var F=function(n){return function(t,r,e){var u={};return r=E(r),A(t,function(i,a){var o=r.call(e,i,a,t);n(u,o,i)}),u}};j.groupBy=F(function(n,t,r){j.has(n,t)?n[t].push(r):n[t]=[r]}),j.indexBy=F(function(n,t,r){n[t]=r}),j.countBy=F(function(n,t){j.has(n,t)?n[t]++:n[t]=1}),j.sortedIndex=function(n,t,r,e){r=E(r);for(var u=r.call(e,t),i=0,a=n.length;a>i;){var o=i+a>>>1;r.call(e,n[o])t?[]:o.call(n,0,t)},j.initial=function(n,t,r){return o.call(n,0,n.length-(null==t||r?1:t))},j.last=function(n,t,r){return null==n?void 0:null==t||r?n[n.length-1]:o.call(n,Math.max(n.length-t,0))},j.rest=j.tail=j.drop=function(n,t,r){return o.call(n,null==t||r?1:t)},j.compact=function(n){return j.filter(n,j.identity)};var M=function(n,t,r){return t&&j.every(n,j.isArray)?c.apply(r,n):(A(n,function(n){j.isArray(n)||j.isArguments(n)?t?a.apply(r,n):M(n,t,r):r.push(n)}),r)};j.flatten=function(n,t){return M(n,t,[])},j.without=function(n){return j.difference(n,o.call(arguments,1))},j.partition=function(n,t){var r=[],e=[];return A(n,function(n){(t(n)?r:e).push(n)}),[r,e]},j.uniq=j.unique=function(n,t,r,e){j.isFunction(t)&&(e=r,r=t,t=!1);var u=r?j.map(n,r,e):n,i=[],a=[];return A(u,function(r,e){(t?e&&a[a.length-1]===r:j.contains(a,r))||(a.push(r),i.push(n[e]))}),i},j.union=function(){return j.uniq(j.flatten(arguments,!0))},j.intersection=function(n){var t=o.call(arguments,1);return j.filter(j.uniq(n),function(n){return j.every(t,function(t){return j.contains(t,n)})})},j.difference=function(n){var t=c.apply(e,o.call(arguments,1));return j.filter(n,function(n){return!j.contains(t,n)})},j.zip=function(){for(var n=j.max(j.pluck(arguments,"length").concat(0)),t=new Array(n),r=0;n>r;r++)t[r]=j.pluck(arguments,""+r);return t},j.object=function(n,t){if(null==n)return{};for(var r={},e=0,u=n.length;u>e;e++)t?r[n[e]]=t[e]:r[n[e][0]]=n[e][1];return r},j.indexOf=function(n,t,r){if(null==n)return-1;var e=0,u=n.length;if(r){if("number"!=typeof r)return e=j.sortedIndex(n,t),n[e]===t?e:-1;e=0>r?Math.max(0,u+r):r}if(y&&n.indexOf===y)return n.indexOf(t,r);for(;u>e;e++)if(n[e]===t)return e;return-1},j.lastIndexOf=function(n,t,r){if(null==n)return-1;var e=null!=r;if(b&&n.lastIndexOf===b)return e?n.lastIndexOf(t,r):n.lastIndexOf(t);for(var u=e?r:n.length;u--;)if(n[u]===t)return u;return-1},j.range=function(n,t,r){arguments.length<=1&&(t=n||0,n=0),r=arguments[2]||1;for(var e=Math.max(Math.ceil((t-n)/r),0),u=0,i=new Array(e);e>u;)i[u++]=n,n+=r;return i};var R=function(){};j.bind=function(n,t){var r,e;if(_&&n.bind===_)return _.apply(n,o.call(arguments,1));if(!j.isFunction(n))throw new TypeError;return r=o.call(arguments,2),e=function(){if(!(this instanceof e))return n.apply(t,r.concat(o.call(arguments)));R.prototype=n.prototype;var u=new R;R.prototype=null;var i=n.apply(u,r.concat(o.call(arguments)));return Object(i)===i?i:u}},j.partial=function(n){var t=o.call(arguments,1);return function(){for(var r=0,e=t.slice(),u=0,i=e.length;i>u;u++)e[u]===j&&(e[u]=arguments[r++]);for(;r=f?(clearTimeout(a),a=null,o=l,i=n.apply(e,u),e=u=null):a||r.trailing===!1||(a=setTimeout(c,f)),i}},j.debounce=function(n,t,r){var e,u,i,a,o,c=function(){var l=j.now()-a;t>l?e=setTimeout(c,t-l):(e=null,r||(o=n.apply(i,u),i=u=null))};return function(){i=this,u=arguments,a=j.now();var l=r&&!e;return e||(e=setTimeout(c,t)),l&&(o=n.apply(i,u),i=u=null),o}},j.once=function(n){var t,r=!1;return function(){return r?t:(r=!0,t=n.apply(this,arguments),n=null,t)}},j.wrap=function(n,t){return j.partial(t,n)},j.compose=function(){var n=arguments;return function(){for(var t=arguments,r=n.length-1;r>=0;r--)t=[n[r].apply(this,t)];return t[0]}},j.after=function(n,t){return function(){return--n<1?t.apply(this,arguments):void 0}},j.keys=function(n){if(!j.isObject(n))return[];if(w)return w(n);var t=[];for(var r in n)j.has(n,r)&&t.push(r);return t},j.values=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=n[t[u]];return e},j.pairs=function(n){for(var t=j.keys(n),r=t.length,e=new Array(r),u=0;r>u;u++)e[u]=[t[u],n[t[u]]];return e},j.invert=function(n){for(var t={},r=j.keys(n),e=0,u=r.length;u>e;e++)t[n[r[e]]]=r[e];return t},j.functions=j.methods=function(n){var t=[];for(var r in n)j.isFunction(n[r])&&t.push(r);return t.sort()},j.extend=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]=t[r]}),n},j.pick=function(n){var t={},r=c.apply(e,o.call(arguments,1));return A(r,function(r){r in n&&(t[r]=n[r])}),t},j.omit=function(n){var t={},r=c.apply(e,o.call(arguments,1));for(var u in n)j.contains(r,u)||(t[u]=n[u]);return t},j.defaults=function(n){return A(o.call(arguments,1),function(t){if(t)for(var r in t)n[r]===void 0&&(n[r]=t[r])}),n},j.clone=function(n){return j.isObject(n)?j.isArray(n)?n.slice():j.extend({},n):n},j.tap=function(n,t){return t(n),n};var S=function(n,t,r,e){if(n===t)return 0!==n||1/n==1/t;if(null==n||null==t)return n===t;n instanceof j&&(n=n._wrapped),t instanceof j&&(t=t._wrapped);var u=l.call(n);if(u!=l.call(t))return!1;switch(u){case"[object String]":return n==String(t);case"[object Number]":return n!=+n?t!=+t:0==n?1/n==1/t:n==+t;case"[object Date]":case"[object Boolean]":return+n==+t;case"[object RegExp]":return n.source==t.source&&n.global==t.global&&n.multiline==t.multiline&&n.ignoreCase==t.ignoreCase}if("object"!=typeof n||"object"!=typeof t)return!1;for(var i=r.length;i--;)if(r[i]==n)return e[i]==t;var a=n.constructor,o=t.constructor;if(a!==o&&!(j.isFunction(a)&&a instanceof a&&j.isFunction(o)&&o instanceof o)&&"constructor"in n&&"constructor"in t)return!1;r.push(n),e.push(t);var c=0,f=!0;if("[object Array]"==u){if(c=n.length,f=c==t.length)for(;c--&&(f=S(n[c],t[c],r,e)););}else{for(var s in n)if(j.has(n,s)&&(c++,!(f=j.has(t,s)&&S(n[s],t[s],r,e))))break;if(f){for(s in t)if(j.has(t,s)&&!c--)break;f=!c}}return r.pop(),e.pop(),f};j.isEqual=function(n,t){return S(n,t,[],[])},j.isEmpty=function(n){if(null==n)return!0;if(j.isArray(n)||j.isString(n))return 0===n.length;for(var t in n)if(j.has(n,t))return!1;return!0},j.isElement=function(n){return!(!n||1!==n.nodeType)},j.isArray=x||function(n){return"[object Array]"==l.call(n)},j.isObject=function(n){return n===Object(n)},A(["Arguments","Function","String","Number","Date","RegExp"],function(n){j["is"+n]=function(t){return l.call(t)=="[object "+n+"]"}}),j.isArguments(arguments)||(j.isArguments=function(n){return!(!n||!j.has(n,"callee"))}),"function"!=typeof/./&&(j.isFunction=function(n){return"function"==typeof n}),j.isFinite=function(n){return isFinite(n)&&!isNaN(parseFloat(n))},j.isNaN=function(n){return j.isNumber(n)&&n!=+n},j.isBoolean=function(n){return n===!0||n===!1||"[object Boolean]"==l.call(n)},j.isNull=function(n){return null===n},j.isUndefined=function(n){return n===void 0},j.has=function(n,t){return f.call(n,t)},j.noConflict=function(){return n._=t,this},j.identity=function(n){return n},j.constant=function(n){return function(){return n}},j.property=function(n){return function(t){return t[n]}},j.matches=function(n){return function(t){if(t===n)return!0;for(var r in n)if(n[r]!==t[r])return!1;return!0}},j.times=function(n,t,r){for(var e=Array(Math.max(0,n)),u=0;n>u;u++)e[u]=t.call(r,u);return e},j.random=function(n,t){return null==t&&(t=n,n=0),n+Math.floor(Math.random()*(t-n+1))},j.now=Date.now||function(){return(new Date).getTime()};var T={escape:{"&":"&","<":"<",">":">",'"':""","'":"'"}};T.unescape=j.invert(T.escape);var I={escape:new RegExp("["+j.keys(T.escape).join("")+"]","g"),unescape:new RegExp("("+j.keys(T.unescape).join("|")+")","g")};j.each(["escape","unescape"],function(n){j[n]=function(t){return null==t?"":(""+t).replace(I[n],function(t){return T[n][t]})}}),j.result=function(n,t){if(null==n)return void 0;var r=n[t];return j.isFunction(r)?r.call(n):r},j.mixin=function(n){A(j.functions(n),function(t){var r=j[t]=n[t];j.prototype[t]=function(){var n=[this._wrapped];return a.apply(n,arguments),z.call(this,r.apply(j,n))}})};var N=0;j.uniqueId=function(n){var t=++N+"";return n?n+t:t},j.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var q=/(.)^/,B={"'":"'","\\":"\\","\r":"r","\n":"n"," ":"t","\u2028":"u2028","\u2029":"u2029"},D=/\\|'|\r|\n|\t|\u2028|\u2029/g;j.template=function(n,t,r){var e;r=j.defaults({},r,j.templateSettings);var u=new RegExp([(r.escape||q).source,(r.interpolate||q).source,(r.evaluate||q).source].join("|")+"|$","g"),i=0,a="__p+='";n.replace(u,function(t,r,e,u,o){return a+=n.slice(i,o).replace(D,function(n){return"\\"+B[n]}),r&&(a+="'+\n((__t=("+r+"))==null?'':_.escape(__t))+\n'"),e&&(a+="'+\n((__t=("+e+"))==null?'':__t)+\n'"),u&&(a+="';\n"+u+"\n__p+='"),i=o+t.length,t}),a+="';\n",r.variable||(a="with(obj||{}){\n"+a+"}\n"),a="var __t,__p='',__j=Array.prototype.join,"+"print=function(){__p+=__j.call(arguments,'');};\n"+a+"return __p;\n";try{e=new Function(r.variable||"obj","_",a)}catch(o){throw o.source=a,o}if(t)return e(t,j);var c=function(n){return e.call(this,n,j)};return c.source="function("+(r.variable||"obj")+"){\n"+a+"}",c},j.chain=function(n){return j(n).chain()};var z=function(n){return this._chain?j(n).chain():n};j.mixin(j),A(["pop","push","reverse","shift","sort","splice","unshift"],function(n){var t=e[n];j.prototype[n]=function(){var r=this._wrapped;return t.apply(r,arguments),"shift"!=n&&"splice"!=n||0!==r.length||delete r[0],z.call(this,r)}}),A(["concat","join","slice"],function(n){var t=e[n];j.prototype[n]=function(){return z.call(this,t.apply(this._wrapped,arguments))}}),j.extend(j.prototype,{chain:function(){return this._chain=!0,this},value:function(){return this._wrapped}}),"function"==typeof define&&define.amd&&define("underscore",[],function(){return j})}).call(this); -//# sourceMappingURL=underscore-min.map \ No newline at end of file