themummy
Version:
UNSTABLE. codecoverage married with integration testing. currently a work in progress
720 lines (657 loc) • 24.7 kB
JavaScript
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
require('./lib/jonoShortcuts.js')
var scroll_setup = require('./scroll_setup.js')
var txt = require('./txt.js')
var vue_o = {
el: '#app',
data: {
txt,
nav: {
items: [
{id: 'about', name: 'about'},
{id: 'webdev', name: 'web development'},
{id: 'contact', name: 'contact'}
],
show_menu: false,
active: 'about',
smallscreen: false
}
},
methods: {
newtab: function (url) {
var win = window.open(url, '_blank')
win.addEventListener('load', () => {
win.focus()
console.log('ok')
})
console.log('ok')
return win
},
send_email: function (e) {
// form submit event
let vm = this
let name = d.qs('#inputName').value
let email = d.qs('#inputEmail').value
let body = d.qs('#message').value
var str = `mailto:jono-lee@hotmail.co.uk?subject=${name}, contact form blogjono.com &body=${body}`
vm.newtab(str)
e.preventDefault()
}
},
mounted: function () {
let vm = this
vm.nav_resizing__init()
vm.scroll__init()
}
}
scroll_setup(vue_o) // TODO improve nav item active, by percentage of el in view
w.vm = new Vue(vue_o)
},{"./lib/jonoShortcuts.js":5,"./scroll_setup.js":6,"./txt.js":7}],2:[function(require,module,exports){
/*!
* enquire.js v2.1.2 - Awesome Media Queries in JavaScript
* Copyright (c) 2014 Nick Williams - http://wicky.nillia.ms/enquire.js
* License: MIT (http://www.opensource.org/licenses/mit-license.php)
*/
!function(a,b,c){var d=window.matchMedia;"undefined"!=typeof module&&module.exports?module.exports=c(d):"function"==typeof define&&define.amd?define(function(){return b[a]=c(d)}):b[a]=c(d)}("enquire",this,function(a){"use strict";function b(a,b){var c,d=0,e=a.length;for(d;e>d&&(c=b(a[d],d),c!==!1);d++);}function c(a){return"[object Array]"===Object.prototype.toString.apply(a)}function d(a){return"function"==typeof a}function e(a){this.options=a,!a.deferSetup&&this.setup()}function f(b,c){this.query=b,this.isUnconditional=c,this.handlers=[],this.mql=a(b);var d=this;this.listener=function(a){d.mql=a,d.assess()},this.mql.addListener(this.listener)}function g(){if(!a)throw new Error("matchMedia not present, legacy browsers require a polyfill");this.queries={},this.browserIsIncapable=!a("only all").matches}return e.prototype={setup:function(){this.options.setup&&this.options.setup(),this.initialised=!0},on:function(){!this.initialised&&this.setup(),this.options.match&&this.options.match()},off:function(){this.options.unmatch&&this.options.unmatch()},destroy:function(){this.options.destroy?this.options.destroy():this.off()},equals:function(a){return this.options===a||this.options.match===a}},f.prototype={addHandler:function(a){var b=new e(a);this.handlers.push(b),this.matches()&&b.on()},removeHandler:function(a){var c=this.handlers;b(c,function(b,d){return b.equals(a)?(b.destroy(),!c.splice(d,1)):void 0})},matches:function(){return this.mql.matches||this.isUnconditional},clear:function(){b(this.handlers,function(a){a.destroy()}),this.mql.removeListener(this.listener),this.handlers.length=0},assess:function(){var a=this.matches()?"on":"off";b(this.handlers,function(b){b[a]()})}},g.prototype={register:function(a,e,g){var h=this.queries,i=g&&this.browserIsIncapable;return h[a]||(h[a]=new f(a,i)),d(e)&&(e={match:e}),c(e)||(e=[e]),b(e,function(b){d(b)&&(b={match:b}),h[a].addHandler(b)}),this},unregister:function(a,b){var c=this.queries[a];return c&&(b?c.removeHandler(b):(c.clear(),delete this.queries[a])),this}},new g});
},{}],3:[function(require,module,exports){
module.exports = function(){
var events = {}
var eventsystem = {
on: function (eventName, fn) {
events[eventName] = events[eventName] || []
events[eventName].push(fn)
},
off: function(eventName, fn) {
if (events[eventName]) {
for (var i = 0; i < events[eventName].length; i++) {
if (events[eventName][i] === fn) {
events[eventName].splice(i, 1)
break
}
}
}
},
emit: function (eventName, data) {
if (events[eventName]) {
events[eventName].forEach(function(fn) {
fn(data)
})
}
}
}
eventsystem.events = events
return eventsystem
}
},{}],4:[function(require,module,exports){
// spy = require('jonoScrollspy.js')
// scrollableEl = d.qs('.main')
// myspy = spy(scrollableEl)
// spy.init()
// cb = function({case: true|false, el}){}
// spy.watch(el, cb)
// spy.watch(anotherEl, cb)
var eventsSystem = require('./eventSystem.js')
function spy(scrollableEl) {
var eve = eventsSystem()
_frame = scrollableEl
_watchin = []
function init(){
scrollableEl.on('scroll', refresh)
w.on('resize', refresh)
}
function destroy(){
scrollableEl.off('scroll', refresh)
w.off('resize', refresh)
}
function refresh() {
// update could be throttled
_watchin = _watchin.map(function(o){
let scrolloci = _frame.scrollTop
if (o.el === 'top') {
if (scrolloci === 0) {
o.cb()
}
return o
}
if (o.el === 'bottom') {
if (scrolloci === _frame.scrollTopMax) {
o.cb()
}
return o
}
// o.frect = _frame.getBoundingClientRect()
// o.erect = o.el.getBoundingClientRect()
let y = -1*(_frame.getBoundingClientRect().top - o.el.getBoundingClientRect().top - _frame.scrollTop)
let cond = scrolloci >= y
if (cond === true && o.cond === false ||
cond === false && o.cond === true /* scrolling up */) {
if (typeof o.cb === 'function') {
o.cb({
is_scrolling_down: cond,
is_scrolling_up: !cond,
el: o.el
})
}
}
o.y = y
o.scrolloci = scrolloci
o.cond = cond
return o
})
}
function watch(el, cb) {
_watchin.push({
el: el,
cb: cb
})
}
return {
init: init,
destroy: destroy,
refresh: refresh,
watch: watch,
_watchin: _watchin
}
}
module.exports = spy
},{"./eventSystem.js":3}],5:[function(require,module,exports){
// Base Browser stuff
window.w = window
w.D = Document
w.d = document
Element.prototype.qs = Element.prototype.querySelector
Element.prototype.qsa = Element.prototype.querySelectorAll
D.prototype.qs = Document.prototype.querySelector
D.prototype.qsa = Document.prototype.querySelectorAll
EventTarget.prototype.on = EventTarget.prototype.addEventListener
EventTarget.prototype.off = EventTarget.prototype.removeEventListener
EventTarget.prototype.emit = EventTarget.prototype.dispatchEvent
// http://stackoverflow.com/questions/11761881/javascript-dom-find-element-index-in-container
Element.prototype.getNodeIndex = function() {
var node = this
var index = 0;
while ( (node = node.previousSibling) ) {
if (node.nodeType != 3 || !/^\s*$/.test(node.data)) {
index++;
}
}
return index;
}
NodeList.prototype.toArray = function() {
return Array.prototype.map.call(this, function(item){
return item
})
}
HTMLCollection.prototype.toArray = function() {
return NodeList.prototype.toArray.call(this)
}
Node.prototype.prependChild = function(el) {
var parentNode = this
parentNode.insertBefore(el, parentNode.firstChild)
}
},{}],6:[function(require,module,exports){
var enquire = require('./lib/enquire.min.js')
w.zenscroll = require('zenscroll')
w.spyScroll = require('./lib/jonoScrollspy.js')
module.exports = function({data, methods}) {
methods.nav_resizing__init = function(){
let vm = this
// media queries within js
enquire.register('screen and (max-width: 500px)', {
deferSetup: false,
setup: function() {},
match : function() {
vm.nav.smallscreen = true
},
unmatch : function() {
vm.nav.smallscreen = false
vm.nav.show_menu = false
}
})
}
var main = undefined
// scroll whereabouts and animated scroll
methods.scroll__init = function() {
let vm = this
main = d.qs('.main')
var spy = spyScroll(main)
spy.init()
function spyCb(o){
// console.log('spy:',o)
if (o.is_scrolling_down){
vm.nav.active = o.el.id
}
if (o.is_scrolling_up) {
// vm.nav.active = o.el.id
// goto prev el
let i = vm.nav.items.findIndex(function(item){
return item.id === o.el.id
})
if (i === 0) {i++}
var item = vm.nav.items[i-1]
vm.nav.active = item.id
}
}
vm.nav.items.forEach(function(item){
spy.watch(d.qs('#'+item.id), spyCb)
})
spy.watch('bottom', function(){
const last = vm.nav.items[vm.nav.items.length-1]
console.log('spy: bottom')
vm.nav.active = last.id
})
spy.watch('top', function(){
const first = vm.nav.items[0]
console.log('spy: top')
vm.nav.active = first.id
})
w.zs = zenscroll.createScroller(main, 500, 0)
}
methods.nav_scroll = function(id) {
let vm = this
let el = d.qs('#'+id)
if (el === null || main === undefined || zs === undefined) {return}
let y = -1*(main.getBoundingClientRect().top - el.getBoundingClientRect().top - main.scrollTop)
y = Math.round(y)
console.log("nav_scroll:", id, y)
if ( zs.moving() ) { zs.stop() }
zs.toY(y, 500, function(){
vm.nav.active = id
})
}
}
},{"./lib/enquire.min.js":2,"./lib/jonoScrollspy.js":4,"zenscroll":8}],7:[function(require,module,exports){
module.exports = {
fullname: 'Jonathan T L Lee',
section2: {
scrollid: 'about',
heading: 'About me',
portrait: '/headshot.jpg', // 'https://avatars3.githubusercontent.com/u/11339630?v=3&s=466',
portrait_alt: 'a portrait photo of Jonathan Lee smiling',
para: `
Hello!
<br><br>
I'm a Javascript Fullstack Developer from Mansfield, GB.
<br><br>
Welcome to my portfolio page, here you can see the product/s of my web development learning.
<br><br>
So take a look and feel free to message/contact me :)
`
},
section4: {
scrollid: 'webdev',
heading: 'Web Development',
gallery: [
{
heading: 'opentorah.uk',
para: 'Is atm a dual pane bible reader with hebrew and english. Tools used were nodejs, mongodb and expressjs.',
img: '/portfolio/opentorah.png',
external_link: 'http://opentorah.uk/reader/pro.c3'
},
{
heading: 'Naughts and Crosses',
para: 'Want a game of naughts and crosses. sure have some fun. I used browserify inorder to separate the logic and the view javascript',
img: '/portfolio/xo.png',
// external_link: 'http://s.codepen.io/Lee182/debug/KgyqrX'
external_link: '/projects/fcc-f-naughts_and_crosses/'
},
{
heading: 'Wikipedia Searcher',
para: 'Search wikipedia with ease',
img: '/portfolio/wiki.png',
// external_link: 'http://s.codepen.io/Lee182/debug/zBJXxy'
external_link: '/projects/fcc-f-wiki_viewer/'
},
{
heading: 'Camper Leaderboard',
para: 'Dynamic table made with the help of vuejs',
img: '/portfolio/camper-leaderboard.png',
// external_link: 'http://s.codepen.io/Lee182/debug/mAqKxo'
external_link: '/projects/fcc-fd-camper_leaderboard/'
},
{
heading: 'Simon Game',
para: 'The simon memory game made with javascript.',
img: '/portfolio/simon-game.png',
// external_link: 'http://s.codepen.io/Lee182/debug/mOmaWr'
external_link: '/projects/fcc-f-simon_memory_game/'
},
{
heading: 'Pomodoro Clock',
para: 'Helps you to have a break whilst working',
img: '/portfolio/pomodoro.png',
// external_link: 'http://s.codepen.io/Lee182/debug/MbozLo'
external_link: '/projects/fcc-f-pomodoro_clock/'
},
{
heading: 'Url Shortener',
para: 'Uses a base62 encoding (ie alphabet uppercase & lowercase, and numbers), to convert a number to a string which is used as a path for a given url. Project utilises nodejs and mongodb',
img: '/portfolio/url-shortener.png',
external_link: 'https://url.blogjono.com/'
},
{
heading: 'Voting App',
para: 'Uses socket.io to cast votes, and add options to vote for. Auth via twitter login',
img: '/portfolio/voting-app.png',
external_link: 'https://vote.blogjono.com/'
},
{
heading: 'twnight',
para: 'Twitter Nightlife; uses the yelp api, and puts your twitter handle on a rsvp list. uses mongodb expiresAt key to automatically remove the document data.',
img: '/portfolio/twnight.png',
external_link: 'https://twnight.blogjono.com/'
},
{
heading: 'Stock market chart',
para: 'Track the stock market, uses yahoo-finance api, and sends the data over the websockets in bson(binary json) format, given that most of the data is numbers.',
img: '/portfolio/fccstocks.png',
external_link: 'https://stocks.blogjono.com/'
},
{
heading: 'Pinterest clone',
para: 'Basic Image hosting site, uses vuejs with my own created routing. Also image upload to nodejs.',
img: '/portfolio/fcc-pin.png',
external_link: 'https://fcc-pin.blogjono.com/'
},
{
heading: '#booktrade',
para: 'book trading single page webapp, self hosted with LetsEncrypt and expressjs.',
img: '/portfolio/booktrade.png',
external_link: 'https://booktrade.blogjono.com/'
}
],
comingsoon: [1]
},
section5: {
scrollid: 'contact',
heading: 'Contact Me'
},
social: {
facebook: 'https://facebook.com/jleey96',
github: 'https://github.com/Lee182',
soundcloud: 'https://soundcloud.com/jonolee182',
youtube: 'https://www.youtube.com/user/TheJTLLee',
linkedin: 'https://uk.linkedin.com/in/jonathan-lee-48b222100',
freecodecamp: 'https://freecodecamp.com/Lee182',
codepen: 'http://codepen.io/Lee182/'
}
}
},{}],8:[function(require,module,exports){
/**
* Zenscroll 3.3.0
* https://github.com/zengabor/zenscroll/
*
* Copyright 2015–2016 Gabor Lenard
*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <http://unlicense.org>
*
*/
/*jshint devel:true, asi:true */
/*global define, module */
(function (root, factory) {
if (typeof define === "function" && define.amd) {
define([], factory())
} else if (typeof module === "object" && module.exports) {
module.exports = factory()
} else {
root.zenscroll = factory()
}
}(this, function () {
"use strict"
// Detect if the browser already supports native smooth scrolling (e.g., Firefox 36+ and Chrome 49+) and it is enabled:
var isNativeSmoothScrollEnabledOn = function (elem) {
return ("getComputedStyle" in window) &&
window.getComputedStyle(elem)["scroll-behavior"] === "smooth"
}
// Exit if it’s not a browser environment:
if (typeof window === "undefined" || !("document" in window)) {
return {}
}
var createScroller = function (scrollContainer, defaultDuration, edgeOffset) {
defaultDuration = defaultDuration || 999 //ms
if (!edgeOffset && edgeOffset !== 0) {
// When scrolling, this amount of distance is kept from the edges of the scrollContainer:
edgeOffset = 9 //px
}
var scrollTimeoutId
var setScrollTimeoutId = function (newValue) {
scrollTimeoutId = newValue
}
var docElem = document.documentElement
var getScrollTop = function () {
if (scrollContainer) {
return scrollContainer.scrollTop
} else {
return window.scrollY || docElem.scrollTop
}
}
var getViewHeight = function () {
if (scrollContainer) {
return Math.min(scrollContainer.offsetHeight, window.innerHeight)
} else {
return window.innerHeight || docElem.clientHeight
}
}
var getRelativeTopOf = function (elem) {
if (scrollContainer) {
return elem.offsetTop
} else {
return elem.getBoundingClientRect().top + getScrollTop() - docElem.offsetTop
}
}
/**
* Immediately stops the current smooth scroll operation
*/
var stopScroll = function () {
clearTimeout(scrollTimeoutId)
setScrollTimeoutId(0)
}
/**
* Scrolls to a specific vertical position in the document.
*
* @param {endY} The vertical position within the document.
* @param {duration} Optionally the duration of the scroll operation.
* If 0 or not provided it is automatically calculated based on the
* distance and the default duration.
* @param {onDone} Callback function to be invoken once the scroll finishes.
*/
var scrollToY = function (endY, duration, onDone) {
stopScroll()
if (isNativeSmoothScrollEnabledOn(scrollContainer ? scrollContainer : document.body)) {
(scrollContainer || window).scrollTo(0, endY)
if (onDone) {
onDone()
}
} else {
var startY = getScrollTop()
var distance = Math.max(endY,0) - startY
duration = duration || Math.min(Math.abs(distance), defaultDuration)
var startTime = new Date().getTime();
(function loopScroll() {
setScrollTimeoutId(setTimeout(function () {
var p = Math.min((new Date().getTime() - startTime) / duration, 1) // percentage
var y = Math.max(Math.floor(startY + distance*(p < 0.5 ? 2*p*p : p*(4 - p*2)-1)), 0)
if (scrollContainer) {
scrollContainer.scrollTop = y
} else {
window.scrollTo(0, y)
}
if (p < 1 && (getViewHeight() + y) < (scrollContainer || docElem).scrollHeight) {
loopScroll()
} else {
setTimeout(stopScroll, 99) // with cooldown time
if (onDone) {
onDone()
}
}
}, 9))
})()
}
}
/**
* Scrolls to the top of a specific element.
*
* @param {elem} The element.
* @param {duration} Optionally the duration of the scroll operation.
* A value of 0 is ignored.
* @param {onDone} Callback function to be invoken once the scroll finishes.
* @returns {endY} The new vertical scoll position that will be valid once the scroll finishes.
*/
var scrollToElem = function (elem, duration, onDone) {
var endY = getRelativeTopOf(elem) - edgeOffset
scrollToY(endY, duration, onDone)
return endY
}
/**
* Scrolls an element into view if necessary.
*
* @param {elem} The element.
* @param {duration} Optionally the duration of the scroll operation.
* A value of 0 is ignored.
* @param {onDone} Callback function to be invoken once the scroll finishes.
*/
var scrollIntoView = function (elem, duration, onDone) {
var elemHeight = elem.getBoundingClientRect().height
var elemTop = getRelativeTopOf(elem)
var elemBottom = elemTop + elemHeight
var containerHeight = getViewHeight()
var containerTop = getScrollTop()
var containerBottom = containerTop + containerHeight
if ((elemTop - edgeOffset) < containerTop || (elemHeight + edgeOffset) > containerHeight) {
// Element is clipped at top or is higher than screen.
scrollToElem(elem, duration, onDone)
} else if ((elemBottom + edgeOffset) > containerBottom) {
// Element is clipped at the bottom.
scrollToY(elemBottom - containerHeight + edgeOffset, duration, onDone)
} else if (onDone) {
onDone()
}
}
/**
* Scrolls to the center of an element.
*
* @param {elem} The element.
* @param {duration} Optionally the duration of the scroll operation.
* @param {offset} Optionally the offset of the top of the element from the center of the screen.
* A value of 0 is ignored.
* @param {onDone} Callback function to be invoken once the scroll finishes.
*/
var scrollToCenterOf = function (elem, duration, offset, onDone) {
scrollToY(
Math.max(
getRelativeTopOf(elem) - getViewHeight()/2 + (offset || elem.getBoundingClientRect().height/2),
0
),
duration,
onDone
)
}
/**
* Changes default settings for this scroller.
*
* @param {newDefaultDuration} New value for default duration, used for each scroll method by default.
* Ignored if 0 or falsy.
* @param {newEdgeOffset} New value for the edge offset, used by each scroll method by default.
*/
var setup = function (newDefaultDuration, newEdgeOffset) {
if (newDefaultDuration) {
defaultDuration = newDefaultDuration
}
if (newEdgeOffset === 0 || newEdgeOffset) {
edgeOffset = newEdgeOffset
}
}
return {
setup: setup,
to: scrollToElem,
toY: scrollToY,
intoView: scrollIntoView,
center: scrollToCenterOf,
stop: stopScroll,
moving: function () { return !!scrollTimeoutId },
getY: getScrollTop
}
}
// Create a scroller for the browser window, omitting parameters:
var defaultScroller = createScroller()
// Create listeners for the documentElement only & exclude IE8-
if ("addEventListener" in window && !(isNativeSmoothScrollEnabledOn(document.body) || window.noZensmooth)) {
if ("scrollRestoration" in history) {
history.scrollRestoration = "manual"
window.addEventListener("popstate", function (event) {
if (event.state && "scrollY" in event.state) {
defaultScroller.toY(event.state.scrollY)
}
}, false)
}
var replaceUrl = function (hash, newY) {
try {
history.replaceState({scrollY:defaultScroller.getY()}, "") // remember the scroll position before scrolling
history.pushState({scrollY:newY}, "", window.location.href.split("#")[0] + hash) // remember the new scroll position (which will be after scrolling)
} catch (e) {
// To avoid the Security exception in Chrome when the page was opened via the file protocol, e.g., file://index.html
}
}
window.addEventListener("click", function (event) {
var anchor = event.target
while (anchor && anchor.tagName !== "A") {
anchor = anchor.parentNode
}
// Only handle links that were clicked with the primary button, without modifier keys:
if (!anchor || event.which !== 1 || event.shiftKey || event.metaKey || event.ctrlKey || event.altKey) {
return
}
var href = anchor.getAttribute("href") || ""
if (href.indexOf("#") === 0) {
if (href === "#") {
event.preventDefault()
defaultScroller.toY(0)
replaceUrl("", 0)
} else {
var targetId = anchor.hash.substring(1)
var targetElem = document.getElementById(targetId)
if (targetElem) {
event.preventDefault()
replaceUrl("#" + targetId, defaultScroller.to(targetElem))
}
}
}
}, false)
}
return {
// Expose the "constructor" that can create a new scroller:
createScroller: createScroller,
// Surface the methods of the default scroller:
setup: defaultScroller.setup,
to: defaultScroller.to,
toY: defaultScroller.toY,
intoView: defaultScroller.intoView,
center: defaultScroller.center,
stop: defaultScroller.stop,
moving: defaultScroller.moving
}
}));
},{}]},{},[1]);