├── .gitignore ├── LICENSE ├── README.md ├── dist ├── climacons │ ├── climacons-webfont.eot │ ├── climacons-webfont.svg │ ├── climacons-webfont.ttf │ └── climacons-webfont.woff ├── index.html ├── scripts.js └── style.css ├── gulpfile.js ├── logo.png ├── manifest.json ├── opt ├── options.html └── options.js ├── package.json ├── screenshot.png └── src ├── index.html ├── script.js ├── style.sass └── vendor ├── climacons.scss ├── jquery-3.3.1.js ├── mousetrap.js └── unsplash-source.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | src/scripts.js 3 | src/style.css 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Paul Schaefer 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ![logo](logo.png) Start [https://pschfr.github.io/start/dist](https://pschfr.github.io/start/dist) 2 | ![https://pschfr.github.io/start/dist](screenshot.png) 3 | ## Personal new tab page with keyboard navigation, bookmarks, weather, quote, most recently listened to song, unread emails, and image background. 4 | 5 | ## Features: 6 | - Background is a random nature photo that changes daily from [Unsplash Source](https://source.unsplash.com/) 7 | - Weather is [powered by Dark Sky!](https://darksky.net/poweredby/) 8 | - If it is snowing or raining, it will "snow" thanks to [HermannBjorgvin/SnowJs](https://github.com/HermannBjorgvin/SnowJs) 9 | - Weather icons are from [Climacons](https://github.com/christiannaths/Climacons-Font) 10 | - Keyboard navigation is thanks to [ccampbell/mousetrap](https://github.com/ccampbell/mousetrap)! (Hit ? for a explanation) 11 | - Random inspirational quote! 12 | - Most recently listened to song thanks to my Last.FM script [pschfr/LastFM.js](https://github.com/pschfr/LastFM.js)! 13 | - Unread emails come straight from Gmail if you're signed in. 14 | 15 | Each category can open by keyboard and click, press escape or click background to close all. 16 | 17 | #### Hint! Press `?` to see a keyboard shortcut explanation. 18 | 19 | ## You need extensions for most browsers to use this as your new tab page: 20 | - [New Tab Redirect for Chrome](https://chrome.google.com/webstore/detail/new-tab-redirect/icpgjfneehieebagbmdbhnlpiopdcmna) 21 | - You can also go to chrome://extensions, tick Developer Mode, and Load as unpacked extension for faster loading. 22 | - [New Tab Override for Firefox](https://addons.mozilla.org/en-US/firefox/addon/new-tab-override/) 23 | - [Custom New Tab Page for Opera](https://addons.opera.com/en/extensions/details/custom-new-tab-page/) 24 | - Safari - just set it as your homepage in settings. 25 | 26 | It's super fast. Fully loaded at ~300ms (~150ms as an extension!!) :D 27 | 28 | Feel free to fork, and add your own bookmarks and quotes. If you have any questions, [tweet me](http://twitter.com/pschfr) or file an issue or pull request. 29 | -------------------------------------------------------------------------------- /dist/climacons/climacons-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschfr/start/b1f3e699625503a8e9c21de6ecf865b6374f1bb9/dist/climacons/climacons-webfont.eot -------------------------------------------------------------------------------- /dist/climacons/climacons-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschfr/start/b1f3e699625503a8e9c21de6ecf865b6374f1bb9/dist/climacons/climacons-webfont.ttf -------------------------------------------------------------------------------- /dist/climacons/climacons-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschfr/start/b1f3e699625503a8e9c21de6ecf865b6374f1bb9/dist/climacons/climacons-webfont.woff -------------------------------------------------------------------------------- /dist/index.html: -------------------------------------------------------------------------------- 1 | New Tab
Loading…
Waiting for Dark Sky…

Loading song from Last.FM…

Fetching unread emails…

-------------------------------------------------------------------------------- /dist/scripts.js: -------------------------------------------------------------------------------- 1 | function startTime(){var e=["January","February","March","April","May","June","July","August","September","October","November","December"],t=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],n=new Date,r=[n.getHours(),n.getMinutes(),n.getSeconds()],o=[n.getDate(),n.getDay(),n.getMonth(),n.getFullYear()],i=r[0],a=r[1],s=r[2],u=i>=12?"PM":"AM",l=o[0],c=t[o[1]],f=e[o[2]],p=o[3];i%=12,i=i?i:12,a=a<10?"0"+a:a,s=s<10?"0"+s:s,document.getElementById("time").innerHTML=i+":"+a+":"+s+" "+u,document.getElementById("date").innerHTML=c+", "+f+" "+l+", "+p;setTimeout(startTime,500)}function randomQuote(){var e=["If you are depressed you are living in the past. If you are anxious you are living in the future. If you are at peace you are living in the present.","Madness, as you know, is a lot like gravity, all it takes is a little push.","The surest way to corrupt a youth is to instruct him to hold in higher esteem those who think alike than those who think differently.","Life has many ways of testing a person's will, either by having nothing happen at all or by having everything happen all at once.","There is no excellent beauty that hath not some strangeness in its proportions.","Children are fantastic little creatures, because next to drunk people, they are the only truly honest people on earth.","I begin with an idea, and then it becomes something else.","Be who you are and say what you feel because those who mind don't matter and those who matter don't mind.","You can make more friends in two months by becoming interested in other people than you can in two years by trying to get people interested in you.","An essential aspect of creativity is not being afraid to fail.","Antisocial behavior is a trait of intelligence in a world of conformists.","What you do today can improve all your tomorrows.","A creative man is motivated by the desire to achieve, not by the desire to beat others.","Don't watch the clock; do what it does. Keep going.","If you can dream it, you can do it.","You can't build a reputation on what you're going to do."],t=["Lao Tzu","Joker","Friedrich Nietzsche","Paulo Coelho","Sir Francis Bacon","Mads Nipper","Pablo Picasso","Dr. Seuss","Dale Carnegie","Edwin Land","Nikola Tesla","Ralph Marston","Ayn Rand","Sam Levenson","Walt Disney","Henry Ford"],n=Math.floor(Math.random()*e.length);document.getElementById("quote").innerHTML="“"+e[n]+"” — "+t[n]+""}function randomBackground(e){var t=["buildings","nature","objects"],n=Math.floor(Math.random()*t.length),r=new UnsplashPhoto;"daily"==e||"weekly"==e?r.all().randomize(e).fromCategory(t[n]).fetch():r.all().fromCategory(t[n]).fetch(),document.body.style.backgroundImage="url("+r.url+")"}function fetchBookmarks(){var e=6;chrome.bookmarks.getTree(function(t){t.forEach(function(t){t.children[0].children.forEach(function(t){t.children&&e>=1&&(console.log(t.title+" "+t.title.charAt(0).toLowerCase()),t.children.forEach(function(e){var t=e.title.match(/\[(.*?)\]/);t&&console.log(e.title+" "+e.url+" "+e.title.match(t[1]))}),console.log(""),e--)})})});var t=document.createElement("div");t.className="left",document.getElementById("box").appendChild(t);var n=document.createElement("div");n.className="right",document.getElementById("box").appendChild(n)}function bindMousetraps(){$.each($(".parent"),function(e,t){Mousetrap.bind($(t).attr("data-key"),function(e){$("a#"+$(t).attr("id")).toggleClass("active").next().slideToggle(150),$.each($(t).parent().find(".tab"),function(e,t){Mousetrap.bind($(t).attr("data-key"),function(e){window.location.href=$(t).attr("href")}),Mousetrap.bind($(t).attr("data-key").toUpperCase(),function(e){window.open($(t).attr("href"),"_blank")})}),Mousetrap.bind($(t).attr("data-key"),function(e){resetMousetraps()})})}),Mousetrap.bind("esc",function(){resetMousetraps()}),Mousetrap.bind("w",function(){window.location.href=document.getElementById("weatherlink")}),Mousetrap.bind("g",function(){window.location.href="https://github.com/pschfr/start"}),Mousetrap.bind("shift+g",function(){window.location.href="https://github.com/pschfr"}),Mousetrap.bind("alt+g",function(){window.location.href="https://github.com/pschfr/start/projects/2?fullscreen=true"}),Mousetrap.bind("?",function(){openModal()})}function openModal(){""==document.getElementById("modal").style.display?document.getElementById("modal").style.display="block":closeModal()}function closeModal(){document.getElementById("modal").style.display=""}function resetMousetraps(){$(".subMenu").slideUp(150),$("li a").removeClass("active"),Mousetrap.reset(),bindMousetraps(),document.getElementById("modal").style.display=""}function getWeather(e){var t="3dc48ab835ed1b4369c089d0e742ff03",n="flags,daily,minutely,alerts",r="https://api.darksky.net/forecast/"+t+"/"+e+"?exclude="+n,o=new XMLHttpRequest;o.open("GET",r,!0),o.onreadystatechange=function(){if(4==o.readyState&&200==o.status){var t=JSON.parse(o.responseText),n="";"clear-day"==t.currently.icon?n="sun":"clear-night"==t.currently.icon?n="moon":"rain"==t.currently.icon?n="rain":"snow"==t.currently.icon?n="snow":"sleet"==t.currently.icon?n="sleet":"wind"==t.currently.icon?n="wind":"fog"==t.currently.icon?n="fog":"cloudy"==t.currently.icon?n="cloud":"partly-cloudy-day"==t.currently.icon?n="cloud sun":"partly-cloudy-night"==t.currently.icon&&(n="cloud moon"),"snow"==t.currently.icon||"sleet"==t.currently.icon?participate("snow"):"rain"==t.currently.icon&&participate("rain"),document.getElementById("weather").innerHTML=' '+t.currently.summary+", "+Math.round(t.currently.temperature)+"°",document.getElementById("details").innerHTML=t.hourly.summary.replace(",",",
")}},o.send(null)}function geolocWeather(){"geolocation"in navigator?navigator.geolocation.getCurrentPosition(function(e){getWeather(e.coords.latitude+","+e.coords.longitude)}):getWeather("40.4406, -79.9959")}function lastfmRequest(){var e="paul_r_schaefer",t="0f680404e39c821cac34008cc4d803db",n="https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user="+e+"&api_key="+t+"&limit=1&format=json",r=document.getElementById("lastFM"),o=new XMLHttpRequest;o.open("GET",n,!0),o.onreadystatechange=function(){if(4==o.readyState&&200==o.status){var e=JSON.parse(o.responseText).recenttracks["@attr"].total,t=JSON.parse(o.responseText).recenttracks.track[0];t["@attr"]&&""!==t["@attr"].nowplaying?r.innerHTML='currently listening to: ':r.innerHTML='last listened to: ',r.innerHTML+=''+t.artist["#text"]+" — "+t.name+" "}},o.send(null)}function getOptions(){chrome.storage.sync.get({categoryBuildings:"category/buildings",categoryFood:"category/food",categoryNature:"category/nature",categoryPeople:"category/people",categoryTechnology:"category/technology",categoryObjects:"category/objects",backgroundRefresh:"daily",lastFMusername:"paul_r_schaefer"},function(e){console.log(e)})}function participate(e){function t(){n.height=n.offsetHeight,n.width=n.offsetWidth}var n=document.getElementById("snow"),r=n.getContext("2d"),o=[];n.style.pointerEvents="none",n.style.position="fixed",n.style.top=0,n.style.left=0,n.style.width="100vw",n.style.height="100vh",t(),window.onresize=function(){t()};var i=Math;setInterval(function(){r.clearRect(0,0,n.width,n.height),r.beginPath();var t=i.random(),a=.05+.95*t;for(flake={},flake.x=1.5*n.width*i.random()-.5*n.width,flake.y=-9,flake.velX=2*a*(i.random()/2+.5),flake.velY=(4+2*i.random())*a,flake.radius=i.pow(5*t,2)/5,flake.update=function(){var t=this;t.x+=t.velX,t.y+=t.velY,r.beginPath(),r.arc(t.x,t.y,t.radius,0,2*i.PI,!1),"snow"==e?r.fillStyle="#FFF":"rain"==e&&(r.fillStyle="#00F"),r.fill()},o.push(flake),b=0;bn.height?o.splice(b,1):o[b].update()},16)}function gmailRequest(){var e="https://mail.google.com/mail/u/0/feed/atom";element=document.getElementById("gmail"),xmlhttp=new XMLHttpRequest,xmlhttp.open("GET",e,!0),xmlhttp.onreadystatechange=function(){if(4==xmlhttp.readyState&&200==xmlhttp.status){var t=new DOMParser,n=t.parseFromString(xmlhttp.responseText,"application/xml"),r=n.getElementsByTagName("title")[0].innerHTML.replace("Gmail - Inbox for ",""),o=n.getElementsByTagName("fullcount")[0].innerHTML,i=n.getElementsByTagName("entry");if(entryList=r+":\n",plural=o>1?"s":"",0==i.length)element.innerHTML="

Inbox zero. Enjoy your day.

\n";else{for(var a=0;a"+o+" unread email"+plural+"

\n"}}},xmlhttp.send(null)}!function(e,t){"use strict";"object"==typeof module&&"object"==typeof module.exports?module.exports=e.document?t(e,!0):function(e){if(!e.document)throw new Error("jQuery requires a window with a document");return t(e)}:t(e)}("undefined"!=typeof window?window:this,function(e,t){"use strict";function n(e,t,n){t=t||ae;var r,o=t.createElement("script");if(o.text=e,n)for(r in xe)n[r]&&(o[r]=n[r]);t.head.appendChild(o).parentNode.removeChild(o)}function r(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?pe[de.call(e)]||"object":typeof e}function o(e){var t=!!e&&"length"in e&&e.length,n=r(e);return!ve(e)&&!be(e)&&("array"===n||0===t||"number"==typeof t&&t>0&&t-1 in e)}function i(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()}function a(e,t,n){return ve(t)?Te.grep(e,function(e,r){return!!t.call(e,r,e)!==n}):t.nodeType?Te.grep(e,function(e){return e===t!==n}):"string"!=typeof t?Te.grep(e,function(e){return fe.call(t,e)>-1!==n}):Te.filter(t,e,n)}function s(e,t){for(;(e=e[t])&&1!==e.nodeType;);return e}function u(e){var t={};return Te.each(e.match(He)||[],function(e,n){t[n]=!0}),t}function l(e){return e}function c(e){throw e}function f(e,t,n,r){var o;try{e&&ve(o=e.promise)?o.call(e).done(t).fail(n):e&&ve(o=e.then)?o.call(e,t,n):t.apply(void 0,[e].slice(r))}catch(e){n.apply(void 0,[e])}}function p(){ae.removeEventListener("DOMContentLoaded",p),e.removeEventListener("load",p),Te.ready()}function d(e,t){return t.toUpperCase()}function h(e){return e.replace(Re,"ms-").replace(Be,d)}function y(){this.expando=Te.expando+y.uid++}function g(e){return"true"===e||"false"!==e&&("null"===e?null:e===+e+""?+e:We.test(e)?JSON.parse(e):e)}function m(e,t,n){var r;if(void 0===n&&1===e.nodeType)if(r="data-"+t.replace(ze,"-$&").toLowerCase(),n=e.getAttribute(r),"string"==typeof n){try{n=g(n)}catch(e){}Fe.set(e,t,n)}else n=void 0;return n}function v(e,t,n,r){var o,i,a=20,s=r?function(){return r.cur()}:function(){return Te.css(e,t,"")},u=s(),l=n&&n[3]||(Te.cssNumber[t]?"":"px"),c=(Te.cssNumber[t]||"px"!==l&&+u)&&Xe.exec(Te.css(e,t));if(c&&c[3]!==l){for(u/=2,l=l||c[3],c=+u||1;a--;)Te.style(e,t,c+l),(1-i)*(1-(i=s()/u||.5))<=0&&(a=0),c/=i;c=2*c,Te.style(e,t,c+l),n=n||[]}return n&&(c=+c||+u||0,o=n[1]?c+(n[1]+1)*n[2]:+n[2],r&&(r.unit=l,r.start=c,r.end=o)),o}function b(e){var t,n=e.ownerDocument,r=e.nodeName,o=Ge[r];return o?o:(t=n.body.appendChild(n.createElement(r)),o=Te.css(t,"display"),t.parentNode.removeChild(t),"none"===o&&(o="block"),Ge[r]=o,o)}function x(e,t){for(var n,r,o=[],i=0,a=e.length;i-1)i&&i.push(a);else if(c=Te.contains(a.ownerDocument,a),s=w(p.appendChild(a),"script"),c&&T(s),n)for(f=0;a=s[f++];)Ze.test(a.type||"")&&n.push(a);return p}function C(){return!0}function E(){return!1}function S(){try{return ae.activeElement}catch(e){}}function N(e,t,n,r,o,i){var a,s;if("object"==typeof t){"string"!=typeof n&&(r=r||n,n=void 0);for(s in t)N(e,s,n,r,t[s],i);return e}if(null==r&&null==o?(o=n,r=n=void 0):null==o&&("string"==typeof n?(o=r,r=void 0):(o=r,r=n,n=void 0)),o===!1)o=E;else if(!o)return e;return 1===i&&(a=o,o=function(e){return Te().off(e),a.apply(this,arguments)},o.guid=a.guid||(a.guid=Te.guid++)),e.each(function(){Te.event.add(this,t,o,r,n)})}function A(e,t){return i(e,"table")&&i(11!==t.nodeType?t:t.firstChild,"tr")?Te(e).children("tbody")[0]||e:e}function D(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function j(e){return"true/"===(e.type||"").slice(0,5)?e.type=e.type.slice(5):e.removeAttribute("type"),e}function L(e,t){var n,r,o,i,a,s,u,l;if(1===t.nodeType){if($e.hasData(e)&&(i=$e.access(e),a=$e.set(t,i),l=i.events)){delete a.handle,a.events={};for(o in l)for(n=0,r=l[o].length;n1&&"string"==typeof h&&!me.checkClone&&ut.test(h))return e.each(function(n){var i=e.eq(n);y&&(t[0]=h.call(this,n,i.html())),M(i,t,r,o)});if(p&&(i=k(t,e[0].ownerDocument,!1,e,o),a=i.firstChild,1===i.childNodes.length&&(i=a),a||o)){for(s=Te.map(w(i,"script"),D),u=s.length;f=0&&(u+=Math.max(0,Math.ceil(e["offset"+t[0].toUpperCase()+t.slice(1)]-i-u-s-.5))),u}function $(e,t,n){var r=ft(e),o=P(e,t,r),i="border-box"===Te.css(e,"boxSizing",!1,r),a=i;if(ct.test(o)){if(!n)return o;o="auto"}return a=a&&(me.boxSizingReliable()||o===e.style[t]),("auto"===o||!parseFloat(o)&&"inline"===Te.css(e,"display",!1,r))&&(o=e["offset"+t[0].toUpperCase()+t.slice(1)],a=!0),o=parseFloat(o)||0,o+_(e,t,n||(i?"border":"content"),a,r,o)+"px"}function F(e,t,n,r,o){return new F.prototype.init(e,t,n,r,o)}function W(){xt&&(ae.hidden===!1&&e.requestAnimationFrame?e.requestAnimationFrame(W):e.setTimeout(W,Te.fx.interval),Te.fx.tick())}function z(){return e.setTimeout(function(){bt=void 0}),bt=Date.now()}function U(e,t){var n,r=0,o={height:e};for(t=t?1:0;r<4;r+=2-t)n=Ke[r],o["margin"+n]=o["padding"+n]=e;return t&&(o.opacity=o.width=e),o}function X(e,t,n){for(var r,o=(Y.tweeners[t]||[]).concat(Y.tweeners["*"]),i=0,a=o.length;i=0&&nk.cacheLength&&delete e[t.shift()],e[n+" "]=r}var t=[];return e}function r(e){return e[_]=!0,e}function o(e){var t=M.createElement("fieldset");try{return!!e(t)}catch(e){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function i(e,t){for(var n=e.split("|"),r=n.length;r--;)k.attrHandle[n[r]]=t}function a(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&e.sourceIndex-t.sourceIndex;if(r)return r;if(n)for(;n=n.nextSibling;)if(n===t)return-1;return e?1:-1}function s(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function u(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function l(e){return function(t){return"form"in t?t.parentNode&&t.disabled===!1?"label"in t?"label"in t.parentNode?t.parentNode.disabled===e:t.disabled===e:t.isDisabled===e||t.isDisabled!==!e&&Ce(t)===e:t.disabled===e:"label"in t&&t.disabled===e}}function c(e){return r(function(t){return t=+t,r(function(n,r){for(var o,i=e([],n.length,t),a=i.length;a--;)n[o=i[a]]&&(n[o]=!(r[o]=n[o]))})})}function f(e){return e&&"undefined"!=typeof e.getElementsByTagName&&e}function p(){}function d(e){for(var t=0,n=e.length,r="";t1?function(t,n,r){for(var o=e.length;o--;)if(!e[o](t,n,r))return!1;return!0}:e[0]}function g(e,n,r){for(var o=0,i=n.length;o-1&&(r[l]=!(a[l]=f))}}else b=m(b===a?b.splice(h,b.length):b),i?i(null,a,b,u):Q.apply(a,b)})}function b(e){for(var t,n,r,o=e.length,i=k.relative[e[0].type],a=i||k.relative[" "],s=i?1:0,u=h(function(e){return e===t},a,!0),l=h(function(e){return ee(t,e)>-1},a,!0),c=[function(e,n,r){var o=!i&&(r||n!==D)||((t=n).nodeType?u(e,n,r):l(e,n,r));return t=null,o}];s1&&y(c),s>1&&d(e.slice(0,s-1).concat({value:" "===e[s-2].type?"*":""})).replace(se,"$1"),n,s0,i=e.length>0,a=function(r,a,s,u,l){var c,f,p,d=0,h="0",y=r&&[],g=[],v=D,b=r||i&&k.find.TAG("*",l),x=F+=null==v?1:Math.random()||.1,w=b.length;for(l&&(D=a===M||a||l);h!==w&&null!=(c=b[h]);h++){if(i&&c){for(f=0,a||c.ownerDocument===M||(q(c),s=!P);p=e[f++];)if(p(c,a||M,s)){u.push(c);break}l&&(F=x)}o&&((c=!p&&c)&&d--,r&&y.push(c))}if(d+=h,o&&h!==d){for(f=0;p=n[f++];)p(y,g,a,s);if(r){if(d>0)for(;h--;)y[h]||g[h]||(g[h]=G.call(u));g=m(g)}Q.apply(u,g),l&&!r&&g.length>0&&d+n.length>1&&t.uniqueSort(u)}return l&&(F=x,D=v),y};return o?r(a):a}var w,T,k,C,E,S,N,A,D,j,L,q,M,H,P,O,I,R,B,_="sizzle"+1*new Date,$=e.document,F=0,W=0,z=n(),U=n(),X=n(),K=function(e,t){return e===t&&(L=!0),0},V={}.hasOwnProperty,Y=[],G=Y.pop,J=Y.push,Q=Y.push,Z=Y.slice,ee=function(e,t){for(var n=0,r=e.length;n+~]|"+ne+")"+ne+"*"),ce=new RegExp("="+ne+"*([^\\]'\"]*?)"+ne+"*\\]","g"),fe=new RegExp(ie),pe=new RegExp("^"+re+"$"),de={ID:new RegExp("^#("+re+")"),CLASS:new RegExp("^\\.("+re+")"),TAG:new RegExp("^("+re+"|[*])"),ATTR:new RegExp("^"+oe),PSEUDO:new RegExp("^"+ie),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+ne+"*(even|odd|(([+-]|)(\\d*)n|)"+ne+"*(?:([+-]|)"+ne+"*(\\d+)|))"+ne+"*\\)|)","i"),bool:new RegExp("^(?:"+te+")$","i"),needsContext:new RegExp("^"+ne+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+ne+"*((?:-\\d)?\\d*)"+ne+"*\\)|)(?=[^-]|$)","i")},he=/^(?:input|select|textarea|button)$/i,ye=/^h\d$/i,ge=/^[^{]+\{\s*\[native \w/,me=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ve=/[+~]/,be=new RegExp("\\\\([\\da-f]{1,6}"+ne+"?|("+ne+")|.)","ig"),xe=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:r<0?String.fromCharCode(r+65536):String.fromCharCode(r>>10|55296,1023&r|56320)},we=/([\0-\x1f\x7f]|^-?\d)|^-$|[^\0-\x1f\x7f-\uFFFF\w-]/g,Te=function(e,t){return t?"\0"===e?"�":e.slice(0,-1)+"\\"+e.charCodeAt(e.length-1).toString(16)+" ":"\\"+e},ke=function(){q()},Ce=h(function(e){return e.disabled===!0&&("form"in e||"label"in e)},{dir:"parentNode",next:"legend"});try{Q.apply(Y=Z.call($.childNodes),$.childNodes),Y[$.childNodes.length].nodeType}catch(e){Q={apply:Y.length?function(e,t){J.apply(e,Z.call(t))}:function(e,t){ 2 | for(var n=e.length,r=0;e[n++]=t[r++];);e.length=n-1}}}T=t.support={},E=t.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return!!t&&"HTML"!==t.nodeName},q=t.setDocument=function(e){var t,n,r=e?e.ownerDocument||e:$;return r!==M&&9===r.nodeType&&r.documentElement?(M=r,H=M.documentElement,P=!E(M),$!==M&&(n=M.defaultView)&&n.top!==n&&(n.addEventListener?n.addEventListener("unload",ke,!1):n.attachEvent&&n.attachEvent("onunload",ke)),T.attributes=o(function(e){return e.className="i",!e.getAttribute("className")}),T.getElementsByTagName=o(function(e){return e.appendChild(M.createComment("")),!e.getElementsByTagName("*").length}),T.getElementsByClassName=ge.test(M.getElementsByClassName),T.getById=o(function(e){return H.appendChild(e).id=_,!M.getElementsByName||!M.getElementsByName(_).length}),T.getById?(k.filter.ID=function(e){var t=e.replace(be,xe);return function(e){return e.getAttribute("id")===t}},k.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&P){var n=t.getElementById(e);return n?[n]:[]}}):(k.filter.ID=function(e){var t=e.replace(be,xe);return function(e){var n="undefined"!=typeof e.getAttributeNode&&e.getAttributeNode("id");return n&&n.value===t}},k.find.ID=function(e,t){if("undefined"!=typeof t.getElementById&&P){var n,r,o,i=t.getElementById(e);if(i){if(n=i.getAttributeNode("id"),n&&n.value===e)return[i];for(o=t.getElementsByName(e),r=0;i=o[r++];)if(n=i.getAttributeNode("id"),n&&n.value===e)return[i]}return[]}}),k.find.TAG=T.getElementsByTagName?function(e,t){return"undefined"!=typeof t.getElementsByTagName?t.getElementsByTagName(e):T.qsa?t.querySelectorAll(e):void 0}:function(e,t){var n,r=[],o=0,i=t.getElementsByTagName(e);if("*"===e){for(;n=i[o++];)1===n.nodeType&&r.push(n);return r}return i},k.find.CLASS=T.getElementsByClassName&&function(e,t){if("undefined"!=typeof t.getElementsByClassName&&P)return t.getElementsByClassName(e)},I=[],O=[],(T.qsa=ge.test(M.querySelectorAll))&&(o(function(e){H.appendChild(e).innerHTML="",e.querySelectorAll("[msallowcapture^='']").length&&O.push("[*^$]="+ne+"*(?:''|\"\")"),e.querySelectorAll("[selected]").length||O.push("\\["+ne+"*(?:value|"+te+")"),e.querySelectorAll("[id~="+_+"-]").length||O.push("~="),e.querySelectorAll(":checked").length||O.push(":checked"),e.querySelectorAll("a#"+_+"+*").length||O.push(".#.+[+~]")}),o(function(e){e.innerHTML="";var t=M.createElement("input");t.setAttribute("type","hidden"),e.appendChild(t).setAttribute("name","D"),e.querySelectorAll("[name=d]").length&&O.push("name"+ne+"*[*^$|!~]?="),2!==e.querySelectorAll(":enabled").length&&O.push(":enabled",":disabled"),H.appendChild(e).disabled=!0,2!==e.querySelectorAll(":disabled").length&&O.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),O.push(",.*:")})),(T.matchesSelector=ge.test(R=H.matches||H.webkitMatchesSelector||H.mozMatchesSelector||H.oMatchesSelector||H.msMatchesSelector))&&o(function(e){T.disconnectedMatch=R.call(e,"*"),R.call(e,"[s!='']:x"),I.push("!=",ie)}),O=O.length&&new RegExp(O.join("|")),I=I.length&&new RegExp(I.join("|")),t=ge.test(H.compareDocumentPosition),B=t||ge.test(H.contains)?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)for(;t=t.parentNode;)if(t===e)return!0;return!1},K=t?function(e,t){if(e===t)return L=!0,0;var n=!e.compareDocumentPosition-!t.compareDocumentPosition;return n?n:(n=(e.ownerDocument||e)===(t.ownerDocument||t)?e.compareDocumentPosition(t):1,1&n||!T.sortDetached&&t.compareDocumentPosition(e)===n?e===M||e.ownerDocument===$&&B($,e)?-1:t===M||t.ownerDocument===$&&B($,t)?1:j?ee(j,e)-ee(j,t):0:4&n?-1:1)}:function(e,t){if(e===t)return L=!0,0;var n,r=0,o=e.parentNode,i=t.parentNode,s=[e],u=[t];if(!o||!i)return e===M?-1:t===M?1:o?-1:i?1:j?ee(j,e)-ee(j,t):0;if(o===i)return a(e,t);for(n=e;n=n.parentNode;)s.unshift(n);for(n=t;n=n.parentNode;)u.unshift(n);for(;s[r]===u[r];)r++;return r?a(s[r],u[r]):s[r]===$?-1:u[r]===$?1:0},M):M},t.matches=function(e,n){return t(e,null,null,n)},t.matchesSelector=function(e,n){if((e.ownerDocument||e)!==M&&q(e),n=n.replace(ce,"='$1']"),T.matchesSelector&&P&&!X[n+" "]&&(!I||!I.test(n))&&(!O||!O.test(n)))try{var r=R.call(e,n);if(r||T.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(e){}return t(n,M,null,[e]).length>0},t.contains=function(e,t){return(e.ownerDocument||e)!==M&&q(e),B(e,t)},t.attr=function(e,t){(e.ownerDocument||e)!==M&&q(e);var n=k.attrHandle[t.toLowerCase()],r=n&&V.call(k.attrHandle,t.toLowerCase())?n(e,t,!P):void 0;return void 0!==r?r:T.attributes||!P?e.getAttribute(t):(r=e.getAttributeNode(t))&&r.specified?r.value:null},t.escape=function(e){return(e+"").replace(we,Te)},t.error=function(e){throw new Error("Syntax error, unrecognized expression: "+e)},t.uniqueSort=function(e){var t,n=[],r=0,o=0;if(L=!T.detectDuplicates,j=!T.sortStable&&e.slice(0),e.sort(K),L){for(;t=e[o++];)t===e[o]&&(r=n.push(o));for(;r--;)e.splice(n[r],1)}return j=null,e},C=t.getText=function(e){var t,n="",r=0,o=e.nodeType;if(o){if(1===o||9===o||11===o){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=C(e)}else if(3===o||4===o)return e.nodeValue}else for(;t=e[r++];)n+=C(t);return n},k=t.selectors={cacheLength:50,createPseudo:r,match:de,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(be,xe),e[3]=(e[3]||e[4]||e[5]||"").replace(be,xe),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||t.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&t.error(e[0]),e},PSEUDO:function(e){var t,n=!e[6]&&e[2];return de.CHILD.test(e[0])?null:(e[3]?e[2]=e[4]||e[5]||"":n&&fe.test(n)&&(t=S(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(be,xe).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=z[e+" "];return t||(t=new RegExp("(^|"+ne+")"+e+"("+ne+"|$)"))&&z(e,function(e){return t.test("string"==typeof e.className&&e.className||"undefined"!=typeof e.getAttribute&&e.getAttribute("class")||"")})},ATTR:function(e,n,r){return function(o){var i=t.attr(o,e);return null==i?"!="===n:!n||(i+="","="===n?i===r:"!="===n?i!==r:"^="===n?r&&0===i.indexOf(r):"*="===n?r&&i.indexOf(r)>-1:"$="===n?r&&i.slice(-r.length)===r:"~="===n?(" "+i.replace(ae," ")+" ").indexOf(r)>-1:"|="===n&&(i===r||i.slice(0,r.length+1)===r+"-"))}},CHILD:function(e,t,n,r,o){var i="nth"!==e.slice(0,3),a="last"!==e.slice(-4),s="of-type"===t;return 1===r&&0===o?function(e){return!!e.parentNode}:function(t,n,u){var l,c,f,p,d,h,y=i!==a?"nextSibling":"previousSibling",g=t.parentNode,m=s&&t.nodeName.toLowerCase(),v=!u&&!s,b=!1;if(g){if(i){for(;y;){for(p=t;p=p[y];)if(s?p.nodeName.toLowerCase()===m:1===p.nodeType)return!1;h=y="only"===e&&!h&&"nextSibling"}return!0}if(h=[a?g.firstChild:g.lastChild],a&&v){for(p=g,f=p[_]||(p[_]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),l=c[e]||[],d=l[0]===F&&l[1],b=d&&l[2],p=d&&g.childNodes[d];p=++d&&p&&p[y]||(b=d=0)||h.pop();)if(1===p.nodeType&&++b&&p===t){c[e]=[F,d,b];break}}else if(v&&(p=t,f=p[_]||(p[_]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),l=c[e]||[],d=l[0]===F&&l[1],b=d),b===!1)for(;(p=++d&&p&&p[y]||(b=d=0)||h.pop())&&((s?p.nodeName.toLowerCase()!==m:1!==p.nodeType)||!++b||(v&&(f=p[_]||(p[_]={}),c=f[p.uniqueID]||(f[p.uniqueID]={}),c[e]=[F,b]),p!==t)););return b-=o,b===r||b%r===0&&b/r>=0}}},PSEUDO:function(e,n){var o,i=k.pseudos[e]||k.setFilters[e.toLowerCase()]||t.error("unsupported pseudo: "+e);return i[_]?i(n):i.length>1?(o=[e,e,"",n],k.setFilters.hasOwnProperty(e.toLowerCase())?r(function(e,t){for(var r,o=i(e,n),a=o.length;a--;)r=ee(e,o[a]),e[r]=!(t[r]=o[a])}):function(e){return i(e,0,o)}):i}},pseudos:{not:r(function(e){var t=[],n=[],o=N(e.replace(se,"$1"));return o[_]?r(function(e,t,n,r){for(var i,a=o(e,null,r,[]),s=e.length;s--;)(i=a[s])&&(e[s]=!(t[s]=i))}):function(e,r,i){return t[0]=e,o(t,null,i,n),t[0]=null,!n.pop()}}),has:r(function(e){return function(n){return t(e,n).length>0}}),contains:r(function(e){return e=e.replace(be,xe),function(t){return(t.textContent||t.innerText||C(t)).indexOf(e)>-1}}),lang:r(function(e){return pe.test(e||"")||t.error("unsupported lang: "+e),e=e.replace(be,xe).toLowerCase(),function(t){var n;do if(n=P?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===H},focus:function(e){return e===M.activeElement&&(!M.hasFocus||M.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:l(!1),disabled:l(!0),checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeType<6)return!1;return!0},parent:function(e){return!k.pseudos.empty(e)},header:function(e){return ye.test(e.nodeName)},input:function(e){return he.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||"text"===t.toLowerCase())},first:c(function(){return[0]}),last:c(function(e,t){return[t-1]}),eq:c(function(e,t,n){return[n<0?n+t:n]}),even:c(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:c(function(e,t,n){for(var r=n<0?n+t:n;++r2&&"ID"===(a=i[0]).type&&9===t.nodeType&&P&&k.relative[i[1].type]){if(t=(k.find.ID(a.matches[0].replace(be,xe),t)||[])[0],!t)return n;l&&(t=t.parentNode),e=e.slice(i.shift().value.length)}for(o=de.needsContext.test(e)?0:i.length;o--&&(a=i[o],!k.relative[s=a.type]);)if((u=k.find[s])&&(r=u(a.matches[0].replace(be,xe),ve.test(i[0].type)&&f(t.parentNode)||t))){if(i.splice(o,1),e=r.length&&d(i),!e)return Q.apply(n,r),n;break}}return(l||N(e,c))(r,t,!P,n,!t||ve.test(e)&&f(t.parentNode)||t),n},T.sortStable=_.split("").sort(K).join("")===_,T.detectDuplicates=!!L,q(),T.sortDetached=o(function(e){return 1&e.compareDocumentPosition(M.createElement("fieldset"))}),o(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||i("type|href|height|width",function(e,t,n){if(!n)return e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),T.attributes&&o(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||i("value",function(e,t,n){if(!n&&"input"===e.nodeName.toLowerCase())return e.defaultValue}),o(function(e){return null==e.getAttribute("disabled")})||i(te,function(e,t,n){var r;if(!n)return e[t]===!0?t.toLowerCase():(r=e.getAttributeNode(t))&&r.specified?r.value:null}),t}(e);Te.find=Ce,Te.expr=Ce.selectors,Te.expr[":"]=Te.expr.pseudos,Te.uniqueSort=Te.unique=Ce.uniqueSort,Te.text=Ce.getText,Te.isXMLDoc=Ce.isXML,Te.contains=Ce.contains,Te.escapeSelector=Ce.escape;var Ee=function(e,t,n){for(var r=[],o=void 0!==n;(e=e[t])&&9!==e.nodeType;)if(1===e.nodeType){if(o&&Te(e).is(n))break;r.push(e)}return r},Se=function(e,t){for(var n=[];e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n},Ne=Te.expr.match.needsContext,Ae=/^<([a-z][^\/\0>:\x20\t\r\n\f]*)[\x20\t\r\n\f]*\/?>(?:<\/\1>|)$/i;Te.filter=function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?Te.find.matchesSelector(r,e)?[r]:[]:Te.find.matches(e,Te.grep(t,function(e){return 1===e.nodeType}))},Te.fn.extend({find:function(e){var t,n,r=this.length,o=this;if("string"!=typeof e)return this.pushStack(Te(e).filter(function(){for(t=0;t1?Te.uniqueSort(n):n},filter:function(e){return this.pushStack(a(this,e||[],!1))},not:function(e){return this.pushStack(a(this,e||[],!0))},is:function(e){return!!a(this,"string"==typeof e&&Ne.test(e)?Te(e):e||[],!1).length}});var De,je=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]+))$/,Le=Te.fn.init=function(e,t,n){var r,o;if(!e)return this;if(n=n||De,"string"==typeof e){if(r="<"===e[0]&&">"===e[e.length-1]&&e.length>=3?[null,e,null]:je.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof Te?t[0]:t,Te.merge(this,Te.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:ae,!0)),Ae.test(r[1])&&Te.isPlainObject(t))for(r in t)ve(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return o=ae.getElementById(r[2]),o&&(this[0]=o,this.length=1),this}return e.nodeType?(this[0]=e,this.length=1,this):ve(e)?void 0!==n.ready?n.ready(e):e(Te):Te.makeArray(e,this)};Le.prototype=Te.fn,De=Te(ae);var qe=/^(?:parents|prev(?:Until|All))/,Me={children:!0,contents:!0,next:!0,prev:!0};Te.fn.extend({has:function(e){var t=Te(e,this),n=t.length;return this.filter(function(){for(var e=0;e-1:1===n.nodeType&&Te.find.matchesSelector(n,e))){i.push(n);break}return this.pushStack(i.length>1?Te.uniqueSort(i):i)},index:function(e){return e?"string"==typeof e?fe.call(Te(e),this[0]):fe.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){return this.pushStack(Te.uniqueSort(Te.merge(this.get(),Te(e,t))))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}}),Te.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return Ee(e,"parentNode")},parentsUntil:function(e,t,n){return Ee(e,"parentNode",n)},next:function(e){return s(e,"nextSibling")},prev:function(e){return s(e,"previousSibling")},nextAll:function(e){return Ee(e,"nextSibling")},prevAll:function(e){return Ee(e,"previousSibling")},nextUntil:function(e,t,n){return Ee(e,"nextSibling",n)},prevUntil:function(e,t,n){return Ee(e,"previousSibling",n)},siblings:function(e){return Se((e.parentNode||{}).firstChild,e)},children:function(e){return Se(e.firstChild)},contents:function(e){return i(e,"iframe")?e.contentDocument:(i(e,"template")&&(e=e.content||e),Te.merge([],e.childNodes))}},function(e,t){Te.fn[e]=function(n,r){var o=Te.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(o=Te.filter(r,o)),this.length>1&&(Me[e]||Te.uniqueSort(o),qe.test(e)&&o.reverse()),this.pushStack(o)}});var He=/[^\x20\t\r\n\f]+/g;Te.Callbacks=function(e){e="string"==typeof e?u(e):Te.extend({},e);var t,n,o,i,a=[],s=[],l=-1,c=function(){for(i=i||e.once,o=t=!0;s.length;l=-1)for(n=s.shift();++l-1;)a.splice(n,1),n<=l&&l--}),this},has:function(e){return e?Te.inArray(e,a)>-1:a.length>0},empty:function(){return a&&(a=[]),this},disable:function(){return i=s=[],a=n="",this},disabled:function(){return!a},lock:function(){return i=s=[],n||t||(a=n=""),this},locked:function(){return!!i},fireWith:function(e,n){return i||(n=n||[],n=[e,n.slice?n.slice():n],s.push(n),t||c()),this},fire:function(){return f.fireWith(this,arguments),this},fired:function(){return!!o}};return f},Te.extend({Deferred:function(t){var n=[["notify","progress",Te.Callbacks("memory"),Te.Callbacks("memory"),2],["resolve","done",Te.Callbacks("once memory"),Te.Callbacks("once memory"),0,"resolved"],["reject","fail",Te.Callbacks("once memory"),Te.Callbacks("once memory"),1,"rejected"]],r="pending",o={state:function(){return r},always:function(){return i.done(arguments).fail(arguments),this},catch:function(e){return o.then(null,e)},pipe:function(){var e=arguments;return Te.Deferred(function(t){Te.each(n,function(n,r){var o=ve(e[r[4]])&&e[r[4]];i[r[1]](function(){var e=o&&o.apply(this,arguments);e&&ve(e.promise)?e.promise().progress(t.notify).done(t.resolve).fail(t.reject):t[r[0]+"With"](this,o?[e]:arguments)})}),e=null}).promise()},then:function(t,r,o){function i(t,n,r,o){return function(){var s=this,u=arguments,f=function(){var e,f;if(!(t=a&&(r!==c&&(s=void 0,u=[e]),n.rejectWith(s,u))}};t?p():(Te.Deferred.getStackHook&&(p.stackTrace=Te.Deferred.getStackHook()),e.setTimeout(p))}}var a=0;return Te.Deferred(function(e){n[0][3].add(i(0,e,ve(o)?o:l,e.notifyWith)),n[1][3].add(i(0,e,ve(t)?t:l)),n[2][3].add(i(0,e,ve(r)?r:c))}).promise()},promise:function(e){return null!=e?Te.extend(e,o):o}},i={};return Te.each(n,function(e,t){var a=t[2],s=t[5];o[t[1]]=a.add,s&&a.add(function(){r=s},n[3-e][2].disable,n[3-e][3].disable,n[0][2].lock,n[0][3].lock),a.add(t[3].fire),i[t[0]]=function(){return i[t[0]+"With"](this===i?void 0:this,arguments),this},i[t[0]+"With"]=a.fireWith}),o.promise(i),t&&t.call(i,i),i},when:function(e){var t=arguments.length,n=t,r=Array(n),o=ue.call(arguments),i=Te.Deferred(),a=function(e){return function(n){r[e]=this,o[e]=arguments.length>1?ue.call(arguments):n,--t||i.resolveWith(r,o)}};if(t<=1&&(f(e,i.done(a(n)).resolve,i.reject,!t),"pending"===i.state()||ve(o[n]&&o[n].then)))return i.then();for(;n--;)f(o[n],a(n),i.reject);return i.promise()}});var Pe=/^(Eval|Internal|Range|Reference|Syntax|Type|URI)Error$/;Te.Deferred.exceptionHook=function(t,n){e.console&&e.console.warn&&t&&Pe.test(t.name)&&e.console.warn("jQuery.Deferred exception: "+t.message,t.stack,n)},Te.readyException=function(t){e.setTimeout(function(){throw t})};var Oe=Te.Deferred();Te.fn.ready=function(e){return Oe.then(e).catch(function(e){Te.readyException(e)}),this},Te.extend({isReady:!1,readyWait:1,ready:function(e){(e===!0?--Te.readyWait:Te.isReady)||(Te.isReady=!0,e!==!0&&--Te.readyWait>0||Oe.resolveWith(ae,[Te]))}}),Te.ready.then=Oe.then,"complete"===ae.readyState||"loading"!==ae.readyState&&!ae.documentElement.doScroll?e.setTimeout(Te.ready):(ae.addEventListener("DOMContentLoaded",p),e.addEventListener("load",p));var Ie=function(e,t,n,o,i,a,s){var u=0,l=e.length,c=null==n;if("object"===r(n)){i=!0;for(u in n)Ie(e,t,u,n[u],!0,a,s)}else if(void 0!==o&&(i=!0,ve(o)||(s=!0),c&&(s?(t.call(e,o),t=null):(c=t,t=function(e,t,n){return c.call(Te(e),n)})),t))for(;u1,null,!0)},removeData:function(e){return this.each(function(){Fe.remove(this,e)})}}),Te.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=$e.get(e,t),n&&(!r||Array.isArray(n)?r=$e.access(e,t,Te.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=Te.queue(e,t),r=n.length,o=n.shift(),i=Te._queueHooks(e,t),a=function(){Te.dequeue(e,t)};"inprogress"===o&&(o=n.shift(),r--),o&&("fx"===t&&n.unshift("inprogress"),delete i.stop,o.call(e,a,i)),!r&&i&&i.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return $e.get(e,n)||$e.access(e,n,{empty:Te.Callbacks("once memory").add(function(){$e.remove(e,[t+"queue",n])})})}}),Te.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),arguments.length\x20\t\r\n\f]+)/i,Ze=/^$|^module$|\/(?:java|ecma)script/i,et={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};et.optgroup=et.option,et.tbody=et.tfoot=et.colgroup=et.caption=et.thead,et.th=et.td;var tt=/<|&#?\w+;/;!function(){var e=ae.createDocumentFragment(),t=e.appendChild(ae.createElement("div")),n=ae.createElement("input");n.setAttribute("type","radio"),n.setAttribute("checked","checked"),n.setAttribute("name","t"),t.appendChild(n),me.checkClone=t.cloneNode(!0).cloneNode(!0).lastChild.checked,t.innerHTML="",me.noCloneChecked=!!t.cloneNode(!0).lastChild.defaultValue}();var nt=ae.documentElement,rt=/^key/,ot=/^(?:mouse|pointer|contextmenu|drag|drop)|click/,it=/^([^.]*)(?:\.(.+)|)/;Te.event={global:{},add:function(e,t,n,r,o){var i,a,s,u,l,c,f,p,d,h,y,g=$e.get(e);if(g)for(n.handler&&(i=n,n=i.handler,o=i.selector),o&&Te.find.matchesSelector(nt,o),n.guid||(n.guid=Te.guid++),(u=g.events)||(u=g.events={}),(a=g.handle)||(a=g.handle=function(t){return"undefined"!=typeof Te&&Te.event.triggered!==t.type?Te.event.dispatch.apply(e,arguments):void 0}),t=(t||"").match(He)||[""],l=t.length;l--;)s=it.exec(t[l])||[],d=y=s[1],h=(s[2]||"").split(".").sort(),d&&(f=Te.event.special[d]||{},d=(o?f.delegateType:f.bindType)||d,f=Te.event.special[d]||{},c=Te.extend({type:d,origType:y,data:r,handler:n,guid:n.guid,selector:o,needsContext:o&&Te.expr.match.needsContext.test(o),namespace:h.join(".")},i),(p=u[d])||(p=u[d]=[],p.delegateCount=0,f.setup&&f.setup.call(e,r,h,a)!==!1||e.addEventListener&&e.addEventListener(d,a)),f.add&&(f.add.call(e,c),c.handler.guid||(c.handler.guid=n.guid)),o?p.splice(p.delegateCount++,0,c):p.push(c),Te.event.global[d]=!0)},remove:function(e,t,n,r,o){var i,a,s,u,l,c,f,p,d,h,y,g=$e.hasData(e)&&$e.get(e);if(g&&(u=g.events)){for(t=(t||"").match(He)||[""],l=t.length;l--;)if(s=it.exec(t[l])||[],d=y=s[1],h=(s[2]||"").split(".").sort(),d){for(f=Te.event.special[d]||{},d=(r?f.delegateType:f.bindType)||d,p=u[d]||[],s=s[2]&&new RegExp("(^|\\.)"+h.join("\\.(?:.*\\.|)")+"(\\.|$)"),a=i=p.length;i--;)c=p[i],!o&&y!==c.origType||n&&n.guid!==c.guid||s&&!s.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(p.splice(i,1),c.selector&&p.delegateCount--,f.remove&&f.remove.call(e,c));a&&!p.length&&(f.teardown&&f.teardown.call(e,h,g.handle)!==!1||Te.removeEvent(e,d,g.handle),delete u[d])}else for(d in u)Te.event.remove(e,d+t[l],n,r,!0);Te.isEmptyObject(u)&&$e.remove(e,"handle events")}},dispatch:function(e){var t,n,r,o,i,a,s=Te.event.fix(e),u=new Array(arguments.length),l=($e.get(this,"events")||{})[s.type]||[],c=Te.event.special[s.type]||{};for(u[0]=s,t=1;t=1))for(;l!==this;l=l.parentNode||this)if(1===l.nodeType&&("click"!==e.type||l.disabled!==!0)){for(i=[],a={},n=0;n-1:Te.find(o,this,null,[l]).length),a[o]&&i.push(r);i.length&&s.push({elem:l,handlers:i})}return l=this,u\x20\t\r\n\f]*)[^>]*)\/>/gi,st=/\s*$/g;Te.extend({htmlPrefilter:function(e){return e.replace(at,"<$1>")},clone:function(e,t,n){var r,o,i,a,s=e.cloneNode(!0),u=Te.contains(e.ownerDocument,e); 3 | if(!(me.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||Te.isXMLDoc(e)))for(a=w(s),i=w(e),r=0,o=i.length;r0&&T(a,!u&&w(e,"script")),s},cleanData:function(e){for(var t,n,r,o=Te.event.special,i=0;void 0!==(n=e[i]);i++)if(_e(n)){if(t=n[$e.expando]){if(t.events)for(r in t.events)o[r]?Te.event.remove(n,r):Te.removeEvent(n,r,t.handle);n[$e.expando]=void 0}n[Fe.expando]&&(n[Fe.expando]=void 0)}}}),Te.fn.extend({detach:function(e){return H(this,e,!0)},remove:function(e){return H(this,e)},text:function(e){return Ie(this,function(e){return void 0===e?Te.text(this):this.empty().each(function(){1!==this.nodeType&&11!==this.nodeType&&9!==this.nodeType||(this.textContent=e)})},null,e,arguments.length)},append:function(){return M(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=A(this,e);t.appendChild(e)}})},prepend:function(){return M(this,arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=A(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return M(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return M(this,arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},empty:function(){for(var e,t=0;null!=(e=this[t]);t++)1===e.nodeType&&(Te.cleanData(w(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null!=e&&e,t=null==t?e:t,this.map(function(){return Te.clone(this,e,t)})},html:function(e){return Ie(this,function(e){var t=this[0]||{},n=0,r=this.length;if(void 0===e&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!st.test(e)&&!et[(Qe.exec(e)||["",""])[1].toLowerCase()]){e=Te.htmlPrefilter(e);try{for(;n1)}}),Te.Tween=F,F.prototype={constructor:F,init:function(e,t,n,r,o,i){this.elem=e,this.prop=n,this.easing=o||Te.easing._default,this.options=t,this.start=this.now=this.cur(),this.end=r,this.unit=i||(Te.cssNumber[n]?"":"px")},cur:function(){var e=F.propHooks[this.prop];return e&&e.get?e.get(this):F.propHooks._default.get(this)},run:function(e){var t,n=F.propHooks[this.prop];return this.options.duration?this.pos=t=Te.easing[this.easing](e,this.options.duration*e,0,1,this.options.duration):this.pos=t=e,this.now=(this.end-this.start)*t+this.start,this.options.step&&this.options.step.call(this.elem,this.now,this),n&&n.set?n.set(this):F.propHooks._default.set(this),this}},F.prototype.init.prototype=F.prototype,F.propHooks={_default:{get:function(e){var t;return 1!==e.elem.nodeType||null!=e.elem[e.prop]&&null==e.elem.style[e.prop]?e.elem[e.prop]:(t=Te.css(e.elem,e.prop,""),t&&"auto"!==t?t:0)},set:function(e){Te.fx.step[e.prop]?Te.fx.step[e.prop](e):1!==e.elem.nodeType||null==e.elem.style[Te.cssProps[e.prop]]&&!Te.cssHooks[e.prop]?e.elem[e.prop]=e.now:Te.style(e.elem,e.prop,e.now+e.unit)}}},F.propHooks.scrollTop=F.propHooks.scrollLeft={set:function(e){e.elem.nodeType&&e.elem.parentNode&&(e.elem[e.prop]=e.now)}},Te.easing={linear:function(e){return e},swing:function(e){return.5-Math.cos(e*Math.PI)/2},_default:"swing"},Te.fx=F.prototype.init,Te.fx.step={};var bt,xt,wt=/^(?:toggle|show|hide)$/,Tt=/queueHooks$/;Te.Animation=Te.extend(Y,{tweeners:{"*":[function(e,t){var n=this.createTween(e,t);return v(n.elem,e,Xe.exec(t),n),n}]},tweener:function(e,t){ve(e)?(t=e,e=["*"]):e=e.match(He);for(var n,r=0,o=e.length;r1)},removeAttr:function(e){return this.each(function(){Te.removeAttr(this,e)})}}),Te.extend({attr:function(e,t,n){var r,o,i=e.nodeType;if(3!==i&&8!==i&&2!==i)return"undefined"==typeof e.getAttribute?Te.prop(e,t,n):(1===i&&Te.isXMLDoc(e)||(o=Te.attrHooks[t.toLowerCase()]||(Te.expr.match.bool.test(t)?kt:void 0)),void 0!==n?null===n?void Te.removeAttr(e,t):o&&"set"in o&&void 0!==(r=o.set(e,n,t))?r:(e.setAttribute(t,n+""),n):o&&"get"in o&&null!==(r=o.get(e,t))?r:(r=Te.find.attr(e,t),null==r?void 0:r))},attrHooks:{type:{set:function(e,t){if(!me.radioValue&&"radio"===t&&i(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},removeAttr:function(e,t){var n,r=0,o=t&&t.match(He);if(o&&1===e.nodeType)for(;n=o[r++];)e.removeAttribute(n)}}),kt={set:function(e,t,n){return t===!1?Te.removeAttr(e,n):e.setAttribute(n,n),n}},Te.each(Te.expr.match.bool.source.match(/\w+/g),function(e,t){var n=Ct[t]||Te.find.attr;Ct[t]=function(e,t,r){var o,i,a=t.toLowerCase();return r||(i=Ct[a],Ct[a]=o,o=null!=n(e,t,r)?a:null,Ct[a]=i),o}});var Et=/^(?:input|select|textarea|button)$/i,St=/^(?:a|area)$/i;Te.fn.extend({prop:function(e,t){return Ie(this,Te.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[Te.propFix[e]||e]})}}),Te.extend({prop:function(e,t,n){var r,o,i=e.nodeType;if(3!==i&&8!==i&&2!==i)return 1===i&&Te.isXMLDoc(e)||(t=Te.propFix[t]||t,o=Te.propHooks[t]),void 0!==n?o&&"set"in o&&void 0!==(r=o.set(e,n,t))?r:e[t]=n:o&&"get"in o&&null!==(r=o.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){var t=Te.find.attr(e,"tabindex");return t?parseInt(t,10):Et.test(e.nodeName)||St.test(e.nodeName)&&e.href?0:-1}}},propFix:{for:"htmlFor",class:"className"}}),me.optSelected||(Te.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null},set:function(e){var t=e.parentNode;t&&(t.selectedIndex,t.parentNode&&t.parentNode.selectedIndex)}}),Te.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){Te.propFix[this.toLowerCase()]=this}),Te.fn.extend({addClass:function(e){var t,n,r,o,i,a,s,u=0;if(ve(e))return this.each(function(t){Te(this).addClass(e.call(this,t,J(this)))});if(t=Q(e),t.length)for(;n=this[u++];)if(o=J(n),r=1===n.nodeType&&" "+G(o)+" "){for(a=0;i=t[a++];)r.indexOf(" "+i+" ")<0&&(r+=i+" ");s=G(r),o!==s&&n.setAttribute("class",s)}return this},removeClass:function(e){var t,n,r,o,i,a,s,u=0;if(ve(e))return this.each(function(t){Te(this).removeClass(e.call(this,t,J(this)))});if(!arguments.length)return this.attr("class","");if(t=Q(e),t.length)for(;n=this[u++];)if(o=J(n),r=1===n.nodeType&&" "+G(o)+" "){for(a=0;i=t[a++];)for(;r.indexOf(" "+i+" ")>-1;)r=r.replace(" "+i+" "," ");s=G(r),o!==s&&n.setAttribute("class",s)}return this},toggleClass:function(e,t){var n=typeof e,r="string"===n||Array.isArray(e);return"boolean"==typeof t&&r?t?this.addClass(e):this.removeClass(e):ve(e)?this.each(function(n){Te(this).toggleClass(e.call(this,n,J(this),t),t)}):this.each(function(){var t,o,i,a;if(r)for(o=0,i=Te(this),a=Q(e);t=a[o++];)i.hasClass(t)?i.removeClass(t):i.addClass(t);else void 0!==e&&"boolean"!==n||(t=J(this),t&&$e.set(this,"__className__",t),this.setAttribute&&this.setAttribute("class",t||e===!1?"":$e.get(this,"__className__")||""))})},hasClass:function(e){var t,n,r=0;for(t=" "+e+" ";n=this[r++];)if(1===n.nodeType&&(" "+G(J(n))+" ").indexOf(t)>-1)return!0;return!1}});var Nt=/\r/g;Te.fn.extend({val:function(e){var t,n,r,o=this[0];{if(arguments.length)return r=ve(e),this.each(function(n){var o;1===this.nodeType&&(o=r?e.call(this,n,Te(this).val()):e,null==o?o="":"number"==typeof o?o+="":Array.isArray(o)&&(o=Te.map(o,function(e){return null==e?"":e+""})),t=Te.valHooks[this.type]||Te.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&void 0!==t.set(this,o,"value")||(this.value=o))});if(o)return t=Te.valHooks[o.type]||Te.valHooks[o.nodeName.toLowerCase()],t&&"get"in t&&void 0!==(n=t.get(o,"value"))?n:(n=o.value,"string"==typeof n?n.replace(Nt,""):null==n?"":n)}}}),Te.extend({valHooks:{option:{get:function(e){var t=Te.find.attr(e,"value");return null!=t?t:G(Te.text(e))}},select:{get:function(e){var t,n,r,o=e.options,a=e.selectedIndex,s="select-one"===e.type,u=s?null:[],l=s?a+1:o.length;for(r=a<0?l:s?a:0;r-1)&&(n=!0);return n||(e.selectedIndex=-1),i}}}}),Te.each(["radio","checkbox"],function(){Te.valHooks[this]={set:function(e,t){if(Array.isArray(t))return e.checked=Te.inArray(Te(e).val(),t)>-1}},me.checkOn||(Te.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})}),me.focusin="onfocusin"in e;var At=/^(?:focusinfocus|focusoutblur)$/,Dt=function(e){e.stopPropagation()};Te.extend(Te.event,{trigger:function(t,n,r,o){var i,a,s,u,l,c,f,p,d=[r||ae],h=he.call(t,"type")?t.type:t,y=he.call(t,"namespace")?t.namespace.split("."):[];if(a=p=s=r=r||ae,3!==r.nodeType&&8!==r.nodeType&&!At.test(h+Te.event.triggered)&&(h.indexOf(".")>-1&&(y=h.split("."),h=y.shift(),y.sort()),l=h.indexOf(":")<0&&"on"+h,t=t[Te.expando]?t:new Te.Event(h,"object"==typeof t&&t),t.isTrigger=o?2:3,t.namespace=y.join("."),t.rnamespace=t.namespace?new RegExp("(^|\\.)"+y.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=void 0,t.target||(t.target=r),n=null==n?[t]:Te.makeArray(n,[t]),f=Te.event.special[h]||{},o||!f.trigger||f.trigger.apply(r,n)!==!1)){if(!o&&!f.noBubble&&!be(r)){for(u=f.delegateType||h,At.test(u+h)||(a=a.parentNode);a;a=a.parentNode)d.push(a),s=a;s===(r.ownerDocument||ae)&&d.push(s.defaultView||s.parentWindow||e)}for(i=0;(a=d[i++])&&!t.isPropagationStopped();)p=a,t.type=i>1?u:f.bindType||h,c=($e.get(a,"events")||{})[t.type]&&$e.get(a,"handle"),c&&c.apply(a,n),c=l&&a[l],c&&c.apply&&_e(a)&&(t.result=c.apply(a,n),t.result===!1&&t.preventDefault());return t.type=h,o||t.isDefaultPrevented()||f._default&&f._default.apply(d.pop(),n)!==!1||!_e(r)||l&&ve(r[h])&&!be(r)&&(s=r[l],s&&(r[l]=null),Te.event.triggered=h,t.isPropagationStopped()&&p.addEventListener(h,Dt),r[h](),t.isPropagationStopped()&&p.removeEventListener(h,Dt),Te.event.triggered=void 0,s&&(r[l]=s)),t.result}},simulate:function(e,t,n){var r=Te.extend(new Te.Event,n,{type:e,isSimulated:!0});Te.event.trigger(r,null,t)}}),Te.fn.extend({trigger:function(e,t){return this.each(function(){Te.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];if(n)return Te.event.trigger(e,t,n,!0)}}),me.focusin||Te.each({focus:"focusin",blur:"focusout"},function(e,t){var n=function(e){Te.event.simulate(t,e.target,Te.event.fix(e))};Te.event.special[t]={setup:function(){var r=this.ownerDocument||this,o=$e.access(r,t);o||r.addEventListener(e,n,!0),$e.access(r,t,(o||0)+1)},teardown:function(){var r=this.ownerDocument||this,o=$e.access(r,t)-1;o?$e.access(r,t,o):(r.removeEventListener(e,n,!0),$e.remove(r,t))}}});var jt=e.location,Lt=Date.now(),qt=/\?/;Te.parseXML=function(t){var n;if(!t||"string"!=typeof t)return null;try{n=(new e.DOMParser).parseFromString(t,"text/xml")}catch(e){n=void 0}return n&&!n.getElementsByTagName("parsererror").length||Te.error("Invalid XML: "+t),n};var Mt=/\[\]$/,Ht=/\r?\n/g,Pt=/^(?:submit|button|image|reset|file)$/i,Ot=/^(?:input|select|textarea|keygen)/i;Te.param=function(e,t){var n,r=[],o=function(e,t){var n=ve(t)?t():t;r[r.length]=encodeURIComponent(e)+"="+encodeURIComponent(null==n?"":n)};if(Array.isArray(e)||e.jquery&&!Te.isPlainObject(e))Te.each(e,function(){o(this.name,this.value)});else for(n in e)Z(n,e[n],t,o);return r.join("&")},Te.fn.extend({serialize:function(){return Te.param(this.serializeArray())},serializeArray:function(){return this.map(function(){var e=Te.prop(this,"elements");return e?Te.makeArray(e):this}).filter(function(){var e=this.type;return this.name&&!Te(this).is(":disabled")&&Ot.test(this.nodeName)&&!Pt.test(e)&&(this.checked||!Je.test(e))}).map(function(e,t){var n=Te(this).val();return null==n?null:Array.isArray(n)?Te.map(n,function(e){return{name:t.name,value:e.replace(Ht,"\r\n")}}):{name:t.name,value:n.replace(Ht,"\r\n")}}).get()}});var It=/%20/g,Rt=/#.*$/,Bt=/([?&])_=[^&]*/,_t=/^(.*?):[ \t]*([^\r\n]*)$/gm,$t=/^(?:about|app|app-storage|.+-extension|file|res|widget):$/,Ft=/^(?:GET|HEAD)$/,Wt=/^\/\//,zt={},Ut={},Xt="*/".concat("*"),Kt=ae.createElement("a");Kt.href=jt.href,Te.extend({active:0,lastModified:{},etag:{},ajaxSettings:{url:jt.href,type:"GET",isLocal:$t.test(jt.protocol),global:!0,processData:!0,async:!0,contentType:"application/x-www-form-urlencoded; charset=UTF-8",accepts:{"*":Xt,text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript"},contents:{xml:/\bxml\b/,html:/\bhtml/,json:/\bjson\b/},responseFields:{xml:"responseXML",text:"responseText",json:"responseJSON"},converters:{"* text":String,"text html":!0,"text json":JSON.parse,"text xml":Te.parseXML},flatOptions:{url:!0,context:!0}},ajaxSetup:function(e,t){return t?ne(ne(e,Te.ajaxSettings),t):ne(Te.ajaxSettings,e)},ajaxPrefilter:ee(zt),ajaxTransport:ee(Ut),ajax:function(t,n){function r(t,n,r,s){var l,p,d,x,w,T=n;c||(c=!0,u&&e.clearTimeout(u),o=void 0,a=s||"",k.readyState=t>0?4:0,l=t>=200&&t<300||304===t,r&&(x=re(h,k,r)),x=oe(h,x,k,l),l?(h.ifModified&&(w=k.getResponseHeader("Last-Modified"),w&&(Te.lastModified[i]=w),w=k.getResponseHeader("etag"),w&&(Te.etag[i]=w)),204===t||"HEAD"===h.type?T="nocontent":304===t?T="notmodified":(T=x.state,p=x.data,d=x.error,l=!d)):(d=T,!t&&T||(T="error",t<0&&(t=0))),k.status=t,k.statusText=(n||T)+"",l?m.resolveWith(y,[p,T,k]):m.rejectWith(y,[k,T,d]),k.statusCode(b),b=void 0,f&&g.trigger(l?"ajaxSuccess":"ajaxError",[k,h,l?p:d]),v.fireWith(y,[k,T]),f&&(g.trigger("ajaxComplete",[k,h]),--Te.active||Te.event.trigger("ajaxStop")))}"object"==typeof t&&(n=t,t=void 0),n=n||{};var o,i,a,s,u,l,c,f,p,d,h=Te.ajaxSetup({},n),y=h.context||h,g=h.context&&(y.nodeType||y.jquery)?Te(y):Te.event,m=Te.Deferred(),v=Te.Callbacks("once memory"),b=h.statusCode||{},x={},w={},T="canceled",k={readyState:0,getResponseHeader:function(e){var t;if(c){if(!s)for(s={};t=_t.exec(a);)s[t[1].toLowerCase()]=t[2];t=s[e.toLowerCase()]}return null==t?null:t},getAllResponseHeaders:function(){return c?a:null},setRequestHeader:function(e,t){return null==c&&(e=w[e.toLowerCase()]=w[e.toLowerCase()]||e,x[e]=t),this},overrideMimeType:function(e){return null==c&&(h.mimeType=e),this},statusCode:function(e){var t;if(e)if(c)k.always(e[k.status]);else for(t in e)b[t]=[b[t],e[t]];return this},abort:function(e){var t=e||T;return o&&o.abort(t),r(0,t),this}};if(m.promise(k),h.url=((t||h.url||jt.href)+"").replace(Wt,jt.protocol+"//"),h.type=n.method||n.type||h.method||h.type,h.dataTypes=(h.dataType||"*").toLowerCase().match(He)||[""],null==h.crossDomain){l=ae.createElement("a");try{l.href=h.url,l.href=l.href,h.crossDomain=Kt.protocol+"//"+Kt.host!=l.protocol+"//"+l.host}catch(e){h.crossDomain=!0}}if(h.data&&h.processData&&"string"!=typeof h.data&&(h.data=Te.param(h.data,h.traditional)),te(zt,h,n,k),c)return k;f=Te.event&&h.global,f&&0===Te.active++&&Te.event.trigger("ajaxStart"),h.type=h.type.toUpperCase(),h.hasContent=!Ft.test(h.type),i=h.url.replace(Rt,""),h.hasContent?h.data&&h.processData&&0===(h.contentType||"").indexOf("application/x-www-form-urlencoded")&&(h.data=h.data.replace(It,"+")):(d=h.url.slice(i.length),h.data&&(h.processData||"string"==typeof h.data)&&(i+=(qt.test(i)?"&":"?")+h.data,delete h.data),h.cache===!1&&(i=i.replace(Bt,"$1"),d=(qt.test(i)?"&":"?")+"_="+Lt++ +d),h.url=i+d),h.ifModified&&(Te.lastModified[i]&&k.setRequestHeader("If-Modified-Since",Te.lastModified[i]),Te.etag[i]&&k.setRequestHeader("If-None-Match",Te.etag[i])),(h.data&&h.hasContent&&h.contentType!==!1||n.contentType)&&k.setRequestHeader("Content-Type",h.contentType),k.setRequestHeader("Accept",h.dataTypes[0]&&h.accepts[h.dataTypes[0]]?h.accepts[h.dataTypes[0]]+("*"!==h.dataTypes[0]?", "+Xt+"; q=0.01":""):h.accepts["*"]);for(p in h.headers)k.setRequestHeader(p,h.headers[p]);if(h.beforeSend&&(h.beforeSend.call(y,k,h)===!1||c))return k.abort();if(T="abort",v.add(h.complete),k.done(h.success),k.fail(h.error),o=te(Ut,h,n,k)){if(k.readyState=1,f&&g.trigger("ajaxSend",[k,h]),c)return k;h.async&&h.timeout>0&&(u=e.setTimeout(function(){k.abort("timeout")},h.timeout));try{c=!1,o.send(x,r)}catch(e){if(c)throw e;r(-1,e)}}else r(-1,"No Transport");return k},getJSON:function(e,t,n){return Te.get(e,t,n,"json")},getScript:function(e,t){return Te.get(e,void 0,t,"script")}}),Te.each(["get","post"],function(e,t){Te[t]=function(e,n,r,o){return ve(n)&&(o=o||r,r=n,n=void 0),Te.ajax(Te.extend({url:e,type:t,dataType:o,data:n,success:r},Te.isPlainObject(e)&&e))}}),Te._evalUrl=function(e){return Te.ajax({url:e,type:"GET",dataType:"script",cache:!0,async:!1,global:!1,throws:!0})},Te.fn.extend({wrapAll:function(e){var t;return this[0]&&(ve(e)&&(e=e.call(this[0])),t=Te(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){for(var e=this;e.firstElementChild;)e=e.firstElementChild;return e}).append(this)),this},wrapInner:function(e){return ve(e)?this.each(function(t){Te(this).wrapInner(e.call(this,t))}):this.each(function(){var t=Te(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=ve(e);return this.each(function(n){Te(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(e){return this.parent(e).not("body").each(function(){Te(this).replaceWith(this.childNodes)}),this}}),Te.expr.pseudos.hidden=function(e){return!Te.expr.pseudos.visible(e)},Te.expr.pseudos.visible=function(e){return!!(e.offsetWidth||e.offsetHeight||e.getClientRects().length)},Te.ajaxSettings.xhr=function(){try{return new e.XMLHttpRequest}catch(e){}};var Vt={0:200,1223:204},Yt=Te.ajaxSettings.xhr();me.cors=!!Yt&&"withCredentials"in Yt,me.ajax=Yt=!!Yt,Te.ajaxTransport(function(t){var n,r;if(me.cors||Yt&&!t.crossDomain)return{send:function(o,i){var a,s=t.xhr();if(s.open(t.type,t.url,t.async,t.username,t.password),t.xhrFields)for(a in t.xhrFields)s[a]=t.xhrFields[a];t.mimeType&&s.overrideMimeType&&s.overrideMimeType(t.mimeType),t.crossDomain||o["X-Requested-With"]||(o["X-Requested-With"]="XMLHttpRequest");for(a in o)s.setRequestHeader(a,o[a]);n=function(e){return function(){n&&(n=r=s.onload=s.onerror=s.onabort=s.ontimeout=s.onreadystatechange=null,"abort"===e?s.abort():"error"===e?"number"!=typeof s.status?i(0,"error"):i(s.status,s.statusText):i(Vt[s.status]||s.status,s.statusText,"text"!==(s.responseType||"text")||"string"!=typeof s.responseText?{binary:s.response}:{text:s.responseText},s.getAllResponseHeaders()))}},s.onload=n(),r=s.onerror=s.ontimeout=n("error"),void 0!==s.onabort?s.onabort=r:s.onreadystatechange=function(){4===s.readyState&&e.setTimeout(function(){n&&r()})},n=n("abort");try{s.send(t.hasContent&&t.data||null)}catch(e){if(n)throw e}},abort:function(){n&&n()}}}),Te.ajaxPrefilter(function(e){e.crossDomain&&(e.contents.script=!1)}),Te.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/\b(?:java|ecma)script\b/},converters:{"text script":function(e){return Te.globalEval(e),e}}}),Te.ajaxPrefilter("script",function(e){void 0===e.cache&&(e.cache=!1),e.crossDomain&&(e.type="GET")}),Te.ajaxTransport("script",function(e){if(e.crossDomain){var t,n;return{send:function(r,o){t=Te(" 31 | 32 | 33 | -------------------------------------------------------------------------------- /opt/options.js: -------------------------------------------------------------------------------- 1 | // Saves options to chrome.storage 2 | function save_options() { 3 | var categoryBuildings = document.getElementById('categoryBuildings').value; 4 | var categoryFood = document.getElementById('categoryFood').value; 5 | var categoryNature = document.getElementById('categoryNature').value; 6 | var categoryPeople = document.getElementById('categoryPeople').value; 7 | var categoryTechnology = document.getElementById('categoryTechnology').value; 8 | var categoryObjects = document.getElementById('categoryObjects').value; 9 | var backgroundRefresh = document.getElementById('refresh').value; 10 | var lastFMusername = document.getElementById('lastFMusername').value; 11 | chrome.storage.sync.set({ 12 | categoryBuildings: categoryBuildings, 13 | categoryFood: categoryFood, 14 | categoryNature: categoryNature, 15 | categoryPeople: categoryPeople, 16 | categoryTechnology: categoryTechnology, 17 | categoryObjects: categoryObjects, 18 | backgroundRefresh: backgroundRefresh, 19 | lastFMusername: lastFMusername 20 | }, function() { 21 | // Update status to let user know options were saved. 22 | var status = document.getElementById('status'); 23 | status.textContent = 'Options saved!'; 24 | setTimeout(function() { 25 | status.textContent = ''; 26 | }, 1500); 27 | }); 28 | } 29 | 30 | // Restores select box and checkbox state using the preferences stored in chrome.storage. 31 | function restore_options() { 32 | chrome.storage.sync.get({ 33 | categoryBuildings: 'category/buildings', 34 | categoryFood: 'category/food', 35 | categoryNature: 'category/nature', 36 | categoryPeople: 'category/people', 37 | categoryTechnology: 'category/technology', 38 | categoryObjects: 'category/objects', 39 | backgroundRefresh: 'daily', 40 | lastFMusername: 'paul_r_schaefer' 41 | }, function(items) { 42 | document.getElementById('categoryBuildings').value = items.categoryBuildings; 43 | document.getElementById('categoryFood').value = items.categoryFood; 44 | document.getElementById('categoryNature').value = items.categoryNature; 45 | document.getElementById('categoryPeople').value = items.categoryPeople; 46 | document.getElementById('categoryTechnology').value = items.categoryTechnology; 47 | document.getElementById('categoryObjects').value = items.categoryObjects; 48 | document.getElementById('refresh').value = items.backgroundRefresh; 49 | document.getElementById('lastFMusername').value = items.lastFMusername; 50 | }); 51 | } 52 | document.addEventListener('DOMContentLoaded', restore_options); 53 | document.getElementById('save').addEventListener('click', save_options); 54 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "start", 3 | "version": "4.2.0", 4 | "description": "![https://pschfr.github.io/start/](screenshot.png) ##Personal new tab page with keyboard navigation, bookmarks, weather, quote, most recently listened to song, and image background.", 5 | "main": "dist/index.html", 6 | "repository": { 7 | "type": "git", 8 | "url": "git+https://github.com/pschfr/Start.git" 9 | }, 10 | "author": "Paul Schaefer", 11 | "license": "MIT", 12 | "bugs": { 13 | "url": "https://github.com/pschfr/Start/issues" 14 | }, 15 | "homepage": "https://github.com/pschfr/Start#readme", 16 | "dependencies": { 17 | "gulp": "^3.9.1", 18 | "gulp-concat": "^2.6.1", 19 | "gulp-htmlmin": "^3.0.0", 20 | "gulp-sass": "^2.3.2", 21 | "gulp-uglify": "^2.0.0", 22 | "gulp-uglifycss": "^1.0.6", 23 | "gulp-watch": "^4.3.11" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pschfr/start/b1f3e699625503a8e9c21de6ecf865b6374f1bb9/screenshot.png -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | New Tab 7 | 8 | 9 | 10 |
11 | 12 |
13 |
14 |
15 |
Loading…
16 |
17 |
18 |
19 |
Waiting for Dark Sky…
20 |
21 | 97 |
98 |

Loading song from Last.FM…

99 |

Fetching unread emails…

100 |
101 |

102 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /src/script.js: -------------------------------------------------------------------------------- 1 | // Finds current time and date, formats it properly 2 | function startTime() { 3 | var monthNames = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"]; 4 | var dayNames = ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"]; 5 | var now = new Date(); 6 | var time = [now.getHours(), now.getMinutes(), now.getSeconds()]; 7 | var date = [now.getDate(), now.getDay(), now.getMonth(), now.getFullYear()]; 8 | var hour = time[0]; 9 | var mins = time[1]; 10 | var secs = time[2]; 11 | var ampm = hour >= 12 ? 'PM' : 'AM'; 12 | var day = date[0]; 13 | var weekday = dayNames[date[1]]; 14 | var month = monthNames[date[2]]; 15 | var year = date[3]; 16 | hour = hour % 12; 17 | hour = hour ? hour : 12; 18 | mins = mins < 10 ? '0' + mins : mins; 19 | secs = secs < 10 ? '0' + secs : secs; 20 | document.getElementById('time').innerHTML = hour + ':' + mins + ':' + secs + ' ' + ampm; 21 | document.getElementById('date').innerHTML = weekday + ', ' + month + ' ' + day + ', ' + year; 22 | var t = setTimeout(startTime, 500); 23 | } 24 | 25 | // Random quote function. Important: Make sure each quote has a corresponding "quoted". 26 | function randomQuote() { 27 | var quotes = [ "If you are depressed you are living in the past. If you are anxious you are living in the future. If you are at peace you are living in the present.", "Madness, as you know, is a lot like gravity, all it takes is a little push.", "The surest way to corrupt a youth is to instruct him to hold in higher esteem those who think alike than those who think differently.", "Life has many ways of testing a person's will, either by having nothing happen at all or by having everything happen all at once.", "There is no excellent beauty that hath not some strangeness in its proportions.", "Children are fantastic little creatures, because next to drunk people, they are the only truly honest people on earth.", "I begin with an idea, and then it becomes something else.", "Be who you are and say what you feel because those who mind don't matter and those who matter don't mind.", "You can make more friends in two months by becoming interested in other people than you can in two years by trying to get people interested in you.", "An essential aspect of creativity is not being afraid to fail.", "Antisocial behavior is a trait of intelligence in a world of conformists.", "What you do today can improve all your tomorrows.", "A creative man is motivated by the desire to achieve, not by the desire to beat others.", "Don't watch the clock; do what it does. Keep going.", "If you can dream it, you can do it.", "You can't build a reputation on what you're going to do." ]; 28 | var quoted = [ "Lao Tzu", "Joker", "Friedrich Nietzsche", "Paulo Coelho", "Sir Francis Bacon", "Mads Nipper", "Pablo Picasso", "Dr. Seuss", "Dale Carnegie", "Edwin Land", "Nikola Tesla", "Ralph Marston", "Ayn Rand", "Sam Levenson", "Walt Disney", "Henry Ford" ]; 29 | var randNumQuotes = Math.floor((Math.random() * quotes.length)); 30 | document.getElementById('quote').innerHTML = '“' + quotes[randNumQuotes] + '” — ' + '' + quoted[randNumQuotes] + ''; 31 | } 32 | 33 | function randomBackground(time) { // daily, weekly, or every time 34 | // var categories = ['buildings', 'food', 'nature', 'people', 'technology' 'objects']; 35 | var categories = ['buildings', 'nature', 'objects']; 36 | var randomCategory = Math.floor((Math.random() * categories.length)); 37 | var photo = new UnsplashPhoto(); 38 | 39 | if (time == 'daily' || time == 'weekly') 40 | photo.all().randomize(time).fromCategory(categories[randomCategory]).fetch(); 41 | else 42 | photo.all().fromCategory(categories[randomCategory]).fetch(); 43 | 44 | document.body.style.backgroundImage = "url(" + photo.url + ")"; 45 | } 46 | 47 | // Loop through the user's first 6 bookmark folders 48 | function fetchBookmarks() { 49 | var count = 6; 50 | chrome.bookmarks.getTree(function(itemTree){ // gets list of bookmarks 51 | itemTree.forEach(function(item){ // loops through them all 52 | item.children[0].children.forEach(function(child) { // filters to only bookmarks in the bookmarks bar 53 | if (child.children && count >= 1) { // filters to folders on bookmarks bar and limits to 6 54 | console.log(child.title + ' ' + child.title.charAt(0).toLowerCase()); // get folder title and first letter 55 | child.children.forEach(function(bookmark) { 56 | var matches = bookmark.title.match(/\[(.*?)\]/); // fetch character between [] for keyboard shortcut 57 | if (matches) 58 | console.log(bookmark.title + ' ' + bookmark.url + ' ' + bookmark.title.match(matches[1])); 59 | }); 60 | console.log(''); 61 | count--; 62 | } 63 | }); 64 | }); 65 | }); 66 | 67 | var left = document.createElement('div'); 68 | left.className = 'left'; 69 | document.getElementById('box').appendChild(left); 70 | var right = document.createElement('div'); 71 | right.className = 'right'; 72 | document.getElementById('box').appendChild(right); 73 | } 74 | 75 | // Initializes keyboard nav 76 | function bindMousetraps() { 77 | // Loops through parent cells, opening those on request 78 | $.each($('.parent'), function(i, val) { 79 | Mousetrap.bind($(val).attr('data-key'), function(e) { 80 | $('a#' + $(val).attr('id')).toggleClass('active').next().slideToggle(150); 81 | 82 | // Binds key shortcuts for parent cell children when opened 83 | $.each($(val).parent().find('.tab'), function(i, val) { 84 | // Go to link URL 85 | Mousetrap.bind($(val).attr('data-key'), function(e) { 86 | window.location.href = $(val).attr('href'); 87 | }); 88 | 89 | // Go to link URL in new tab 90 | Mousetrap.bind($(val).attr('data-key').toUpperCase(), function(e) { 91 | window.open($(val).attr('href'), '_blank'); 92 | }); 93 | }); 94 | 95 | // Resets key shortcuts when parent cell key pressed twice 96 | Mousetrap.bind($(val).attr('data-key'), function(e) { 97 | resetMousetraps(); 98 | }); 99 | }); 100 | }); 101 | 102 | // Resets on ESC 103 | Mousetrap.bind('esc', function() { 104 | resetMousetraps(); 105 | }); 106 | 107 | // Binds Weather link 108 | Mousetrap.bind('w', function() { 109 | window.location.href = document.getElementById('weatherlink'); 110 | }); 111 | 112 | // Binds secret GitHub links 113 | Mousetrap.bind('g', function() { 114 | window.location.href = 'https://github.com/pschfr/start'; 115 | }); 116 | Mousetrap.bind('shift+g', function() { 117 | window.location.href = 'https://github.com/pschfr'; 118 | }) 119 | Mousetrap.bind('alt+g', function() { 120 | window.location.href = 'https://github.com/pschfr/start/projects/2?fullscreen=true'; 121 | }); 122 | 123 | // Binds keyboard shortcut helper modal 124 | Mousetrap.bind('?', function() { 125 | openModal(); 126 | }); 127 | } 128 | 129 | // Keyboard shortcuts modal 130 | function openModal() { 131 | if (document.getElementById('modal').style.display == '') 132 | document.getElementById('modal').style.display = 'block'; 133 | else 134 | closeModal(); 135 | } 136 | function closeModal() { 137 | document.getElementById('modal').style.display = ''; 138 | } 139 | 140 | // Closes cells, rebinds keyboard shortcuts 141 | function resetMousetraps() { 142 | $('.subMenu').slideUp(150); 143 | $('li a').removeClass('active'); 144 | Mousetrap.reset(); 145 | bindMousetraps(); 146 | document.getElementById('modal').style.display = ''; 147 | } 148 | 149 | // Gets weather for requested location, appends to page 150 | function getWeather(location) { 151 | var API_key = '3dc48ab835ed1b4369c089d0e742ff03'; 152 | var exclusions = 'flags,daily,minutely,alerts'; 153 | var darkSkyURL = 'https://api.darksky.net/forecast/' + API_key + '/' + location + '?exclude=' + exclusions; 154 | var xmlhttp = new XMLHttpRequest(); 155 | 156 | xmlhttp.open('GET', darkSkyURL, true); 157 | xmlhttp.onreadystatechange = function() { 158 | if (xmlhttp.readyState == 4) { 159 | if(xmlhttp.status == 200) { 160 | var weather = JSON.parse(xmlhttp.responseText); 161 | 162 | var weatherIcon = ''; 163 | if (weather.currently.icon == 'clear-day') 164 | weatherIcon = 'sun'; 165 | else if (weather.currently.icon == 'clear-night') 166 | weatherIcon = 'moon'; 167 | else if (weather.currently.icon == 'rain') 168 | weatherIcon = 'rain'; 169 | else if (weather.currently.icon == 'snow') 170 | weatherIcon = 'snow'; 171 | else if (weather.currently.icon == 'sleet') 172 | weatherIcon = 'sleet'; 173 | else if (weather.currently.icon == 'wind') 174 | weatherIcon = 'wind'; 175 | else if (weather.currently.icon == 'fog') 176 | weatherIcon = 'fog'; 177 | else if (weather.currently.icon == 'cloudy') 178 | weatherIcon = 'cloud'; 179 | else if (weather.currently.icon == 'partly-cloudy-day') 180 | weatherIcon = 'cloud sun'; 181 | else if (weather.currently.icon == 'partly-cloudy-night') 182 | weatherIcon = 'cloud moon'; 183 | 184 | if (weather.currently.icon == 'snow' || weather.currently.icon == 'sleet') 185 | participate('snow'); 186 | else if (weather.currently.icon == 'rain') 187 | participate('rain'); 188 | 189 | document.getElementById('weather').innerHTML = ' ' + weather.currently.summary + ', ' + Math.round(weather.currently.temperature) + '°'; 190 | document.getElementById('details').innerHTML = weather.hourly.summary.replace(',', ',
'); 191 | } 192 | } 193 | }; 194 | xmlhttp.send(null); 195 | } 196 | 197 | // Geolocates the user, otherwise defaulting to Pittsburgh 198 | function geolocWeather() { 199 | if('geolocation' in navigator) { 200 | navigator.geolocation.getCurrentPosition(function(position) { 201 | getWeather(position.coords.latitude + ',' + position.coords.longitude); 202 | }); 203 | } else { 204 | getWeather('40.4406, -79.9959'); 205 | } 206 | } 207 | 208 | // Connects to Last.FM, retrives most recent song - based on my https://github.com/pschfr/LastFM.js 209 | function lastfmRequest() { 210 | var username = 'paul_r_schaefer'; 211 | var API_key = '0f680404e39c821cac34008cc4d803db'; 212 | var lastFMurl = 'https://ws.audioscrobbler.com/2.0/?method=user.getrecenttracks&user=' + username + '&api_key=' + API_key + '&limit=1&format=json'; 213 | var element = document.getElementById('lastFM'); 214 | var xmlhttp = new XMLHttpRequest(); 215 | 216 | xmlhttp.open('GET', lastFMurl, true); 217 | xmlhttp.onreadystatechange = function() { 218 | if (xmlhttp.readyState == 4) { 219 | if(xmlhttp.status == 200) { 220 | var total = JSON.parse(xmlhttp.responseText).recenttracks['\@attr'].total; 221 | var track = JSON.parse(xmlhttp.responseText).recenttracks.track[0]; 222 | 223 | if (track['\@attr'] && track['\@attr'].nowplaying !== '') 224 | element.innerHTML = 'currently listening to: '; 225 | else 226 | element.innerHTML = 'last listened to: '; 227 | 228 | element.innerHTML += '' + track.artist['\#text'] + ' — ' + track.name + ' '; 229 | } 230 | } 231 | }; 232 | xmlhttp.send(null); 233 | } 234 | 235 | // Loads options from Google Chrome 236 | function getOptions() { 237 | chrome.storage.sync.get({ 238 | categoryBuildings: 'category/buildings', 239 | categoryFood: 'category/food', 240 | categoryNature: 'category/nature', 241 | categoryPeople: 'category/people', 242 | categoryTechnology: 'category/technology', 243 | categoryObjects: 'category/objects', 244 | backgroundRefresh: 'daily', 245 | lastFMusername: 'paul_r_schaefer' 246 | }, function(items) { 247 | console.log(items); 248 | }); 249 | } 250 | 251 | // Generate snow/rain with canvas tag 252 | // https://github.com/HermannBjorgvin/SnowJs 253 | function participate(type) { 254 | var canvas = document.getElementById("snow"); 255 | var ctx = canvas.getContext("2d"); 256 | var flakeArray = []; 257 | canvas.style.pointerEvents = "none"; 258 | canvas.style.position = "fixed"; 259 | canvas.style.top = 0; 260 | canvas.style.left = 0; 261 | canvas.style.width = "100vw"; 262 | canvas.style.height = "100vh"; 263 | function canvasResize(){ 264 | canvas.height = canvas.offsetHeight; 265 | canvas.width = canvas.offsetWidth; 266 | } 267 | canvasResize(); 268 | window.onresize = function() { 269 | canvasResize(); 270 | }; 271 | var MyMath = Math; 272 | setInterval(function() { 273 | ctx.clearRect(0, 0, canvas.width, canvas.height); 274 | ctx.beginPath(); 275 | var random = MyMath.random(); 276 | var distance = .05 + .95 * random; 277 | flake = {}; 278 | flake.x = 1.5 * canvas.width * MyMath.random() - .5 * canvas.width; 279 | flake.y = -9; 280 | flake.velX = 2 * distance * (MyMath.random() / 2 + .5); 281 | flake.velY = (4 + 2 * MyMath.random()) * distance; 282 | flake.radius = MyMath.pow(5 * random, 2) / 5; 283 | flake.update = function() { 284 | var t = this; 285 | t.x += t.velX; 286 | t.y += t.velY; 287 | ctx.beginPath(); 288 | ctx.arc(t.x, t.y, t.radius, 0, 2 * MyMath.PI, !1); 289 | if (type == 'snow') 290 | ctx.fillStyle = "#FFF"; 291 | else if (type == 'rain') 292 | ctx.fillStyle = "#00F"; 293 | ctx.fill() 294 | }; 295 | flakeArray.push(flake); 296 | for (b = 0; b < flakeArray.length; b++) { 297 | flakeArray[b].y > canvas.height ? flakeArray.splice(b, 1) : flakeArray[b].update() 298 | } 299 | }, 16); 300 | } 301 | 302 | // Connects to Gmail and fetches unread message count 303 | function gmailRequest() { 304 | var gmailURL = 'https://mail.google.com/mail/u/0/feed/atom' 305 | element = document.getElementById('gmail'), 306 | xmlhttp = new XMLHttpRequest(); 307 | xmlhttp.open('GET', gmailURL, true); 308 | xmlhttp.onreadystatechange = function() { 309 | if (xmlhttp.readyState == 4) { 310 | if(xmlhttp.status == 200) { 311 | var parser = new DOMParser(), 312 | xmlDoc = parser.parseFromString(xmlhttp.responseText, 'application/xml'), 313 | email = xmlDoc.getElementsByTagName('title')[0].innerHTML.replace('Gmail - Inbox for ', ''), 314 | count = xmlDoc.getElementsByTagName('fullcount')[0].innerHTML, 315 | entries = xmlDoc.getElementsByTagName('entry') 316 | entryList = email + ":\n", 317 | plural = (count > 1)?('s'):(''); 318 | 319 | if (entries.length == 0) 320 | element.innerHTML = "

Inbox zero. Enjoy your day.

\n"; 321 | else { 322 | for (var i = 0; i < entries.length; i++) { 323 | var entryTitle = entries[i].getElementsByTagName('title')[0].innerHTML.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, '''), 324 | authorName = entries[i].getElementsByTagName('author')[0].getElementsByTagName('name')[0].innerHTML.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, '''); 325 | entryList += authorName + ' — ' + entryTitle + "\n"; 326 | } 327 | 328 | element.innerHTML = "

" + count + " unread email" + plural + "

\n"; 329 | } 330 | } 331 | } 332 | }; 333 | xmlhttp.send(null); 334 | } 335 | 336 | // Initializes everything on page load 337 | $(function() { 338 | startTime(); 339 | randomQuote(); 340 | randomBackground(); 341 | bindMousetraps(); 342 | geolocWeather(); 343 | lastfmRequest(); 344 | gmailRequest(); 345 | 346 | // In development 347 | // fetchBookmarks(); 348 | // getOptions(); 349 | 350 | // Binds click events for opening tabs and background click to close 351 | $('li a.parent').click(function() { 352 | $(this).parent('li').find('ul').slideToggle(150); 353 | $(this).toggleClass('active'); 354 | }); 355 | 356 | // Binds click events to close cells and keyboard modal 357 | document.getElementById('background').addEventListener('click', resetMousetraps, false); 358 | document.getElementById('modal').addEventListener('click', closeModal, false); 359 | }); 360 | -------------------------------------------------------------------------------- /src/style.sass: -------------------------------------------------------------------------------- 1 | @charset "utf-8" 2 | html 3 | box-sizing: border-box 4 | *, *:before, *:after 5 | box-sizing: inherit 6 | body 7 | background: #222 no-repeat center/cover fixed 8 | font: 20px -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'Segoe UI', Roboto, Helvetica, Arial, sans-serif 9 | animation: fadeIn 0.75s both ease-in 10 | text-transform: lowercase 11 | text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.5) 12 | color: white 13 | margin: 0 14 | div#background 15 | background-color: rgba(0, 0, 0, 0.5) 16 | position: absolute 17 | height: 100% 18 | width: 100% 19 | div.centerWrapper 20 | transform: translateY(-50%) 21 | position: absolute 22 | width: 50vw 23 | left: 25vw 24 | top: 50% 25 | div.large, div.small 26 | display: flex 27 | padding: 0 5px 28 | align-items: center 29 | div 30 | flex: 1 31 | div.small 32 | padding-bottom: 10px 33 | div.large div 34 | font-size: xx-large 35 | font-feature-settings: 'tnum' 36 | div#lastFM a 37 | color: white 38 | display: inline-block 39 | padding: 15px 0 40 | text-transform: none 41 | ul 42 | text-shadow: none 43 | list-style: none 44 | padding: 0 45 | margin: 0 46 | &.subMenu 47 | display: none 48 | width: 100% 49 | &.box a 50 | color: #111 51 | padding: 15px 52 | text-decoration: none 53 | a 54 | transition: all 0.15s 55 | text-decoration: none 56 | display: block 57 | outline: none 58 | color: white 59 | &:hover, &:focus 60 | text-decoration: underline 61 | p 62 | padding: 15px 63 | margin: 0 64 | #quote 65 | position: absolute 66 | font-size: smaller 67 | width: 100% 68 | bottom: 1% 69 | #modal 70 | display: none 71 | position: absolute 72 | width: 100% 73 | height: 100% 74 | top: 0 75 | background-color: rgba(0, 0, 0, 0.8) 76 | .inner 77 | transform: translateY(-50%) 78 | position: absolute 79 | width: 50vw 80 | left: 25vw 81 | top: 50% 82 | .clear 83 | clear: both 84 | .left 85 | float: left 86 | .right 87 | float: right 88 | .text-right, .right * 89 | text-align: right 90 | .text-center 91 | text-align: center 92 | .centered 93 | margin: 0 auto 94 | .no-transform 95 | text-transform: none 96 | div.right, div.left 97 | width: 50% 98 | ul.box li a 99 | background-color: rgba(255, 255, 255, 0.5) 100 | ul li a:hover, ul li a:focus, ul li a.active, ul li a.active + ul.subMenu li a 101 | background-color: rgba(255, 255, 255, 0.7) 102 | ul li a:hover span, ul li a:focus span, ul li a.active span, ul li a.active + ul.subMenu li a span 103 | text-decoration: underline 104 | @media (max-width: 985px) 105 | ul.box div.left, ul.box div.right, div.large div, div.small div 106 | text-align: center 107 | float: none 108 | width: 100% 109 | .right * 110 | text-align: inherit 111 | div.large, div.small 112 | display: block 113 | div 114 | padding-bottom: 15px 115 | height: auto 116 | @keyframes fadeIn 117 | from 118 | opacity: 0 119 | to 120 | opacity: 1 121 | @import 'vendor/climacons.scss' 122 | -------------------------------------------------------------------------------- /src/vendor/climacons.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Climacons-Font'; 3 | src:url('climacons/climacons-webfont.eot'); 4 | src:url('climacons/climacons-webfont.eot?#iefix') format('embedded-opentype'), 5 | url('climacons/climacons-webfont.svg#Climacons-Font') format('svg'), 6 | url('climacons/climacons-webfont.woff') format('woff'), 7 | url('climacons/climacons-webfont.ttf') format('truetype'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | .climacon { vertical-align: middle; } 12 | .climacon:before{ 13 | font-family: 'Climacons-Font'; 14 | font-style: normal; 15 | font-weight: normal; 16 | line-height: 1; 17 | } 18 | .climacon.cloud:before { content: "\e000"; } 19 | .climacon.cloud.sun:before { content: "\e001"; } 20 | .climacon.cloud.moon:before { content: "\e002"; } 21 | .climacon.rain:before,.climacon.rain.cloud:before { content: "\e003"; } 22 | // .climacon.rain.sun:before,.climacon.rain.cloud.sun:before { content: "\e004"; } 23 | // .climacon.rain.moon:before,.climacon.rain.cloud.moon:before { content: "\e005"; } 24 | // .climacon.showers:before,.climacon.showers.cloud:before { content: "\e006"; } 25 | // .climacon.showers.sun:before,.climacon.showers.cloud.sun:before { content: "\e007"; } 26 | // .climacon.showers.moon:before,.climacon.showers.cloud.moon:before { content: "\e008"; } 27 | // .climacon.downpour:before,.climacon.downpour.cloud:before { content: "\e009"; } 28 | // .climacon.downpour.sun:before,.climacon.downpour.cloud.sun:before { content: "\e00a"; } 29 | // .climacon.downpour.moon:before,.climacon.downpour.cloud.moon:before { content: "\e00b"; } 30 | // .climacon.drizzle:before,.climacon.drizzle.cloud:before { content: "\e00c"; } 31 | // .climacon.drizzle.sun:before,.climacon.drizzle.cloud.sun:before { content: "\e00d"; } 32 | // .climacon.drizzle.moon:before,.climacon.drizzle.cloud.moon:before { content: "\e00e"; } 33 | .climacon.sleet:before,.climacon.sleet.cloud:before { content: "\e00f"; } 34 | // .climacon.sleet.sun:before,.climacon.sleet.cloud.sun:before { content: "\e010"; } 35 | // .climacon.sleet.moon:before,.climacon.sleet.cloud.moon:before { content: "\e011"; } 36 | // .climacon.hail:before,.climacon.hail.cloud:before { content: "\e012"; } 37 | // .climacon.hail.sun:before,.climacon.hail.cloud.sun:before { content: "\e013"; } 38 | // .climacon.hail.moon:before,.climacon.hail.cloud.moon:before { content: "\e014"; } 39 | // .climacon.flurries:before,.climacon.flurries.cloud:before { content: "\e015"; } 40 | // .climacon.flurries.sun:before,.climacon.flurries.cloud.sun:before { content: "\e016"; } 41 | // .climacon.flurries.moon:before,.climacon.flurries.cloud.moon:before { content: "\e017"; } 42 | .climacon.snow:before,.climacon.snow.cloud:before { content: "\e018"; } 43 | // .climacon.snow.sun:before,.climacon.snow.cloud.sun:before { content: "\e019"; } 44 | // .climacon.snow.moon:before,.climacon.snow.cloud.moon:before { content: "\e01a"; } 45 | .climacon.fog:before,.climacon.fog.cloud:before { content: "\e01b"; } 46 | // .climacon.fog.sun:before,.climacon.fog.cloud.sun:before { content: "\e01c"; } 47 | // .climacon.fog.moon:before,.climacon.fog.cloud.moon:before { content: "\e01d"; } 48 | // .climacon.haze:before { content: "\e01e"; } 49 | // .climacon.haze.sun:before { content: "\e01f"; } 50 | // .climacon.haze.moon:before { content: "\e020"; } 51 | .climacon.wind:before { content: "\e021"; } 52 | // .climacon.wind.cloud:before { content: "\e022"; } 53 | // .climacon.wind.sun:before, .climacon.wind.cloud.sun:before { content: "\e023"; } 54 | // .climacon.wind.moon:before, .climacon.wind.cloud.moon:before { content: "\e024"; } 55 | // .climacon.lightning:before, .climacon.lightning.cloud:before { content: "\e025"; } 56 | // .climacon.lightning.sun:before,.climacon.lightning.cloud.sun:before { content: "\e026"; } 57 | // .climacon.lightning.moon:before,.climacon.lightning.cloud.moon:before { content: "\e027"; } 58 | .climacon.sun:before { content: "\e028"; } 59 | // .climacon.sun.set:before,.climacon.sunset:before { content: "\e029"; } 60 | // .climacon.sun.rise:before,.climacon.sunrise:before { content: "\e02a"; } 61 | // .climacon.sun.low:before,.climacon.sun-low:before,.climacon.low-sun:before { content: "\e02b"; } 62 | // .climacon.sun.lower:before,.climacon.sun-lower:before,.climacon.lower-sun:before { content: "\e02c"; } 63 | .climacon.moon:before { content: "\e02d"; } 64 | -------------------------------------------------------------------------------- /src/vendor/mousetrap.js: -------------------------------------------------------------------------------- 1 | /*global define:false */ 2 | /** 3 | * Copyright 2016 Craig Campbell 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | * 17 | * Mousetrap is a simple keyboard shortcut library for Javascript with 18 | * no external dependencies 19 | * 20 | * @version 1.6.0 21 | * @url craig.is/killing/mice 22 | */ 23 | (function(window, document, undefined) { 24 | 25 | // Check if mousetrap is used inside browser, if not, return 26 | if (!window) { 27 | return; 28 | } 29 | 30 | /** 31 | * mapping of special keycodes to their corresponding keys 32 | * 33 | * everything in this dictionary cannot use keypress events 34 | * so it has to be here to map to the correct keycodes for 35 | * keyup/keydown events 36 | * 37 | * @type {Object} 38 | */ 39 | var _MAP = { 40 | 8: 'backspace', 41 | 9: 'tab', 42 | 13: 'enter', 43 | 16: 'shift', 44 | 17: 'ctrl', 45 | 18: 'alt', 46 | 20: 'capslock', 47 | 27: 'esc', 48 | 32: 'space', 49 | 33: 'pageup', 50 | 34: 'pagedown', 51 | 35: 'end', 52 | 36: 'home', 53 | 37: 'left', 54 | 38: 'up', 55 | 39: 'right', 56 | 40: 'down', 57 | 45: 'ins', 58 | 46: 'del', 59 | 91: 'meta', 60 | 93: 'meta', 61 | 224: 'meta' 62 | }; 63 | 64 | /** 65 | * mapping for special characters so they can support 66 | * 67 | * this dictionary is only used incase you want to bind a 68 | * keyup or keydown event to one of these keys 69 | * 70 | * @type {Object} 71 | */ 72 | var _KEYCODE_MAP = { 73 | 106: '*', 74 | 107: '+', 75 | 109: '-', 76 | 110: '.', 77 | 111 : '/', 78 | 186: ';', 79 | 187: '=', 80 | 188: ',', 81 | 189: '-', 82 | 190: '.', 83 | 191: '/', 84 | 192: '`', 85 | 219: '[', 86 | 220: '\\', 87 | 221: ']', 88 | 222: '\'' 89 | }; 90 | 91 | /** 92 | * this is a mapping of keys that require shift on a US keypad 93 | * back to the non shift equivelents 94 | * 95 | * this is so you can use keyup events with these keys 96 | * 97 | * note that this will only work reliably on US keyboards 98 | * 99 | * @type {Object} 100 | */ 101 | var _SHIFT_MAP = { 102 | '~': '`', 103 | '!': '1', 104 | '@': '2', 105 | '#': '3', 106 | '$': '4', 107 | '%': '5', 108 | '^': '6', 109 | '&': '7', 110 | '*': '8', 111 | '(': '9', 112 | ')': '0', 113 | '_': '-', 114 | '+': '=', 115 | ':': ';', 116 | '\"': '\'', 117 | '<': ',', 118 | '>': '.', 119 | '?': '/', 120 | '|': '\\' 121 | }; 122 | 123 | /** 124 | * this is a list of special strings you can use to map 125 | * to modifier keys when you specify your keyboard shortcuts 126 | * 127 | * @type {Object} 128 | */ 129 | var _SPECIAL_ALIASES = { 130 | 'option': 'alt', 131 | 'command': 'meta', 132 | 'return': 'enter', 133 | 'escape': 'esc', 134 | 'plus': '+', 135 | 'mod': /Mac|iPod|iPhone|iPad/.test(navigator.platform) ? 'meta' : 'ctrl' 136 | }; 137 | 138 | /** 139 | * variable to store the flipped version of _MAP from above 140 | * needed to check if we should use keypress or not when no action 141 | * is specified 142 | * 143 | * @type {Object|undefined} 144 | */ 145 | var _REVERSE_MAP; 146 | 147 | /** 148 | * loop through the f keys, f1 to f19 and add them to the map 149 | * programatically 150 | */ 151 | for (var i = 1; i < 20; ++i) { 152 | _MAP[111 + i] = 'f' + i; 153 | } 154 | 155 | /** 156 | * loop through to map numbers on the numeric keypad 157 | */ 158 | for (i = 0; i <= 9; ++i) { 159 | 160 | // This needs to use a string cause otherwise since 0 is falsey 161 | // mousetrap will never fire for numpad 0 pressed as part of a keydown 162 | // event. 163 | // 164 | // @see https://github.com/ccampbell/mousetrap/pull/258 165 | _MAP[i + 96] = i.toString(); 166 | } 167 | 168 | /** 169 | * cross browser add event method 170 | * 171 | * @param {Element|HTMLDocument} object 172 | * @param {string} type 173 | * @param {Function} callback 174 | * @returns void 175 | */ 176 | function _addEvent(object, type, callback) { 177 | if (object.addEventListener) { 178 | object.addEventListener(type, callback, false); 179 | return; 180 | } 181 | 182 | object.attachEvent('on' + type, callback); 183 | } 184 | 185 | /** 186 | * takes the event and returns the key character 187 | * 188 | * @param {Event} e 189 | * @return {string} 190 | */ 191 | function _characterFromEvent(e) { 192 | 193 | // for keypress events we should return the character as is 194 | if (e.type == 'keypress') { 195 | var character = String.fromCharCode(e.which); 196 | 197 | // if the shift key is not pressed then it is safe to assume 198 | // that we want the character to be lowercase. this means if 199 | // you accidentally have caps lock on then your key bindings 200 | // will continue to work 201 | // 202 | // the only side effect that might not be desired is if you 203 | // bind something like 'A' cause you want to trigger an 204 | // event when capital A is pressed caps lock will no longer 205 | // trigger the event. shift+a will though. 206 | if (!e.shiftKey) { 207 | character = character.toLowerCase(); 208 | } 209 | 210 | return character; 211 | } 212 | 213 | // for non keypress events the special maps are needed 214 | if (_MAP[e.which]) { 215 | return _MAP[e.which]; 216 | } 217 | 218 | if (_KEYCODE_MAP[e.which]) { 219 | return _KEYCODE_MAP[e.which]; 220 | } 221 | 222 | // if it is not in the special map 223 | 224 | // with keydown and keyup events the character seems to always 225 | // come in as an uppercase character whether you are pressing shift 226 | // or not. we should make sure it is always lowercase for comparisons 227 | return String.fromCharCode(e.which).toLowerCase(); 228 | } 229 | 230 | /** 231 | * checks if two arrays are equal 232 | * 233 | * @param {Array} modifiers1 234 | * @param {Array} modifiers2 235 | * @returns {boolean} 236 | */ 237 | function _modifiersMatch(modifiers1, modifiers2) { 238 | return modifiers1.sort().join(',') === modifiers2.sort().join(','); 239 | } 240 | 241 | /** 242 | * takes a key event and figures out what the modifiers are 243 | * 244 | * @param {Event} e 245 | * @returns {Array} 246 | */ 247 | function _eventModifiers(e) { 248 | var modifiers = []; 249 | 250 | if (e.shiftKey) { 251 | modifiers.push('shift'); 252 | } 253 | 254 | if (e.altKey) { 255 | modifiers.push('alt'); 256 | } 257 | 258 | if (e.ctrlKey) { 259 | modifiers.push('ctrl'); 260 | } 261 | 262 | if (e.metaKey) { 263 | modifiers.push('meta'); 264 | } 265 | 266 | return modifiers; 267 | } 268 | 269 | /** 270 | * prevents default for this event 271 | * 272 | * @param {Event} e 273 | * @returns void 274 | */ 275 | function _preventDefault(e) { 276 | if (e.preventDefault) { 277 | e.preventDefault(); 278 | return; 279 | } 280 | 281 | e.returnValue = false; 282 | } 283 | 284 | /** 285 | * stops propogation for this event 286 | * 287 | * @param {Event} e 288 | * @returns void 289 | */ 290 | function _stopPropagation(e) { 291 | if (e.stopPropagation) { 292 | e.stopPropagation(); 293 | return; 294 | } 295 | 296 | e.cancelBubble = true; 297 | } 298 | 299 | /** 300 | * determines if the keycode specified is a modifier key or not 301 | * 302 | * @param {string} key 303 | * @returns {boolean} 304 | */ 305 | function _isModifier(key) { 306 | return key == 'shift' || key == 'ctrl' || key == 'alt' || key == 'meta'; 307 | } 308 | 309 | /** 310 | * reverses the map lookup so that we can look for specific keys 311 | * to see what can and can't use keypress 312 | * 313 | * @return {Object} 314 | */ 315 | function _getReverseMap() { 316 | if (!_REVERSE_MAP) { 317 | _REVERSE_MAP = {}; 318 | for (var key in _MAP) { 319 | 320 | // pull out the numeric keypad from here cause keypress should 321 | // be able to detect the keys from the character 322 | if (key > 95 && key < 112) { 323 | continue; 324 | } 325 | 326 | if (_MAP.hasOwnProperty(key)) { 327 | _REVERSE_MAP[_MAP[key]] = key; 328 | } 329 | } 330 | } 331 | return _REVERSE_MAP; 332 | } 333 | 334 | /** 335 | * picks the best action based on the key combination 336 | * 337 | * @param {string} key - character for key 338 | * @param {Array} modifiers 339 | * @param {string=} action passed in 340 | */ 341 | function _pickBestAction(key, modifiers, action) { 342 | 343 | // if no action was picked in we should try to pick the one 344 | // that we think would work best for this key 345 | if (!action) { 346 | action = _getReverseMap()[key] ? 'keydown' : 'keypress'; 347 | } 348 | 349 | // modifier keys don't work as expected with keypress, 350 | // switch to keydown 351 | if (action == 'keypress' && modifiers.length) { 352 | action = 'keydown'; 353 | } 354 | 355 | return action; 356 | } 357 | 358 | /** 359 | * Converts from a string key combination to an array 360 | * 361 | * @param {string} combination like "command+shift+l" 362 | * @return {Array} 363 | */ 364 | function _keysFromString(combination) { 365 | if (combination === '+') { 366 | return ['+']; 367 | } 368 | 369 | combination = combination.replace(/\+{2}/g, '+plus'); 370 | return combination.split('+'); 371 | } 372 | 373 | /** 374 | * Gets info for a specific key combination 375 | * 376 | * @param {string} combination key combination ("command+s" or "a" or "*") 377 | * @param {string=} action 378 | * @returns {Object} 379 | */ 380 | function _getKeyInfo(combination, action) { 381 | var keys; 382 | var key; 383 | var i; 384 | var modifiers = []; 385 | 386 | // take the keys from this pattern and figure out what the actual 387 | // pattern is all about 388 | keys = _keysFromString(combination); 389 | 390 | for (i = 0; i < keys.length; ++i) { 391 | key = keys[i]; 392 | 393 | // normalize key names 394 | if (_SPECIAL_ALIASES[key]) { 395 | key = _SPECIAL_ALIASES[key]; 396 | } 397 | 398 | // if this is not a keypress event then we should 399 | // be smart about using shift keys 400 | // this will only work for US keyboards however 401 | if (action && action != 'keypress' && _SHIFT_MAP[key]) { 402 | key = _SHIFT_MAP[key]; 403 | modifiers.push('shift'); 404 | } 405 | 406 | // if this key is a modifier then add it to the list of modifiers 407 | if (_isModifier(key)) { 408 | modifiers.push(key); 409 | } 410 | } 411 | 412 | // depending on what the key combination is 413 | // we will try to pick the best event for it 414 | action = _pickBestAction(key, modifiers, action); 415 | 416 | return { 417 | key: key, 418 | modifiers: modifiers, 419 | action: action 420 | }; 421 | } 422 | 423 | function _belongsTo(element, ancestor) { 424 | if (element === null || element === document) { 425 | return false; 426 | } 427 | 428 | if (element === ancestor) { 429 | return true; 430 | } 431 | 432 | return _belongsTo(element.parentNode, ancestor); 433 | } 434 | 435 | function Mousetrap(targetElement) { 436 | var self = this; 437 | 438 | targetElement = targetElement || document; 439 | 440 | if (!(self instanceof Mousetrap)) { 441 | return new Mousetrap(targetElement); 442 | } 443 | 444 | /** 445 | * element to attach key events to 446 | * 447 | * @type {Element} 448 | */ 449 | self.target = targetElement; 450 | 451 | /** 452 | * a list of all the callbacks setup via Mousetrap.bind() 453 | * 454 | * @type {Object} 455 | */ 456 | self._callbacks = {}; 457 | 458 | /** 459 | * direct map of string combinations to callbacks used for trigger() 460 | * 461 | * @type {Object} 462 | */ 463 | self._directMap = {}; 464 | 465 | /** 466 | * keeps track of what level each sequence is at since multiple 467 | * sequences can start out with the same sequence 468 | * 469 | * @type {Object} 470 | */ 471 | var _sequenceLevels = {}; 472 | 473 | /** 474 | * variable to store the setTimeout call 475 | * 476 | * @type {null|number} 477 | */ 478 | var _resetTimer; 479 | 480 | /** 481 | * temporary state where we will ignore the next keyup 482 | * 483 | * @type {boolean|string} 484 | */ 485 | var _ignoreNextKeyup = false; 486 | 487 | /** 488 | * temporary state where we will ignore the next keypress 489 | * 490 | * @type {boolean} 491 | */ 492 | var _ignoreNextKeypress = false; 493 | 494 | /** 495 | * are we currently inside of a sequence? 496 | * type of action ("keyup" or "keydown" or "keypress") or false 497 | * 498 | * @type {boolean|string} 499 | */ 500 | var _nextExpectedAction = false; 501 | 502 | /** 503 | * resets all sequence counters except for the ones passed in 504 | * 505 | * @param {Object} doNotReset 506 | * @returns void 507 | */ 508 | function _resetSequences(doNotReset) { 509 | doNotReset = doNotReset || {}; 510 | 511 | var activeSequences = false, 512 | key; 513 | 514 | for (key in _sequenceLevels) { 515 | if (doNotReset[key]) { 516 | activeSequences = true; 517 | continue; 518 | } 519 | _sequenceLevels[key] = 0; 520 | } 521 | 522 | if (!activeSequences) { 523 | _nextExpectedAction = false; 524 | } 525 | } 526 | 527 | /** 528 | * finds all callbacks that match based on the keycode, modifiers, 529 | * and action 530 | * 531 | * @param {string} character 532 | * @param {Array} modifiers 533 | * @param {Event|Object} e 534 | * @param {string=} sequenceName - name of the sequence we are looking for 535 | * @param {string=} combination 536 | * @param {number=} level 537 | * @returns {Array} 538 | */ 539 | function _getMatches(character, modifiers, e, sequenceName, combination, level) { 540 | var i; 541 | var callback; 542 | var matches = []; 543 | var action = e.type; 544 | 545 | // if there are no events related to this keycode 546 | if (!self._callbacks[character]) { 547 | return []; 548 | } 549 | 550 | // if a modifier key is coming up on its own we should allow it 551 | if (action == 'keyup' && _isModifier(character)) { 552 | modifiers = [character]; 553 | } 554 | 555 | // loop through all callbacks for the key that was pressed 556 | // and see if any of them match 557 | for (i = 0; i < self._callbacks[character].length; ++i) { 558 | callback = self._callbacks[character][i]; 559 | 560 | // if a sequence name is not specified, but this is a sequence at 561 | // the wrong level then move onto the next match 562 | if (!sequenceName && callback.seq && _sequenceLevels[callback.seq] != callback.level) { 563 | continue; 564 | } 565 | 566 | // if the action we are looking for doesn't match the action we got 567 | // then we should keep going 568 | if (action != callback.action) { 569 | continue; 570 | } 571 | 572 | // if this is a keypress event and the meta key and control key 573 | // are not pressed that means that we need to only look at the 574 | // character, otherwise check the modifiers as well 575 | // 576 | // chrome will not fire a keypress if meta or control is down 577 | // safari will fire a keypress if meta or meta+shift is down 578 | // firefox will fire a keypress if meta or control is down 579 | if ((action == 'keypress' && !e.metaKey && !e.ctrlKey) || _modifiersMatch(modifiers, callback.modifiers)) { 580 | 581 | // when you bind a combination or sequence a second time it 582 | // should overwrite the first one. if a sequenceName or 583 | // combination is specified in this call it does just that 584 | // 585 | // @todo make deleting its own method? 586 | var deleteCombo = !sequenceName && callback.combo == combination; 587 | var deleteSequence = sequenceName && callback.seq == sequenceName && callback.level == level; 588 | if (deleteCombo || deleteSequence) { 589 | self._callbacks[character].splice(i, 1); 590 | } 591 | 592 | matches.push(callback); 593 | } 594 | } 595 | 596 | return matches; 597 | } 598 | 599 | /** 600 | * actually calls the callback function 601 | * 602 | * if your callback function returns false this will use the jquery 603 | * convention - prevent default and stop propogation on the event 604 | * 605 | * @param {Function} callback 606 | * @param {Event} e 607 | * @returns void 608 | */ 609 | function _fireCallback(callback, e, combo, sequence) { 610 | 611 | // if this event should not happen stop here 612 | if (self.stopCallback(e, e.target || e.srcElement, combo, sequence)) { 613 | return; 614 | } 615 | 616 | if (callback(e, combo) === false) { 617 | _preventDefault(e); 618 | _stopPropagation(e); 619 | } 620 | } 621 | 622 | /** 623 | * handles a character key event 624 | * 625 | * @param {string} character 626 | * @param {Array} modifiers 627 | * @param {Event} e 628 | * @returns void 629 | */ 630 | self._handleKey = function(character, modifiers, e) { 631 | var callbacks = _getMatches(character, modifiers, e); 632 | var i; 633 | var doNotReset = {}; 634 | var maxLevel = 0; 635 | var processedSequenceCallback = false; 636 | 637 | // Calculate the maxLevel for sequences so we can only execute the longest callback sequence 638 | for (i = 0; i < callbacks.length; ++i) { 639 | if (callbacks[i].seq) { 640 | maxLevel = Math.max(maxLevel, callbacks[i].level); 641 | } 642 | } 643 | 644 | // loop through matching callbacks for this key event 645 | for (i = 0; i < callbacks.length; ++i) { 646 | 647 | // fire for all sequence callbacks 648 | // this is because if for example you have multiple sequences 649 | // bound such as "g i" and "g t" they both need to fire the 650 | // callback for matching g cause otherwise you can only ever 651 | // match the first one 652 | if (callbacks[i].seq) { 653 | 654 | // only fire callbacks for the maxLevel to prevent 655 | // subsequences from also firing 656 | // 657 | // for example 'a option b' should not cause 'option b' to fire 658 | // even though 'option b' is part of the other sequence 659 | // 660 | // any sequences that do not match here will be discarded 661 | // below by the _resetSequences call 662 | if (callbacks[i].level != maxLevel) { 663 | continue; 664 | } 665 | 666 | processedSequenceCallback = true; 667 | 668 | // keep a list of which sequences were matches for later 669 | doNotReset[callbacks[i].seq] = 1; 670 | _fireCallback(callbacks[i].callback, e, callbacks[i].combo, callbacks[i].seq); 671 | continue; 672 | } 673 | 674 | // if there were no sequence matches but we are still here 675 | // that means this is a regular match so we should fire that 676 | if (!processedSequenceCallback) { 677 | _fireCallback(callbacks[i].callback, e, callbacks[i].combo); 678 | } 679 | } 680 | 681 | // if the key you pressed matches the type of sequence without 682 | // being a modifier (ie "keyup" or "keypress") then we should 683 | // reset all sequences that were not matched by this event 684 | // 685 | // this is so, for example, if you have the sequence "h a t" and you 686 | // type "h e a r t" it does not match. in this case the "e" will 687 | // cause the sequence to reset 688 | // 689 | // modifier keys are ignored because you can have a sequence 690 | // that contains modifiers such as "enter ctrl+space" and in most 691 | // cases the modifier key will be pressed before the next key 692 | // 693 | // also if you have a sequence such as "ctrl+b a" then pressing the 694 | // "b" key will trigger a "keypress" and a "keydown" 695 | // 696 | // the "keydown" is expected when there is a modifier, but the 697 | // "keypress" ends up matching the _nextExpectedAction since it occurs 698 | // after and that causes the sequence to reset 699 | // 700 | // we ignore keypresses in a sequence that directly follow a keydown 701 | // for the same character 702 | var ignoreThisKeypress = e.type == 'keypress' && _ignoreNextKeypress; 703 | if (e.type == _nextExpectedAction && !_isModifier(character) && !ignoreThisKeypress) { 704 | _resetSequences(doNotReset); 705 | } 706 | 707 | _ignoreNextKeypress = processedSequenceCallback && e.type == 'keydown'; 708 | }; 709 | 710 | /** 711 | * handles a keydown event 712 | * 713 | * @param {Event} e 714 | * @returns void 715 | */ 716 | function _handleKeyEvent(e) { 717 | 718 | // normalize e.which for key events 719 | // @see http://stackoverflow.com/questions/4285627/javascript-keycode-vs-charcode-utter-confusion 720 | if (typeof e.which !== 'number') { 721 | e.which = e.keyCode; 722 | } 723 | 724 | var character = _characterFromEvent(e); 725 | 726 | // no character found then stop 727 | if (!character) { 728 | return; 729 | } 730 | 731 | // need to use === for the character check because the character can be 0 732 | if (e.type == 'keyup' && _ignoreNextKeyup === character) { 733 | _ignoreNextKeyup = false; 734 | return; 735 | } 736 | 737 | self.handleKey(character, _eventModifiers(e), e); 738 | } 739 | 740 | /** 741 | * called to set a 1 second timeout on the specified sequence 742 | * 743 | * this is so after each key press in the sequence you have 1 second 744 | * to press the next key before you have to start over 745 | * 746 | * @returns void 747 | */ 748 | function _resetSequenceTimer() { 749 | clearTimeout(_resetTimer); 750 | _resetTimer = setTimeout(_resetSequences, 1000); 751 | } 752 | 753 | /** 754 | * binds a key sequence to an event 755 | * 756 | * @param {string} combo - combo specified in bind call 757 | * @param {Array} keys 758 | * @param {Function} callback 759 | * @param {string=} action 760 | * @returns void 761 | */ 762 | function _bindSequence(combo, keys, callback, action) { 763 | 764 | // start off by adding a sequence level record for this combination 765 | // and setting the level to 0 766 | _sequenceLevels[combo] = 0; 767 | 768 | /** 769 | * callback to increase the sequence level for this sequence and reset 770 | * all other sequences that were active 771 | * 772 | * @param {string} nextAction 773 | * @returns {Function} 774 | */ 775 | function _increaseSequence(nextAction) { 776 | return function() { 777 | _nextExpectedAction = nextAction; 778 | ++_sequenceLevels[combo]; 779 | _resetSequenceTimer(); 780 | }; 781 | } 782 | 783 | /** 784 | * wraps the specified callback inside of another function in order 785 | * to reset all sequence counters as soon as this sequence is done 786 | * 787 | * @param {Event} e 788 | * @returns void 789 | */ 790 | function _callbackAndReset(e) { 791 | _fireCallback(callback, e, combo); 792 | 793 | // we should ignore the next key up if the action is key down 794 | // or keypress. this is so if you finish a sequence and 795 | // release the key the final key will not trigger a keyup 796 | if (action !== 'keyup') { 797 | _ignoreNextKeyup = _characterFromEvent(e); 798 | } 799 | 800 | // weird race condition if a sequence ends with the key 801 | // another sequence begins with 802 | setTimeout(_resetSequences, 10); 803 | } 804 | 805 | // loop through keys one at a time and bind the appropriate callback 806 | // function. for any key leading up to the final one it should 807 | // increase the sequence. after the final, it should reset all sequences 808 | // 809 | // if an action is specified in the original bind call then that will 810 | // be used throughout. otherwise we will pass the action that the 811 | // next key in the sequence should match. this allows a sequence 812 | // to mix and match keypress and keydown events depending on which 813 | // ones are better suited to the key provided 814 | for (var i = 0; i < keys.length; ++i) { 815 | var isFinal = i + 1 === keys.length; 816 | var wrappedCallback = isFinal ? _callbackAndReset : _increaseSequence(action || _getKeyInfo(keys[i + 1]).action); 817 | _bindSingle(keys[i], wrappedCallback, action, combo, i); 818 | } 819 | } 820 | 821 | /** 822 | * binds a single keyboard combination 823 | * 824 | * @param {string} combination 825 | * @param {Function} callback 826 | * @param {string=} action 827 | * @param {string=} sequenceName - name of sequence if part of sequence 828 | * @param {number=} level - what part of the sequence the command is 829 | * @returns void 830 | */ 831 | function _bindSingle(combination, callback, action, sequenceName, level) { 832 | 833 | // store a direct mapped reference for use with Mousetrap.trigger 834 | self._directMap[combination + ':' + action] = callback; 835 | 836 | // make sure multiple spaces in a row become a single space 837 | combination = combination.replace(/\s+/g, ' '); 838 | 839 | var sequence = combination.split(' '); 840 | var info; 841 | 842 | // if this pattern is a sequence of keys then run through this method 843 | // to reprocess each pattern one key at a time 844 | if (sequence.length > 1) { 845 | _bindSequence(combination, sequence, callback, action); 846 | return; 847 | } 848 | 849 | info = _getKeyInfo(combination, action); 850 | 851 | // make sure to initialize array if this is the first time 852 | // a callback is added for this key 853 | self._callbacks[info.key] = self._callbacks[info.key] || []; 854 | 855 | // remove an existing match if there is one 856 | _getMatches(info.key, info.modifiers, {type: info.action}, sequenceName, combination, level); 857 | 858 | // add this call back to the array 859 | // if it is a sequence put it at the beginning 860 | // if not put it at the end 861 | // 862 | // this is important because the way these are processed expects 863 | // the sequence ones to come first 864 | self._callbacks[info.key][sequenceName ? 'unshift' : 'push']({ 865 | callback: callback, 866 | modifiers: info.modifiers, 867 | action: info.action, 868 | seq: sequenceName, 869 | level: level, 870 | combo: combination 871 | }); 872 | } 873 | 874 | /** 875 | * binds multiple combinations to the same callback 876 | * 877 | * @param {Array} combinations 878 | * @param {Function} callback 879 | * @param {string|undefined} action 880 | * @returns void 881 | */ 882 | self._bindMultiple = function(combinations, callback, action) { 883 | for (var i = 0; i < combinations.length; ++i) { 884 | _bindSingle(combinations[i], callback, action); 885 | } 886 | }; 887 | 888 | // start! 889 | _addEvent(targetElement, 'keypress', _handleKeyEvent); 890 | _addEvent(targetElement, 'keydown', _handleKeyEvent); 891 | _addEvent(targetElement, 'keyup', _handleKeyEvent); 892 | } 893 | 894 | /** 895 | * binds an event to mousetrap 896 | * 897 | * can be a single key, a combination of keys separated with +, 898 | * an array of keys, or a sequence of keys separated by spaces 899 | * 900 | * be sure to list the modifier keys first to make sure that the 901 | * correct key ends up getting bound (the last key in the pattern) 902 | * 903 | * @param {string|Array} keys 904 | * @param {Function} callback 905 | * @param {string=} action - 'keypress', 'keydown', or 'keyup' 906 | * @returns void 907 | */ 908 | Mousetrap.prototype.bind = function(keys, callback, action) { 909 | var self = this; 910 | keys = keys instanceof Array ? keys : [keys]; 911 | self._bindMultiple.call(self, keys, callback, action); 912 | return self; 913 | }; 914 | 915 | /** 916 | * unbinds an event to mousetrap 917 | * 918 | * the unbinding sets the callback function of the specified key combo 919 | * to an empty function and deletes the corresponding key in the 920 | * _directMap dict. 921 | * 922 | * TODO: actually remove this from the _callbacks dictionary instead 923 | * of binding an empty function 924 | * 925 | * the keycombo+action has to be exactly the same as 926 | * it was defined in the bind method 927 | * 928 | * @param {string|Array} keys 929 | * @param {string} action 930 | * @returns void 931 | */ 932 | Mousetrap.prototype.unbind = function(keys, action) { 933 | var self = this; 934 | return self.bind.call(self, keys, function() {}, action); 935 | }; 936 | 937 | /** 938 | * triggers an event that has already been bound 939 | * 940 | * @param {string} keys 941 | * @param {string=} action 942 | * @returns void 943 | */ 944 | Mousetrap.prototype.trigger = function(keys, action) { 945 | var self = this; 946 | if (self._directMap[keys + ':' + action]) { 947 | self._directMap[keys + ':' + action]({}, keys); 948 | } 949 | return self; 950 | }; 951 | 952 | /** 953 | * resets the library back to its initial state. this is useful 954 | * if you want to clear out the current keyboard shortcuts and bind 955 | * new ones - for example if you switch to another page 956 | * 957 | * @returns void 958 | */ 959 | Mousetrap.prototype.reset = function() { 960 | var self = this; 961 | self._callbacks = {}; 962 | self._directMap = {}; 963 | return self; 964 | }; 965 | 966 | /** 967 | * should we stop this event before firing off callbacks 968 | * 969 | * @param {Event} e 970 | * @param {Element} element 971 | * @return {boolean} 972 | */ 973 | Mousetrap.prototype.stopCallback = function(e, element) { 974 | var self = this; 975 | 976 | // if the element has the class "mousetrap" then no need to stop 977 | if ((' ' + element.className + ' ').indexOf(' mousetrap ') > -1) { 978 | return false; 979 | } 980 | 981 | if (_belongsTo(element, self.target)) { 982 | return false; 983 | } 984 | 985 | // stop for input, select, and textarea 986 | return element.tagName == 'INPUT' || element.tagName == 'SELECT' || element.tagName == 'TEXTAREA' || element.isContentEditable; 987 | }; 988 | 989 | /** 990 | * exposes _handleKey publicly so it can be overwritten by extensions 991 | */ 992 | Mousetrap.prototype.handleKey = function() { 993 | var self = this; 994 | return self._handleKey.apply(self, arguments); 995 | }; 996 | 997 | /** 998 | * allow custom key mappings 999 | */ 1000 | Mousetrap.addKeycodes = function(object) { 1001 | for (var key in object) { 1002 | if (object.hasOwnProperty(key)) { 1003 | _MAP[key] = object[key]; 1004 | } 1005 | } 1006 | _REVERSE_MAP = null; 1007 | }; 1008 | 1009 | /** 1010 | * Init the global mousetrap functions 1011 | * 1012 | * This method is needed to allow the global mousetrap functions to work 1013 | * now that mousetrap is a constructor function. 1014 | */ 1015 | Mousetrap.init = function() { 1016 | var documentMousetrap = Mousetrap(document); 1017 | for (var method in documentMousetrap) { 1018 | if (method.charAt(0) !== '_') { 1019 | Mousetrap[method] = (function(method) { 1020 | return function() { 1021 | return documentMousetrap[method].apply(documentMousetrap, arguments); 1022 | }; 1023 | } (method)); 1024 | } 1025 | } 1026 | }; 1027 | 1028 | Mousetrap.init(); 1029 | 1030 | // expose mousetrap to the global object 1031 | window.Mousetrap = Mousetrap; 1032 | 1033 | // expose as a common js module 1034 | if (typeof module !== 'undefined' && module.exports) { 1035 | module.exports = Mousetrap; 1036 | } 1037 | 1038 | // expose mousetrap as an AMD module 1039 | if (typeof define === 'function' && define.amd) { 1040 | define(function() { 1041 | return Mousetrap; 1042 | }); 1043 | } 1044 | }) (typeof window !== 'undefined' ? window : null, typeof window !== 'undefined' ? document : null); 1045 | -------------------------------------------------------------------------------- /src/vendor/unsplash-source.js: -------------------------------------------------------------------------------- 1 | /*! https://unsplash.com unsplash-source-js - v1.0.0 - 2015-11-04 2 | 3 | $$\ $$\ $$\ $$\ 4 | $$ | $$ | $$ | $$ | 5 | $$ | $$ |$$$$$$$\ $$$$$$$\ $$$$$$\ $$ | $$$$$$\ $$$$$$$\ $$$$$$$\ 6 | $$ | $$ |$$ __$$\ $$ _____|$$ __$$\ $$ | \____$$\ $$ _____|$$ __$$\ 7 | $$ | $$ |$$ | $$ |\$$$$$$\ $$ / $$ |$$ | $$$$$$$ |\$$$$$$\ $$ | $$ | 8 | $$ | $$ |$$ | $$ | \____$$\ $$ | $$ |$$ |$$ __$$ | \____$$\ $$ | $$ | 9 | \$$$$$$ |$$ | $$ |$$$$$$$ |$$$$$$$ |$$ |\$$$$$$$ |$$$$$$$ |$$ | $$ | 10 | \______/ \__| \__|\_______/ $$ ____/ \__| \_______|\_______/ \__| \__| 11 | $$ | 12 | $$ | 13 | \__| 14 | */ 15 | 16 | if (!Array.isArray) { 17 | Array.isArray = function(arg) { 18 | return Object.prototype.toString.call(arg) === '[object Array]'; 19 | }; 20 | } 21 | 22 | if (!String.prototype.trim) { 23 | String.prototype.trim = function () { 24 | return this.replace(/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g, ''); 25 | }; 26 | } 27 | 28 | if (!Array.prototype.forEach) { 29 | 30 | Array.prototype.forEach = function(callback, thisArg) { 31 | 32 | var T, k; 33 | 34 | if (this == null) { 35 | throw new TypeError(' this is null or not defined'); 36 | } 37 | 38 | // 1. Let O be the result of calling ToObject passing the |this| value as the argument. 39 | var O = Object(this); 40 | 41 | // 2. Let lenValue be the result of calling the Get internal method of O with the argument "length". 42 | // 3. Let len be ToUint32(lenValue). 43 | var len = O.length >>> 0; 44 | 45 | // 4. If IsCallable(callback) is false, throw a TypeError exception. 46 | // See: http://es5.github.com/#x9.11 47 | if (typeof callback !== "function") { 48 | throw new TypeError(callback + ' is not a function'); 49 | } 50 | 51 | // 5. If thisArg was supplied, let T be thisArg; else let T be undefined. 52 | if (arguments.length > 1) { 53 | T = thisArg; 54 | } 55 | 56 | // 6. Let k be 0 57 | k = 0; 58 | 59 | // 7. Repeat, while k < len 60 | while (k < len) { 61 | 62 | var kValue; 63 | 64 | // a. Let Pk be ToString(k). 65 | // This is implicit for LHS operands of the in operator 66 | // b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk. 67 | // This step can be combined with c 68 | // c. If kPresent is true, then 69 | if (k in O) { 70 | 71 | // i. Let kValue be the result of calling the Get internal method of O with argument Pk. 72 | kValue = O[k]; 73 | 74 | // ii. Call the Call internal method of callback with T as the this value and 75 | // argument list containing kValue, k, and O. 76 | callback.call(T, kValue, k, O); 77 | } 78 | // d. Increase k by 1. 79 | k++; 80 | } 81 | // 8. return undefined 82 | }; 83 | } 84 | 85 | (function (root, undefined) { 86 | "use strict"; 87 | 88 | var UnsplashPhoto = function () { 89 | this.version = "1.0.0"; 90 | this.url = "https://source.unsplash.com"; 91 | this.dimensions = {}; 92 | this.scope = "featured"; 93 | this.randomizationInterval = "perRequest"; 94 | 95 | return this; 96 | }; 97 | 98 | /** 99 | * Finds a photo by its specific public ID 100 | * @param {Int} id 101 | * @return {UnsplashPhoto} 102 | */ 103 | UnsplashPhoto.prototype.find = function (id) { 104 | this.id = id; 105 | 106 | return this; 107 | }; 108 | 109 | /** 110 | * Sets the width of the photo 111 | * 112 | * Note: crops if necessary to maintain the aspect ratio 113 | * 114 | * @param {Int} width 115 | * @return {UnsplashPhoto} 116 | */ 117 | UnsplashPhoto.prototype.width = function (width) { 118 | this.dimensions.width = width; 119 | 120 | return this; 121 | }; 122 | 123 | /** 124 | * Sets the height of the photo 125 | * 126 | * Note: crops if necessary to maintain the aspect ratio 127 | * 128 | * @param {Int} height 129 | * @return {UnsplashPhoto} 130 | */ 131 | UnsplashPhoto.prototype.height = function (height) { 132 | this.dimensions.height = height; 133 | 134 | return this; 135 | }; 136 | 137 | /** 138 | * Shorthand for setting the photo dimensions 139 | * 140 | * Note: crops if necessary to maintain the aspect ratio 141 | * 142 | * @param {Int} width 143 | * @param {Int} height (optional) 144 | * @return {UnsplashPhoto} 145 | */ 146 | UnsplashPhoto.prototype.size = function (width, height) { 147 | this.dimensions = { 148 | width: width, 149 | height: height || width 150 | }; 151 | 152 | return this; 153 | }; 154 | 155 | /** 156 | * Sets the randomization interval 157 | * 158 | * Note: only accepts three possible values (null, daily, or weekly) 159 | * @param {String} interval 160 | * @return {UnsplashPhoto} 161 | */ 162 | UnsplashPhoto.prototype.randomize = function (interval) { 163 | if (interval == "daily" || interval == "weekly") { 164 | this.randomizationInterval = interval; 165 | } else { 166 | this.randomizationInterval = "perRequest"; 167 | } 168 | 169 | return this; 170 | }; 171 | 172 | /** 173 | * Sets the scope to `all` (instead of `featured`) 174 | * @return {UnsplashPhoto} 175 | */ 176 | UnsplashPhoto.prototype.all = function () { 177 | this.scope = "all"; 178 | 179 | return this; 180 | }; 181 | 182 | /** 183 | * Limits the photo to having tags or locations matching the keywords 184 | * @param {[Array || String]} keywords 185 | * @return {UnsplashPhoto} 186 | */ 187 | UnsplashPhoto.prototype.of = function (keywords) { 188 | var sanitizedKeywords = []; 189 | 190 | // Handle a string of comma-separated keywords 191 | if (!Array.isArray(keywords)) { 192 | keywords = keywords.split(","); 193 | } 194 | 195 | // Remove any leading or trailing whitespace from each keyword 196 | keywords.forEach(function (keyword) { 197 | sanitizedKeywords.push(keyword.trim()); 198 | }); 199 | 200 | this.keywords = sanitizedKeywords.join(","); 201 | this.keywords = encodeURI(this.keywords); 202 | 203 | return this; 204 | }; 205 | 206 | /** 207 | * Limits the photos to a specific photographer 208 | * @param {String} username 209 | * @return {UnsplashPhoto} 210 | */ 211 | UnsplashPhoto.prototype.fromUser = function (username) { 212 | this.username = username; 213 | 214 | return this; 215 | }; 216 | 217 | /** 218 | * Limits the photos to a specific category 219 | * @param {String} category 220 | * @return {UnsplashPhoto} 221 | */ 222 | UnsplashPhoto.prototype.fromCategory = function (category) { 223 | this.category = category; 224 | 225 | return this; 226 | }; 227 | 228 | /** 229 | * Returns true if the photo has dimensions set 230 | * @return {Boolean} 231 | */ 232 | UnsplashPhoto.prototype._hasDimensions = function () { 233 | return !!this.dimensions.width && !!this.dimensions.height; 234 | }; 235 | 236 | /** 237 | * Appends the photo dimensions to the URL 238 | * @return {String} the photo URL 239 | */ 240 | UnsplashPhoto.prototype._appendDimensions = function () { 241 | if (this._hasDimensions()) { 242 | this.url += "/" + this.dimensions.width + "x" + this.dimensions.height; 243 | } 244 | 245 | return this.url; 246 | }; 247 | 248 | /** 249 | * Appends the scope to the URL 250 | * @return {String} the photo URL 251 | */ 252 | UnsplashPhoto.prototype._appendScope = function () { 253 | if (this.scope == "all") { 254 | this.url += "/all"; 255 | } 256 | 257 | return this.url; 258 | }; 259 | 260 | /** 261 | * Appends the keywords to the URL 262 | * @return {String} the photo URL 263 | */ 264 | UnsplashPhoto.prototype._appendKeywords = function () { 265 | if (this.keywords) { 266 | this.url += "?" + this.keywords; 267 | } 268 | 269 | return this.url; 270 | }; 271 | 272 | /** 273 | * Appends the randomization interval to the URL 274 | * @param {Boolean} includeRandomPath include the `/random` path in the URL 275 | * @return {String} the photo URL 276 | */ 277 | UnsplashPhoto.prototype._appendRandomization = function (includeRandomPath) { 278 | if (includeRandomPath && this.randomizationInterval == "perRequest") { 279 | this.url += "/random"; 280 | } else if (this.randomizationInterval == "daily") { 281 | this.url += "/daily"; 282 | } else if (this.randomizationInterval == "weekly") { 283 | this.url += "/weekly"; 284 | } 285 | 286 | return this.url; 287 | }; 288 | 289 | /** 290 | * Creates the URL based on the previous actions 291 | * @return {String} the photo URL 292 | */ 293 | UnsplashPhoto.prototype.fetch = function () { 294 | if (!!this.id) { 295 | this.url += "/" + this.id; 296 | this._appendDimensions(); 297 | return this.url; 298 | 299 | } else if (!!this.username) { 300 | this.url += "/user/" + this.username; 301 | this._appendScope(); 302 | this._appendDimensions(); 303 | this._appendRandomization(false); 304 | this._appendKeywords(); 305 | return this.url; 306 | 307 | } else if (!!this.category) { 308 | this.url += "/category/" + this.category; 309 | this._appendScope(); 310 | this._appendDimensions(); 311 | this._appendRandomization(false); 312 | this._appendKeywords(); 313 | return this.url; 314 | 315 | } else { 316 | this._appendScope(); 317 | this._appendDimensions(); 318 | this._appendRandomization(true); 319 | this._appendKeywords(); 320 | return this.url; 321 | 322 | } 323 | }; 324 | 325 | root.UnsplashPhoto = UnsplashPhoto; 326 | 327 | })(this); 328 | --------------------------------------------------------------------------------