diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index fff4b5ecb..8701552b9 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -24,7 +24,6 @@ jobs: - run: npm run build env: CI: true - GA_KEY: ${{ secrets.GA_KEY }} - name: Deploy 🚀 uses: JamesIves/github-pages-deploy-action@4.1.4 with: diff --git a/.gitignore b/.gitignore index f21438c31..7aa2a5512 100644 --- a/.gitignore +++ b/.gitignore @@ -65,3 +65,6 @@ dist/ # Editor specific files .vscode/ + +# Metalsmith +.metalsmith/ diff --git a/.markdownlintrc b/.markdownlintrc deleted file mode 100644 index 62baab583..000000000 --- a/.markdownlintrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "first-line-h1": false -} diff --git a/.nvmrc b/.nvmrc index 2831228ed..9de225682 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -14.17.1 +lts/iron diff --git a/README.md b/README.md index e6f13dfac..c644710de 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,16 @@ -[![Build Status](https://travis-ci.org/asyncjs/async-website.svg?branch=master)](https://travis-ci.org/asyncjs/async-website) # Async Website -Welcome to the repository for the Async website. The site is statically generated using [Metalsmith][#metalsmith] and hosted on [GitHub pages][#gh-pages]. This just means this repository contains templates and assets that are converted into a static HTML site each time a commit is pushed to GitHub. +![Build Status](https://github.com/asyncjs/async-website/actions/workflows/build.yml/badge.svg) +![Deploy Status](https://github.com/asyncjs/async-website/actions/workflows/deploy.yml/badge.svg) + +Welcome to the repository for the Async website. The site is statically generated using [Metalsmith][#metalsmith] and hosted on [GitHub Pages][#gh-pages]. This just means this repository contains templates and assets that are converted into a static HTML site each time a commit is pushed to GitHub. ## Running the server locally You'll need [node.js][#nodejs] >=12.13.0 and npm >=6.10.0 installed locally. -If you are using nvm, you can run the following to install and use node 12: +If you are using nvm, you can run the following to install using `.nvmrc`: ```bash nvm install @@ -20,12 +22,6 @@ You will need to run the following each time you come to work on the site: nvm use ``` -You can then globally install npm 6 with: - -```bash -npm i npm@6.10 -g -``` - Once you have the correct node and npm versions you can install project dependencies by running: ```bash @@ -43,7 +39,7 @@ $ npm start To run a single build and not create a watch task, run the following from the project directory: ```bash -$ npm test +$ npm run build ``` This writes out the site files into the `dist/` directory. @@ -81,7 +77,7 @@ venue: An object containing venue data, location: A link to a map for the venue, address: The address of the venue all in one line, latlong: Lat long for the venue, e.g. "50.826945,-0.136401". -layout: The page layout file. Usually "event.html". +layout: The page layout file. Usually "event.hbs". collection: The page category. Usually "events". draft: If true then the post will not appear on the site. ``` @@ -98,7 +94,7 @@ lanyrd: (optional) The url to the lanyrd page for the event. authors: An array of authors, - name: Author name, link: Author website or Twitter account. -layout: news.html +layout: news.hbs collection: news draft: false ``` @@ -108,7 +104,7 @@ draft: false GitHub pages rebuilds the site whenever a new commit is made. If a re-build is required, even without new content - e.g. in order to re-render the 'next event' on the home page, then run the following: ```bash -$ git commit --allow-empty -m "Republish GitHub pages (empty commit)" +$ git commit --allow-empty -m "Republish GitHub Pages (empty commit)" $ git push ``` @@ -117,9 +113,9 @@ $ git push Async was founded by [Premasagar Rose][#prem] in 2010. He has since moved to Portugal, but kindly allowed the community to continue using the name, website, and domain. In the event of Async ceasing to continue, the domain of asyncjs.com should pass back to [Prem][#prem]. -[#metalsmith]: http://metalsmith.io/ +[#metalsmith]: https://metalsmith.io/ [#gh-pages]: https://pages.github.com/ [#nodejs]: https://nodejs.org/ [#md]: https://daringfireball.net/projects/markdown/ -[#yaml]: http://yaml.org/ +[#yaml]: https://yaml.org/ [#prem]: http://premasagar.com/ diff --git a/_assets/images/iconmonstr/megaphone.svg b/_assets/images/iconmonstr/megaphone.svg new file mode 100644 index 000000000..322349cbd --- /dev/null +++ b/_assets/images/iconmonstr/megaphone.svg @@ -0,0 +1 @@ + diff --git a/_assets/images/news/javascript-jungle.jpg b/_assets/images/news/javascript-jungle.jpg new file mode 100644 index 000000000..2ff0eb95d Binary files /dev/null and b/_assets/images/news/javascript-jungle.jpg differ diff --git a/_assets/scripts/hubbub.js b/_assets/scripts/hubbub.js index e720f53a2..09ffde79d 100644 --- a/_assets/scripts/hubbub.js +++ b/_assets/scripts/hubbub.js @@ -9,7 +9,7 @@ MIT license */ -var Hubbub = (function() { +Hubbub = (function() { "use strict"; diff --git a/_assets/scripts/lanyrd.js b/_assets/scripts/lanyrd.js deleted file mode 100644 index bb391d390..000000000 --- a/_assets/scripts/lanyrd.js +++ /dev/null @@ -1,1175 +0,0 @@ -/* global lanyrd */ - -/* lanyrd.js - v0.0.0 - * Copyright 2011-2012, Dharmafly - * Released under the MIT License - * More Information: https://github.com/dharmafly/lanyrd.js - */ -(function( _lanyrd, jQuery, module ) { - "use strict"; - - var config, utils, lanyrd, parseUrl; - - // Useful utility functions for working with deferred's. - utils = { - // Cached anchor element for parsing urls. This is used by url methods - // like .pathname(). - a: module.document && module.document.createElement("a" ), - - // Check to see if the passed object is an array. - isArray: function( object ) { - return Object.prototype.toString.call( object ) === "[object Array]"; - }, - - // Iterates over an array or object and calls the callback with each - // item. - each: function( items, fn, context ) { - var index = 0, length = items.length, item; - if ( typeof length === "number" ) { - for ( ; index < length; index += 1 ) { - item = items[ index ]; - fn.call( context || item, item, index, items ); - } - } else { - for ( index in items ) { - if ( Object.prototype.hasOwnProperty.call( items, index ) ) { - item = items[ index ]; - fn.call( context || item, item, index, items ); - } - } - } - return items; - }, - - // Returns the keys for the object provided in an array. - keys: function( object ) { - return utils.map( object, function( value, key ) { - return key; - }); - }, - - // Iterates over an array or object and collects the return values of - // each callback function and returns it in an array. - map: function( items, fn, context ) { - var collected = []; - utils.each( items, function( item ) { - collected.push( fn ? fn.apply( this, arguments ) : item ); - }, context ); - return collected; - }, - - // Iterates over an array or object and collects items where the - // callback returned a truthy value and returns them in an array. - filter: function( items, fn, context ) { - var collected = []; - utils.each( items, function( item ) { - if ( fn.apply( this, arguments ) ) { - collected.push( item ); - } - }, context ); - return collected; - }, - - // Extend the first object passed as an argument with successive ones. - extend: function( reciever ) { - var target = arguments[ 0 ], - objects = Array.prototype.slice.call( arguments, 1 ), - count = objects.length, - index = 0, object, property; - - for ( ; index < count; index += 1 ) { - object = objects[ index ]; - for ( property in object ) { - if ( Object.prototype.hasOwnProperty.call( object, property ) ) { - target[ property ] = object[ property ]; - } - } - } - - return target; - }, - - // Calls the method on the object provided, subsequent arguments will - // be passed in to the method. Returns an array containing the returned - // values of each function. - invoke: function( object, method /* args */ ) { - var collected = [], args = [].slice.call( arguments, 2 ); - utils.each( object, function( item ) { - collected.push( item[ method ].apply( item, args ) ); - }); - return collected; - }, - - // Extracts a single property from each object in the collection. - pluck: function( object, path, fallback ) { - var args = [].slice.call( arguments, 2 ); - return utils.map( object, function( item ) { - return utils.keypath( item, path, fallback ); - }); - }, - - // Creates a new deferred object. - deferred: function() { - if ( jQuery && jQuery.Deferred ) { - return new jQuery.Deferred(); - } - return new utils._.Deferred(); - }, - - // Allows you to determine when multiple promises have resolved. Each - // promise should be provided as an argument. Alternatively a single - // array of promises can be provided. - when: function( array ) { - var promises = arguments.length === 1 && utils.isArray( array ) ? array : arguments; - if ( jQuery && jQuery.Deferred ) { - return jQuery.when.apply( jQuery, promises ); - } - return utils._.when.apply( utils._, promises ); - }, - - // Requests a json representation from the url provided. Returns a - // promise object. This should be used to request resources from the - // lanyrd API, it includes specialised error handling for the API. - request: function( url ) { - var type = config.requestType, - request = utils.rawRequest( url, type ), - deferred = utils.deferred(), - promise = deferred.promise({ - data: null, - type: type, - xhr: type === utils.request.JSONP ? null : request - }); - - request.then(function( data ) { - promise.data = data; - deferred[ !data || data.error ? "reject" : "resolve" ]( promise ); - }, function() { - deferred.reject( promise ); - }); - - return promise; - }, - - // Request function that will use jQuery if available otherwise fall back to - // the built in lanyrd methods. Allows the type to be specified, this - // can be used by scripts to request non lanyrd methods. - rawRequest: function( url, type ) { - type = type || "json"; - if ( jQuery ) { - return jQuery.ajax({ url: url, dataType: type }); - } - return lanyrd.utils[ type ]( url ); - }, - - // Escapes html entities within a string. - // https://www.owasp.org/index.php/XSS_(Cross_Site_Scripting)_Prevention_Cheat_Sheet#RULE_.231_-_HTML_Escape_Before_Inserting_Untrusted_Data_into_HTML_Element_Content - escape: function( string ) { - return ("" + string) - .replace( /&/g, "&" ) - .replace( //g, ">" ) - .replace( /"/g, """ ) - .replace( /'/g, "'" ) - .replace( /\//g, "/" ); - }, - - // Function for looking up a value in an object using a key path (period - // delimited string). - // Often useful when working with large objects such as JSON data returned - // from a server as it allows quick navigation to only the required - // information. - keypath: function keypath( object, path, fallback, prototype ) { - var keys = (path || "").split("." ), - key; - - if ( !path ) { - return object; - } - - while ( object && keys.length ) { - key = keys.shift(); - - if ( Object.prototype.hasOwnProperty.call( object, key ) || (prototype === true && object[ key ] !== undefined) ) { - object = object[ key ]; - - if ( keys.length === 0 && object !== undefined ) { - return object; - } - } else { - break; - } - } - - return (arguments.length > 2) ? fallback : null; - }, - - // Returns the pathname of a url. - pathname: function( url ) { - utils.a.href = url || "/"; - return utils.a.pathname; - } - }; - - utils.request.JSON = "json"; - utils.request.JSONP = "jsonp"; - - // A Collection is simply a collection of objects, it provides support for - // iteration as well as getting properties and related resources. It treats the - // collection in the same way that jQuery does in that a getter method will - // return just the first item. To access all children the iterator methods - // should be used. - function Collection( items ) { - items = utils.isArray( items ) ? items : [ items ]; - for ( var index = 0, len = items.length, item; index < len; index += 1 ) { - item = items[ index ]; - if ( item instanceof Collection || item instanceof Resource ) { - item = item.get(); - } - this[ index ] = item; - } - this.length = items.length; - } - - Collection.prototype = { - constructor: Collection, - - // Lookup keys in the attributes object, period delimited key paths can - // be used to access nested keys without having to check for the - // existence of each property. This method will return a single object - // for the first item in the list. - get: function( path, fallback ) { - return utils.keypath( this[ 0 ], path, fallback ); - }, - - // Same as #get() but path returns the property with html entities escaped - // if it is a string, otherwise returns the coerced value. - escape: function( path, fallback ) { - return utils.escape( this.get( path, fallback ) ); - }, - - // Gets a related resource for the key provided. If the current resource - // doesn't support the key then a rejected Resource will be returned. - // Accepts an optional prefix in case the object with the api urls is - // nested further into the model chain. For example a user is nested - // inside speaker and attendee objects. - // - // If the path provided returns a url then this will be loaded. - // - // Examples: - // - // var related = speakers.related('user.user'); - // var related = conference.related('attendees'); - // - // // Direct path to a resource url also works. - // var related = speakers.related('user.api_urls.user'); - related: function( path ) { - var parts = path.split( "." ), - key = parts.pop(), - url; - - parts.push( "api_urls" ); - - // Needs to support various keypaths including. - // "api_url", "api_urls.attendees", "user.api_urls.user" - url = this.get( parts.join("." ), {})[ key ]; - if ( !url ) { - url = this.get( path ); - } - return url ? new Resource({ url: url }) : Resource.noop(); - }, - - // Same as #related() but immediately calls #fetch() on the resource. - fetchRelated: function( path, success, error ) { - return this.related( path ).fetch( success, error ); - }, - - // Fetches the related resource for all items in the list. Useful for - // requesting all attendees to a conference for example. The method will - // return a generic promise object. #done() callbacks receive two - // arguments, an array of completed resources and an array of request - // (jqXHR) objects. - fetchAllRelated: function( path, success, error ) { - var deferred = utils.deferred(); - - function iterator( item ) { return item.fetchRelated( path ); } - jQuery.when.apply( jQuery, this.map( iterator ) ).then(function() { - var resources = [], requests = []; - jQuery.each( arguments, function() { - requests.push( this[ 1 ] ); - resources.push( this[ 0 ] ); - }); - deferred.resolve( resources, requests ); - }, deferred.reject ); - - return deferred.promise().then( success, error ); - }, - - // Get the object at the index provided. A negative index takes items - // from the end of the list. - at: function( index ) { - index = index < 0 ? index + this.length : index; - return this[ index ] || null; - } - }; - - utils.each([ "each", "map", "filter", "pluck", "invoke" ], function( method ) { - Collection.prototype[ method ] = function() { - return utils[ method ].apply( this, [].concat.apply( this, arguments ) ); - }; - }); - - // The Resource acts as a request builder for the API and provides basic - // methods for accessing the returned data. For most use cases it will be - // all you need to interact with the API. Each instance requires a full path - // to a Lanyrd resource which can then be fetched. Once loaded the data can - // be accessed and pages can be iterated. Using a Resource requires full - // knowledge of the returned data structure. - // - // Related resources can also be fetched using the #related() method this - // allows the API to be traversed using the keys found under the "api_urls" - // parameter. - function Resource( options ) { - options = options || {}; - - this.url = options.url; - this.data = options.data || {}; - - var deferred = options.deferred || utils.deferred(); - if ( this.url ) { - // We create the fetch method within the constructor to hide the - // deferred object within the closure. - this.fetch = function( success, error ) { - if ( this.state() !== Resource.PENDING ) { - return this; - } - - var url = this.url, request, resource; - - if ( config.requestType === utils.request.JSONP ) { - url += (url.indexOf( "?" ) < 0 ? "?" : "&") + "callback=?"; - } - request = utils.request( url ); - resource = this; - - request.done(function() { - resource.data = request.data; - deferred.resolveWith( resource, [ resource, request ]); - }); - request.fail(function() { - deferred.rejectWith( resource, [ resource, request ]); - }); - - return this.then( success, error ); - }; - } - deferred.promise( this ); - } - - // Constants for use with pagination. - Resource.PREV = "prev"; - Resource.NEXT = "next"; - - // Constants for use with Resource#state(). - Resource.RESOLVED = "resolved"; - Resource.REJECTED = "rejected"; - Resource.PENDING = "pending"; - - // Creates a rejected Resource that can be returned by methods when a - // request cannot be made. For example at the last page of a paginated - // collection or if no related resource exists. - Resource.noop = function( options ) { - var rejected = utils.deferred(), - noop = new Resource( utils.extend({ deferred: rejected }, options ) ); - - rejected.rejectWith( noop, [ noop, null ]); - return noop; - }; - - Resource.prototype = { - constructor: Resource, - - url: null, - data: null, - - // Actually defined in the constructor to hide the deferred object - // within a closure. This method loads the resource available at the - // Resource#url endpoint. If no href is provided to the constructor - // then this method is a no-op. - fetch: function() { return this; }, - - // Checks to see if the current resource was successfully loaded from - // the server. Will return false if the request failed or is still - // pending. Resource#state() can be used for more fine grained checks. - fetched: function() { - return this.state && this.state() === Resource.RESOLVED; - }, - - // Shortcut to Collection#related(). Can be used to fetch a related resource. - related: function( path ) { - return this.collection().related( path ); - }, - - // Lookup keys in the raw data object returned from the server. - get: function( path, fallback ) { - return utils.keypath( this.data, path, fallback ); - }, - - // Gets a path for a string with html entities escaped. - escape: function( path, fallback ) { - return utils.escape( this.get( path, fallback ) ); - }, - - // Like #get() but wraps the resource at the end of the path in a Collection - // this makes it much easier to work with some of the resource data such - // as a list or user object. - collection: function( path ) { - var data = this.get( path ); - if ( data ) { - data = new Collection( data ); - } - return data; - }, - - // Paginates through the resource in the direction specified. Use one of the - // Resource.NEXT or Resource.PREV constants for the first argument. This - // method returns an unloaded Resource object. To actually fetch the - // page you must call #fetch(). - paginate: function( direction ) { - var url = this.get( "pagination.api_urls", {})[ direction ]; - return url ? new Resource({ url: url }) : Resource.noop(); - }, - - // Checks to see if the current resource is paginated. It can be useful - // to call this before #next() #prev() or #all(). - paginated: function() { - return this.get( "pagination.num_pages", 1 ) > 1; - }, - - // Checks to see if this resource is the first page. If there is no - // pagination then this will still return true. - first: function() { - return this.get("pagination.page", 1 ) === 1; - }, - - // Checks to see if the resource is the last page. Returns true if there - // is only one page or there is no pagination data. - last: function() { - var pagination = this.get("pagination", {}); - return pagination.page === pagination.num_pages; - }, - - // Requests the next page of resources in a collection and returns the - // promised resource. This calls fetch internally, both success and - // error callbacks can be passed as arguments. - next: function( success, error ) { - return this.paginate( Resource.NEXT ).fetch().then( success, error ); - }, - - // Requests the previous page of resources in a collection and returns the - // promised resource. This calls fetch internally, both success and - // error callbacks can be passed as arguments. - prev: function( success, error ) { - return this.paginate( Resource.PREV ).fetch().then( success, error ); - }, - - // Walks through all pages for a paginated resource and collects the - // results in a single array. This is then returned as a new "master" - // Resource. - all: function( success, error ) { - var self = this, - deferred = utils.deferred(), - combined = new Resource({ deferred: deferred }); - - function inner() { - var key = self.get("pagination.paginated_key" ), - collected = [], - remaining = 2; - - function complete( resource, request ) { - remaining -= 1; - if ( remaining ) { - return; - } - - if ( !request ) { - combined.data = {}; - combined.data[ key ] = collected; - deferred.resolveWith( combined, [ combined ]); - } else { - deferred.rejectWith( combined, [ combined, request ]); - } - } - - (function backward( resource ) { - if ( !resource.flag ) { - resource.flag = true; - deferred.notifyWith( resource, arguments ); - collected = resource.get( key ).slice().concat( collected ); - } - return resource.prev().then( backward, complete ); - })( self ); - - (function forward( resource ) { - if ( !resource.flag ) { - resource.flag = true; - deferred.notifyWith( resource, arguments ); - collected = collected.concat( resource.get( key ) ); - } - return resource.next().then( forward, complete ); - })( self ); - } - - // If the current object is not yet loaded then do so before - // loading related resources. - if ( this.fetched() ) { - inner(); - } else { - this.fetch().always( inner ); - } - - return combined.then( success, error ); - } - }; - - // The base API object. - lanyrd = { - API_DOMAIN: "http://lanyrd.asyncjs.com", - - // Attempts to match a conference resource for the Lanyrd url provided. - // This breaks rest conventions but provides a nicer API for people to - // get started quickly. - conference: function( url ) { - return this.resource({ url: this.url( url ) }); - }, - person: function( url ) { - var apiUrl = this.url( url ).replace( /\/profile\//i, "/people/" ); - return this.resource({ url: apiUrl }); - }, - place: function( url ) { - return this.resource({ url: this.url( url ) }); - }, - topic: function( url ) { - return this.resource({ url: this.url( url ) }); - }, - collection: function( object ) { - return new Collection( object ); - }, - resource: function( options ) { - return new Resource( options ); - }, - url: function( url ) { - // Oh dear, here we assume that a Lanyrd API endpoint is the same as - // the website. Ideally there should be some kind of lookup service - // that does this for us. - return this.API_DOMAIN + utils.pathname( url ); - }, - // Allows you to switch between JSON and JSONP transports. - config: function( newer ) { - utils.extend( config, newer ); - }, - utils: utils, - noConflict: function() { - module.lanyrd = _lanyrd; - return lanyrd; - }, - Collection: Collection, - Resource: Resource - }; - - config = { requestType: utils.request.JSONP }; - - if ( typeof module.define === "function" && module.define.amd ) { - module.define("lanyrd", function() { - return lanyrd; - }); - } else if ( module.exports ) { - // Pass utils object into module factories to be augmented. - require("./lanyrd/deferred" )( utils ); - require("./lanyrd/request" )( utils ); - - // Override pathname with node specific code. - parseUrl = require( "url" ).parse; - lanyrd.utils.pathname = function( url ) { - return parseUrl( url ).pathname; - }; - // Default request type is now JSON. - config.requestType = utils.request.JSON; - - module.exports = lanyrd; - } else { - module.lanyrd = lanyrd; - } - - })( this.lanyrd, this.jQuery, typeof module !== "undefined" ? module : this ); -// Underscore Deferred library is wrapped in a closure into which we pass -// lanyrd.utils, this allows us to capture the methods without adding them -// to the window object. -(function() { - // underscore.Deferred by wookiehangover - // Released under the MIT License - // https://github.com/wookiehangover/underscore.Deferred - (function( root ) { - - // Let's borrow a couple of things from Underscore that we'll need - - // _.each - var breaker = {}, - hasOwn = Object.prototype.hasOwnProperty, - toString = Object.prototype.toString, - forEach = Array.prototype.forEach, - slice = Array.prototype.slice; - - var _each = function( obj, iterator, context ) { - var key, i, l; - - if ( !obj ) { - return; - } - if ( forEach && obj.forEach === forEach ) { - obj.forEach( iterator, context ); - } else if ( obj.length === +obj.length ) { - for ( i = 0, l = obj.length; i < l; i++ ) { - if ( i in obj && iterator.call( context, obj[ i ], i, obj ) === breaker ) { - return; - } - } - } else { - for ( key in obj ) { - if ( hasOwn.call( obj, key ) ) { - if ( iterator.call( context, obj[ key ], key, obj ) === breaker ) { - return; - } - } - } - } - }; - - // _.isFunction - var _isFunction = function( obj ) { - return !!(obj && obj.constructor && obj.call && obj.apply); - }; - - // _.extend - var _extend = function( obj ) { - - _each( slice.call( arguments, 1 ), function( source ) { - var prop; - - for ( prop in source ) { - if ( source[ prop ] !== void 0 ) { - obj[ prop ] = source[ prop ]; - } - } - }); - return obj; - }; - - // And some jQuery specific helpers - - var class2type = { "[object Array]": "array", "[object Function]": "function" }; - - var _type = function( obj ) { - return !obj ? - String( obj ) : - class2type[ toString.call( obj ) ] || "object"; - }; - - // Now start the jQuery-cum-Underscore implementation. Some very - // minor changes to the jQuery source to get this working. - - var promiseMethods = "done fail isResolved isRejected promise then always pipe".split(" "); - - // Internal Deferred namespace - var _d = {}; - - var flagsCache = {}; - // Convert String-formatted flags into Object-formatted ones and store in cache - function createFlags( flags ) { - var object = flagsCache[ flags ] = {}, - i, length; - flags = flags.split( /\s+/ ); - for ( i = 0, length = flags.length; i < length; i++ ) { - object[ flags[ i ] ] = true; - } - return object; - } - - _d.Callbacks = function( flags ) { - - // Convert flags from String-formatted to Object-formatted - // (we check in cache first) - flags = flags ? ( flagsCache[ flags ] || createFlags( flags ) ) : {}; - - var // Actual callback list - list = [], - // Stack of fire calls for repeatable lists - stack = [], - // Last fire value (for non-forgettable lists) - memory, - // Flag to know if list is currently firing - firing, - // First callback to fire (used internally by add and fireWith) - firingStart, - // End of the loop when firing - firingLength, - // Index of currently firing callback (modified by remove if needed) - firingIndex, - // Add one or several callbacks to the list - add = function( args ) { - var i, - length, - elem, - type, - actual; - for ( i = 0, length = args.length; i < length; i++ ) { - elem = args[ i ]; - type = _type( elem ); - if ( type === "array" ) { - // Inspect recursively - add( elem ); - } else if ( type === "function" ) { - // Add if not in unique mode and callback is not in - if ( !flags.unique || !self.has( elem ) ) { - list.push( elem ); - } - } - } - }, - // Fire callbacks - fire = function( context, args ) { - args = args || []; - memory = !flags.memory || [ context, args ]; - firing = true; - firingIndex = firingStart || 0; - firingStart = 0; - firingLength = list.length; - for ( ; list && firingIndex < firingLength; firingIndex++ ) { - if ( list[ firingIndex ].apply( context, args ) === false && flags.stopOnFalse ) { - memory = true; // Mark as halted - break; - } - } - firing = false; - if ( list ) { - if ( !flags.once ) { - if ( stack && stack.length ) { - memory = stack.shift(); - self.fireWith( memory[ 0 ], memory[ 1 ] ); - } - } else if ( memory === true ) { - self.disable(); - } else { - list = []; - } - } - }, - // Actual Callbacks object - self = { - // Add a callback or a collection of callbacks to the list - add: function() { - if ( list ) { - var length = list.length; - add( arguments ); - // Do we need to add the callbacks to the - // current firing batch? - if ( firing ) { - firingLength = list.length; - // With memory, if we're not firing then - // we should call right away, unless previous - // firing was halted (stopOnFalse) - } else if ( memory && memory !== true ) { - firingStart = length; - fire( memory[ 0 ], memory[ 1 ] ); - } - } - return this; - }, - // Remove a callback from the list - remove: function() { - if ( list ) { - var args = arguments, - argIndex = 0, - argLength = args.length; - for ( ; argIndex < argLength ; argIndex++ ) { - for ( var i = 0; i < list.length; i++ ) { - if ( args[ argIndex ] === list[ i ] ) { - // Handle firingIndex and firingLength - if ( firing ) { - if ( i <= firingLength ) { - firingLength--; - if ( i <= firingIndex ) { - firingIndex--; - } - } - } - // Remove the element - list.splice( i--, 1 ); - // If we have some unicity property then - // we only need to do this once - if ( flags.unique ) { - break; - } - } - } - } - } - return this; - }, - // Control if a given callback is in the list - has: function( fn ) { - if ( list ) { - var i = 0, - length = list.length; - for ( ; i < length; i++ ) { - if ( fn === list[ i ] ) { - return true; - } - } - } - return false; - }, - // Remove all callbacks from the list - empty: function() { - list = []; - return this; - }, - // Have the list do nothing anymore - disable: function() { - list = stack = memory = undefined; - return this; - }, - // Is it disabled? - disabled: function() { - return !list; - }, - // Lock the list in its current state - lock: function() { - stack = undefined; - if ( !memory || memory === true ) { - self.disable(); - } - return this; - }, - // Is it locked? - locked: function() { - return !stack; - }, - // Call all callbacks with the given context and arguments - fireWith: function( context, args ) { - if ( stack ) { - if ( firing ) { - if ( !flags.once ) { - stack.push([ context, args ]); - } - } else if ( !( flags.once && memory ) ) { - fire( context, args ); - } - } - return this; - }, - // Call all the callbacks with the given arguments - fire: function() { - self.fireWith( this, arguments ); - return this; - }, - // To know if the callbacks have already been called at least once - fired: function() { - return !!memory; - } - }; - - return self; - }; - - _d.Deferred = function( func ) { - var doneList = _d.Callbacks( "once memory" ), - failList = _d.Callbacks( "once memory" ), - progressList = _d.Callbacks( "memory" ), - state = "pending", - lists = { - resolve: doneList, - reject: failList, - notify: progressList - }, - promise = { - done: doneList.add, - fail: failList.add, - progress: progressList.add, - - state: function() { - return state; - }, - - // Deprecated - isResolved: doneList.fired, - isRejected: failList.fired, - - then: function( doneCallbacks, failCallbacks, progressCallbacks ) { - deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks ); - return this; - }, - always: function() { - deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments ); - return this; - }, - pipe: function( fnDone, fnFail, fnProgress ) { - return _d.Deferred(function( newDefer ) { - _each({ - done: [ fnDone, "resolve" ], - fail: [ fnFail, "reject" ], - progress: [ fnProgress, "notify" ] - }, function( data, handler ) { - var fn = data[ 0 ], - action = data[ 1 ], - returned; - if ( _isFunction( fn ) ) { - deferred[ handler ](function() { - returned = fn.apply( this, arguments ); - if ( returned && _isFunction( returned.promise ) ) { - returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify ); - } else { - newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ]); - } - }); - } else { - deferred[ handler ]( newDefer[ action ] ); - } - }); - }).promise(); - }, - // Get a promise for this deferred - // If obj is provided, the promise aspect is added to the object - promise: function( obj ) { - if ( !obj ) { - obj = promise; - } else { - for ( var key in promise ) { - obj[ key ] = promise[ key ]; - } - } - return obj; - } - }, - deferred = promise.promise({}), - key; - - for ( key in lists ) { - deferred[ key ] = lists[ key ].fire; - deferred[ key + "With" ] = lists[ key ].fireWith; - } - - // Handle state - deferred.done(function() { - state = "resolved"; - }, failList.disable, progressList.lock ).fail(function() { - state = "rejected"; - }, doneList.disable, progressList.lock ); - - // Call given func if any - if ( func ) { - func.call( deferred, deferred ); - } - - // All done! - return deferred; - }; - - // Deferred helper - _d.when = function( firstParam ) { - var args = slice.call( arguments, 0 ), - i = 0, - length = args.length, - pValues = new Array( length ), - count = length, - pCount = length, - deferred = length <= 1 && firstParam && _isFunction( firstParam.promise ) ? - firstParam : - _d.Deferred(), - promise = deferred.promise(); - function resolveFunc( i ) { - return function( value ) { - args[ i ] = arguments.length > 1 ? slice.call( arguments, 0 ) : value; - if ( !( --count ) ) { - deferred.resolveWith( deferred, args ); - } - }; - } - function progressFunc( i ) { - return function( value ) { - pValues[ i ] = arguments.length > 1 ? slice.call( arguments, 0 ) : value; - deferred.notifyWith( promise, pValues ); - }; - } - if ( length > 1 ) { - for ( ; i < length; i++ ) { - if ( args[ i ] && args[ i ].promise && _isFunction( args[ i ].promise ) ) { - args[ i ].promise().then( resolveFunc( i ), deferred.reject, progressFunc( i ) ); - } else { - --count; - } - } - if ( !count ) { - deferred.resolveWith( deferred, args ); - } - } else if ( deferred !== firstParam ) { - deferred.resolveWith( deferred, length ? [ firstParam ] : []); - } - return promise; - }; - - // Try exporting as a Common.js Module - if ( typeof module !== "undefined" && module.exports ) { - module.exports = _d; - - // Or mixin to Underscore.js - } else if ( typeof root._ !== "undefined" ) { - root._.mixin( _d ); - - // Or assign it to window._ - } else { - root._ = _d; - } - - })( this ); - - // Export the code as a node module. - if ( typeof module !== "undefined" && module.exports ) { - var _ = module.exports; - module.exports = function( utils ) { - utils._ = _; - }; - } - -}).call( this.lanyrd && this.lanyrd.utils ); -/*globals ActiveXObject */ -(function( utils ) { - // Node specific code. - if ( typeof module !== "undefined" && module.exports ) { - return (function() { - var url = require("url" ), - http = require("http" ); - - module.exports = function( utils ) { - utils.json = utils.jsonp = function( uri, fn ) { - var parsed = url.parse( uri ), - deferred = utils.deferred(), - promise = deferred.promise(); - - promise.xhr = http.get( parsed, function( res ) { - var data = []; - - res.on( "data", function( chunk ) { - data.push( chunk ); - }); - - res.on( "end", function() { - try { - if ( res.statusCode >= 200 && res.statusCode < 300 ) { - deferred.resolve( JSON.parse( data.join() ), res ); - } else { - deferred.reject( res ); - } - } catch ( error ) { - deferred.reject( res, error ); - } - }); - }); - - return promise; - }; - return utils; - }; - })(); - } - - // Browser specific code. - var head = document.getElementsByTagName( "head" )[ 0 ], - uuid = 0; - - function createXMLHTTPObject() { - var factory = createXMLHTTPObject.cached, - XMLHttpFactories, xhr = null; - - if ( factory ) { - return factory(); - } - - XMLHttpFactories = [ - function() { return new XMLHttpRequest(); }, - function() { return new ActiveXObject("Msxml2.XMLHTTP"); }, - function() { return new ActiveXObject("Msxml3.XMLHTTP"); }, - function() { return new ActiveXObject("Microsoft.XMLHTTP"); } - ]; - - do { - factory = XMLHttpFactories.shift(); - try { - xhr = factory(); - createXMLHTTPObject.cached = factory; - break; - } catch ( error ) {} - } while ( XMLHttpFactories.length); - - return xhr; - } - - function json( url, fn ) { - var xhr = createXMLHTTPObject(), - deferred = lanyrd.utils.deferred(); - - xhr.open( "GET", url, true ); - xhr.onreadystatechange = function() { - var data; - if ( xhr.readyState === 4 ) { - try { - if ( xhr.status >= 200 && xhr.status < 300 ) { - data = JSON.parse( xhr.responseText ); - deferred.resolve( data, xhr ); - } else { - deferred.reject( xhr ); - } - } catch ( error ) { - deferred.reject( xhr, error ); - } - } - }; - - if ( xhr.readyState !== 4 ) { - xhr.send(); - } - - // This might be a bad idea... - return deferred.promise( xhr ).done( fn ); - } - - function jsonp( url, fn ) { - var deferred = lanyrd.utils.deferred(), - script = document.createElement( "script" ), - callback = "callback" + (uuid += 1), - esc = encodeURIComponent, - key; - - url = url.replace( /\?$/, callback ); - window[ callback ] = function( json ) { - deferred.resolve( json ); - delete window[ callback ]; - }; - - script.src = url; - script.onload = script.onreadystatechange = function() { - if ( !script.readyState || /loaded|complete/.test( script.readyState ) ) { - script.onload = script.onreadystatechange = null; - script.parentNode.removeChild( script ); - script = undefined; - } - }; - head.insertBefore( script, head.lastChild ); - - setTimeout( deferred.reject, 30000 ); - return deferred.promise().done( fn ); - } - - lanyrd.utils.json = json; - lanyrd.utils.jsonp = jsonp; - - })( this.lanyrd && this.lanyrd.utils ); diff --git a/_assets/scripts/lanyrd.widget.js b/_assets/scripts/lanyrd.widget.js deleted file mode 100644 index 6b6b7bcb6..000000000 --- a/_assets/scripts/lanyrd.widget.js +++ /dev/null @@ -1,230 +0,0 @@ -/* global lanyrd */ - -/* - Lanyrd widget. An extension on top of the Lanyrd API. -*/ - -(function() { -"use strict"; - -// -// Extend Lanyrd as `lanyrd.widget` - -lanyrd.widget = {}; - -// -// Tim (a tiny, secure JavaScript micro-templating script) -// https://github.com/premasagar/tim - -var tim = function() {var e = /{{\s*([a-z0-9_][\\.a-z0-9_]*)\s*}}/gi;return function( f, g ) {return f.replace( e, function( h, i ) {for ( var c = i.split("."), d = c.length, b = g, a = 0; a < d; a++ ) {b = b[ c[ a ] ];if ( b === void 0 )throw"tim: '" + c[ a ] + "' not found in " + h;if ( a === d - 1 )return b;}});};}(); - -// -// Here be Widgets ! - -// People widget -// -// Returns a promise wrapped html string containing markup primarily showing the avatars -// of event(s) attendees and trackers. -// -// parameters: -// - url: A lanyrd conference url or an array of lanyrd conference urls -// - elem: An optional html dom element which will be populated with the rendered -// html template. The html is also passed to the promise resolution -// callback. -// - options: Object of key value pairs detailing lower level parameters -// - all: Flag which determines how many trackers/attendees will be -// displayed. (true by default) -// - append: Flag for determining how the rendered html template is inserted -// into the provided dom element. If false any html in the supplied -// dom element will be overwritten. (false by default) -// - headingLevel Number which decides what heading level tags the rendered html -// should use. (2 by default) - -lanyrd.widget.people = function( url, elem, options ) { - var promise, - conference, - options = options || {}; - options.all = options.all || true; - options.headingLevel = options.headingLevel || 2; - - if ( lanyrd.utils.isArray( url ) && url.length > 1 ) { - - // Get all attendees and trackers for merged lanyrd conference events - // 1. Convert passed urls to equivalent Lanyrd api resource objects - // 2. Pass merged attendees and trackers to render and return render's - // promise. - - url = lanyrd.utils.map( url, function( url ) { - return lanyrd.conference( url ).fetch(); - }); - - promise = lanyrd.mergeRelated( url, { related: [ "attendees", "trackers" ] }) - .pipe(function( merged ) { - return render( merged.attendees, merged.trackers ) - .then( tryPlaceInElement ); - }); - } else { - - // Get attendees and trackers for a lanyrd conference event - // 1. Change single item array as string - // 2. Fetch conference data - // 3. Get related attendees and trackers - // 4. Pass attendees and trackers to render and return render's - // promise. - - url += ""; - promise = lanyrd.conference( url ).fetch() - .pipe(function( con ) { - conference = con.get("conference" ); - return con; - }) - .pipe( getAttendeesTrackers ) - .pipe(function( attendees, trackers ) { - return render( - attendees.get( "attendees" ), - trackers.get( "trackers" ) - ) - .then( tryPlaceInElement ); - }); - } - - // Returns a promise representing the retrieval of both the attendees - // and the trackers. The amount of attendees/trackers passed to the - // promise resolution is dependant on the options.all parameter. - - function getAttendeesTrackers( con ) { - if ( options.all ) { - return lanyrd.utils.when( - con.related("conference.attendees" ).all(), - con.related("conference.trackers" ).all() - ); - } else { - return lanyrd.utils.when( - con.related("conference.attendees" ).fetch(), - con.related( "conference.trackers" ).fetch() - ); - } - } - - // Places the generated html in the supplied html dom element - // (if given). - - function tryPlaceInElement( html ) { - if ( elem ) { - if ( options.append ) { - elem.innerHTML += html; - } else { - elem.innerHTML = html; - } - } - } - - // A render function unique to lanyrd.widget.people - - function render( attendees, trackers ) { - var deferred = lanyrd.utils.deferred(), - promise = deferred.promise(), - html = ""; - - // returns attendees and trackers markup - - function drawList( people, peopleType ) { - var peopleTemplate = options.peopleTemplate || '', - personTemplate = options.personTemplate || '
  • ', - builtUpTemplate = "", - person, - i = 0; - - for ( i; i < people.length; i++ ) { - person = people[ i ].user || people[ i ]; - builtUpTemplate += tim( personTemplate, person ); - } - html += tim( peopleTemplate, { list: builtUpTemplate, peopleType: peopleType }); - } - - // Build the rest of the template - // (!) Needs cleanup! - // Only 'see more attendees' link supported on single conferences (no merged events) - - // Attendees heading - html += "' + attendees.length + - " attending"; - - // Attendee avatars - drawList( attendees, "lanyrd-attendees" ); - - // Provide conference link to conferences - if ( conference ) { - html += tim( '

    → Attendee details

    ", conference ); - } - - if ( trackers.length > 0 ) { - // Trackers heading - html += "' + trackers.length + - " tracking"; - // Tracker avatars - drawList( trackers, "lanyrd-trackers" ); - } - - // Encapsulating div and embedded style - html = '
    ' + - "" + - html + - "
    "; - - // Instantly resolve and pass rendered html as promise - - deferred.resolve( html ); - return promise; - } - - return promise; - }; - -// Person widget -// (!) to be revised - -lanyrd.widget.person = function( url, elem, options ) { - var promise, - options = options || {}; - - function render( person ) { - var deferred = lanyrd.utils.deferred(), - promise = deferred.promise(), - html, - personTemplate = options.personTemplate || '

    {{name}}

    ' + - '' + - "

    {{short_bio}}

    "; - - html = tim( personTemplate, person ); - deferred.resolve( html ); - return promise; - } - - promise = lanyrd.person( url ).fetch() - .pipe(function( person ) { - return person.get("user" ); - }) - .pipe(function( person ) { - return render( person ).then(function( html ) { - if ( elem ) elem.innerHTML = html; - }); - }); - - return promise; - }; - -}()); diff --git a/_assets/scripts/main.js b/_assets/scripts/main.js index 54b6b44d5..94d908983 100644 --- a/_assets/scripts/main.js +++ b/_assets/scripts/main.js @@ -1,244 +1,22 @@ -if ( document.querySelectorAll ) { - (function() { - var test = document.getElementById("media-test" ), - isWide = test && window.getComputedStyle( test, null ).width === "1px", - scripts = [ "lanyrd", "lanyrd.widget" ], - remaining = scripts.length; - - if ( isWide ) { - (function loadScript( src ) { - var script = document.createElement("script"); - script.onload = function() { - remaining -= 1; - if ( remaining === 0 ) { - setupLanyrd( window.lanyrd ); - setupHubbub(); - } else { - loadScript( scripts.shift() ); - } - }; - script.src = "/js/" + src + ".js"; - document.body.appendChild( script ); - })( scripts.shift() ); - - (function embedSlides() { - function createFrame( src, type ) { - var div = document.createElement( "div" ), iframe; - div.className = "iframe-wrapper iframe-" + type; - - iframe = document.createElement("iframe" ); - iframe.src = src; - iframe.width = "100%"; - iframe.className = "iframe"; - - div.appendChild( iframe ); - return div; - } - - function createSection( title, type ) { - var media = document.querySelectorAll( "[data-" + type + "]" ), - section, heading; - - if ( media.length ) { - section = document.createElement( "section" ); - section.className = "extra " + type; - - heading = document.createElement("h3" ); - heading.className = "heading"; - heading.appendChild( document.createTextNode( title ) ); - - section.appendChild( heading ); - - [].forEach.call( media, function( link ) { - var data = link.dataset, - src = data && (data.video || data.slides) || link.href; - - section.appendChild( createFrame( src, type ) ); - }); - - document.querySelector(".event-detail" ).appendChild( section ); - } - } - - createSection( "Slides", "slides" ); - createSection("Videos", "video" ); - })(); - } else { - setupHubbub(); - } - - function setupLanyrd( lanyrd ) { - var event, target, url, container, upcoming, eventUrlRegex; - - eventUrlRegex = /^\s*https?:\/\/(?:www.)?lanyrd.com\/\d{4}\/[a-z0-9-]+\/\s*$/; - event = document.querySelector( ".event-detail" ); - if ( event ) { - target = event.querySelector( ".lanyrd-link" ); - url = target && target.href; - - if ( url && eventUrlRegex.test( url ) ) { - container = document.createElement( "section" ); - container.className = "extra lanyrd"; - event.appendChild( container ); - - lanyrd.widget.people([ url ], container, { - append: true, - headingLevel: 3 - }).done(function() { - var style, more; - - style = container.querySelector( "style" ); - style.parentNode.removeChild( style ); - - more = container.querySelector("p" ); - more.parentNode.removeChild( more ); - - lanyrd.utils.each( container.querySelectorAll("h3" ), function() { - var parts = this.innerHTML.split( " " ), - count = parts.shift(); - - this.classList.add("heading" ); - this.innerHTML = parts.join( " " ) + " (" + count + ")"; - }); - - lanyrd.utils.each( container.querySelectorAll("li a" ), function() { - this.classList.add( "bordered" ); - }); - }); - } - } - - upcoming = document.querySelectorAll( ".event-item" ); - - lanyrd.utils.each( upcoming, function() { - var item = this, - target = this.querySelector( ".lanyrd-link" ), - url = target && target.getAttribute("href" ); - - if ( url && eventUrlRegex.test( url ) ) { - lanyrd.conference( target.href ).fetch(function( conference ) { - var requests = [ - conference.related("conference.attendees" ).fetch(), - conference.related("conference.trackers" ).fetch() - ]; - - lanyrd.utils.when( requests ).then(function() { - var trackers = arguments[ 1 ][ 0 ], - attendees = arguments[ 0 ][ 0 ], - container = document.createElement( "p" ); - - container.className = "lanyrd-simple"; - container.innerHTML = [ - attendees.get("pagination.total" ), - "attending", - "•", - trackers.get("pagination.total" ), - "tracking" - ].join( " " ); - item.appendChild( container ); - }); - }); - } - }); - } - - function setupHubbub () { - var gists = document.querySelectorAll( "a[data-gist]" ); - - function loadScript () { - var script = document.createElement("script" ); - script.onload = function() { - setupWidgets( window.Hubbub ); - }; - script.src = "/js/hubbub.js"; - document.body.appendChild( script ); - } - - function setupWidgets ( Hubbub ) { - Hubbub.css.avatarLink = "hubbub-avatar-link bordered"; - [].forEach.call( gists, function( gist ) { - var section = document.createElement( "section" ); - section.setAttribute( "class", "extra" ); - document.querySelector( "section.event-detail" ).appendChild( section ); - Hubbub.appendWidget( section, gist.href ); - }); - } - - if ( gists.length < 1 ) { - return; - } - - loadScript(); - } - })(); - - (function buildEmails() { - var emails = document.querySelectorAll("a[href^=mailto]" ); - - function replace( email ) { - return email.replace( /(?:%20| )\[([^\]]+)\](?:%20| )/g, function( $0, $1 ) { - return { at: "@", dot: "." }[ $1 ] || $0; - }); - } - - [].forEach.call( emails || [], function( link ) { - link.href = replace( link.href ); - link.innerHTML = replace( link.innerHTML ); +(function setupHubbub() { + const gists = document.querySelectorAll("a[data-gist]"); + + if (gists.length < 1) { + return; + } + + const setupWidgets = (Hubbub) => { + Hubbub.css.avatarLink = "hubbub-avatar-link bordered"; + gists.forEach((gist) => { + const section = document.createElement("section"); + section.setAttribute("class", "extra"); + document.querySelector("section.event-detail").appendChild(section); + Hubbub.appendWidget(section, gist.href); }); - })(); - - // No longer required since Metalsmith update - // (function removeOldNews() { - // var news = document.querySelectorAll( ".news" ), - // expires = new Date().getTime() - (30 * 24 * 60 * 60 * 1000); - - // [].forEach.call( news || [], function( element ) { - // var time = Date.parse( element.getAttribute( "data-timestamp" ) ); - // if ( time < expires ) { - // element.parentNode.removeChild( element ); - // } - // }); - // })(); - - (function preventOrphanedLanyrdIcons() { - var lanyrdLinks = document.querySelectorAll(".lanyrd-link" ); - - [].forEach.call( lanyrdLinks || [], function( link ) { - var parent = link.parentNode, - prev = link.previousSibling, words, wrap, clone; - - while ( prev && prev.nodeType === 3 && !prev.nodeValue.trim() ) { - prev = prev.previousSibling; - } + }; - if ( prev ) { - if ( prev.nodeType === 3 ) { - words = prev.nodeValue.trim().split(" "); - } else { - words = prev.innerHTML.split(" "); - } - - wrap = document.createElement("span" ); - wrap.className = "no-wrap"; - - if ( words.length > 1 ) { - if ( prev.nodeType === 3 ) { - clone = document.createTextNode( words.pop() ); - prev.nodeValue = words.join(" ") + " "; - } else { - clone = prev.cloneNode(); - clone.innerHTML = words.pop(); - prev.innerHTML = words.join(" ") + " "; - } - - wrap.appendChild( clone ); - } else { - wrap.appendChild( prev ); - } - - wrap.appendChild( link ); - parent.appendChild( wrap ); - } - }); - })(); -} + const script = document.createElement("script"); + script.onload = () => setupWidgets(window.Hubbub); + script.src = "/js/hubbub.js"; + document.body.appendChild(script); +})(); diff --git a/_assets/styles/footer.scss b/_assets/styles/_footer.css similarity index 69% rename from _assets/styles/footer.scss rename to _assets/styles/_footer.css index 3130021e6..5358128f6 100644 --- a/_assets/styles/footer.scss +++ b/_assets/styles/_footer.css @@ -1,12 +1,12 @@ -@import 'variables'; +@import '_variables'; .footer { position: absolute; width: 100%; left: 0; bottom: 0; - background-color: $primary-dark; - color: $grey-light; + background-color: var(--primary-dark); + color: var(--grey-light); } .links { @@ -16,7 +16,7 @@ &::after { content: " •"; margin: 0 8px; - color: $grey-light; + color: var(--grey-light); } &:last-child::after { diff --git a/_assets/styles/hero.scss b/_assets/styles/_hero.css similarity index 64% rename from _assets/styles/hero.scss rename to _assets/styles/_hero.css index 519388c8d..9b309eb33 100644 --- a/_assets/styles/hero.scss +++ b/_assets/styles/_hero.css @@ -1,4 +1,4 @@ -@import 'variables'; +@import '_variables'; .about-hero { width: 100%; @@ -7,7 +7,7 @@ .hero { position: relative; - color: $white; + color: var(--white); text-align: center; background-image: url('../img/hero.jpg'); background-repeat: no-repeat; @@ -15,7 +15,7 @@ background-position: center; h1 { - color: $white; + color: var(--white); font-size: 28px; } @@ -23,6 +23,15 @@ font-size: 20px; } + &::after { + content: ''; + position: absolute; + inset: 0; + z-index: 0; + opacity: 0.7; + background-color: color-mix(in srgb, var(--primary), #000 10%); + } + @media all and (min-width: 768px) { text-align: left; @@ -34,20 +43,4 @@ font-size: 24px; } } - - .overlay { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background-color: rgba(darken($primary, 5%), 0.7); - z-index: 0; - } - - .hero-content { - position: relative; - z-index: 1; - margin: 25% 0; - } } diff --git a/_assets/styles/masthead.scss b/_assets/styles/_masthead.css similarity index 82% rename from _assets/styles/masthead.scss rename to _assets/styles/_masthead.css index 2e0c7fefb..d5a144868 100644 --- a/_assets/styles/masthead.scss +++ b/_assets/styles/_masthead.css @@ -1,4 +1,4 @@ -@import 'variables'; +@import '_variables'; .masthead { box-sizing: border-box; @@ -6,9 +6,9 @@ top: 0; left: 0; width: 100%; - height: $masthead-height; + height: var(--masthead-height); padding: 10px; - border-bottom: 1px solid $grey-light; + border-bottom: 1px solid var(--grey-light); background-color: rgba(255, 255, 255, 0.98); z-index: 1000; display: table; @@ -26,7 +26,7 @@ .main-header { a { - color: $primary; + color: var(--primary); line-height: 1em; &:hover { @@ -42,7 +42,7 @@ .logo, .logo-small { - transition: $image-transition; + transition: var(--image-transition); vertical-align: middle; height: 40px; } @@ -86,11 +86,11 @@ a { display: inline-block; - font-family: $font-family-header; + font-family: var(--font-family-header); text-decoration: none; &.active { - color: $primary; + color: var(--primary); span::after { content: ''; @@ -100,7 +100,7 @@ width: 100%; display: block; margin-top: 4px; - border-bottom: 2px solid $primary; + border-bottom: 2px solid var(--primary); } } @@ -120,7 +120,7 @@ } svg { - fill: $tertiary; + fill: var(--tertiary); width: 18px; height: 18px; margin-right: 10px; @@ -128,6 +128,6 @@ } a.active svg { - fill: $primary; + fill: var(--primary); } } diff --git a/_assets/styles/module.scss b/_assets/styles/_module.css similarity index 77% rename from _assets/styles/module.scss rename to _assets/styles/_module.css index 53f6ee636..07e9e286d 100644 --- a/_assets/styles/module.scss +++ b/_assets/styles/_module.css @@ -1,4 +1,4 @@ -@import 'variables'; +@import '_variables'; .module { margin: 25px 0; @@ -18,5 +18,5 @@ .module-heading { font-size: 24px; padding-bottom: 10px; - border-bottom: 2px solid $primary; + border-bottom: 2px solid var(--primary); } diff --git a/_assets/styles/overrides.scss b/_assets/styles/_overrides.css similarity index 62% rename from _assets/styles/overrides.scss rename to _assets/styles/_overrides.css index a70b68e6f..b9a68f916 100644 --- a/_assets/styles/overrides.scss +++ b/_assets/styles/_overrides.css @@ -1,13 +1,4 @@ -@import 'variables'; - -html, -body { - margin: 0; - padding: 0; - height: 100%; - color: $black; - font-size: 16px; -} +@import '_variables'; h1, h2, @@ -16,11 +7,11 @@ h4, h5, h6, .header { - color: $primary; - text-shadow: 0 0 1px $white; + color: var(--primary); + text-shadow: 0 0 1px var(--white); svg { - fill: $primary; + fill: currentColor; width: 18px; height: 18px; margin-right: 10px; @@ -36,13 +27,16 @@ h6, ul, ol, p { - margin: 16px 0; - padding: 0; - list-style-type: none; + line-height: 1.2; + margin-top: 16px; + margin-bottom: 16px; + padding-top: 0; + padding-bottom: 0; } h1 { - margin: 25px 0; + margin-top: 25px; + margin-bottom: 25px; svg { width: 24px; @@ -55,7 +49,7 @@ abbr { } a { - color: $tertiary; + color: var(--tertiary); text-decoration: none; transition: background-color, border-color, color, fill; transition-duration: 0.2s; @@ -72,13 +66,6 @@ iframe { margin: 16px 0; } -img { - display: block; - max-width: 100%; - height: auto; - border: none; -} - h1 { font-size: 36px; } diff --git a/_assets/styles/photos.scss b/_assets/styles/_photos.css similarity index 64% rename from _assets/styles/photos.scss rename to _assets/styles/_photos.css index 816d3a49b..20d51e5c1 100644 --- a/_assets/styles/photos.scss +++ b/_assets/styles/_photos.css @@ -1,9 +1,9 @@ -@import 'variables'; +@import '_variables'; .photo-grid { &::before, &::after { - // Clearfix for float + /* Clearfix for float */ content: ''; display: table; clear: both; @@ -13,7 +13,7 @@ float: left; width: 50%; - @media (min-width: $screen-md) { + @media (min-width: theme(screens.md)) { width: 25%; } } diff --git a/_assets/styles/social.scss b/_assets/styles/_social.css similarity index 69% rename from _assets/styles/social.scss rename to _assets/styles/_social.css index 88ce6664c..cfac1c973 100644 --- a/_assets/styles/social.scss +++ b/_assets/styles/_social.css @@ -1,4 +1,4 @@ -@import 'variables'; +@import '_variables'; .social { display: block; @@ -8,8 +8,8 @@ &::before, &::after { - // Clearfix for float - content: ''; + /* Clearfix for float */ + content: ""; display: table; clear: both; } @@ -25,15 +25,15 @@ svg { display: block; - width: $social-icon-size; - height: $social-icon-size; - fill: $white; + width: var(--social-icon-size); + height: var(--social-icon-size); + fill: var(--white); } } a { svg { - transition: $image-transition; + transition: var(--image-transition); } &:hover svg { @@ -42,17 +42,17 @@ a { } .meetup-link { - color: $meetup-color; + color: var(--meetup-color); svg { - fill: $meetup-color; + fill: var(--meetup-color); margin-right: 0; } } .slack-link { svg { - fill: $tertiary; + fill: var(--tertiary); margin-right: 0; } } @@ -74,6 +74,5 @@ p .meetup-link svg, p .slack-link svg { width: 18px; height: 18px; - margin-left: 4px; - margin-bottom: -3px; + margin-bottom: -2px; } diff --git a/_assets/styles/_variables.css b/_assets/styles/_variables.css new file mode 100644 index 000000000..52e218bdb --- /dev/null +++ b/_assets/styles/_variables.css @@ -0,0 +1,30 @@ +:root { + --white: #fff; + --grey-light: #ccc; + --grey-medium: #aaa; + --black: #333; + + --primary: #b144ab; + --secondary: #e97223; + --tertiary: #0da1b8; + --success: #2da73c; + --error: #e35050; + + --primary-dark: color-mix(in srgb, var(--primary), #000 60%); + + --meetup-color: #F64060; + + --green-light: #a6c664; + + --social-icon-size: 32px; + --masthead-height: 70px; + --footer-height: 250px; + + --font-weight-regular: 400; + --font-weight-bold: 600; + + --font-family-header: 'Varela Round', helvetica, arial, sans-serif; + --font-family-body: 'Open Sans', helvetica, arial, sans-serif; + + --image-transition: ease-in-out 0.1s all; +} diff --git a/_assets/styles/_wrapper.css b/_assets/styles/_wrapper.css new file mode 100644 index 000000000..f79ab5f2c --- /dev/null +++ b/_assets/styles/_wrapper.css @@ -0,0 +1,9 @@ +@import '_variables'; + +.wrapper { + position: relative; + box-sizing: border-box; + min-height: 100%; + padding-top: var(--masthead-height); + padding-bottom: var(--footer-height); +} diff --git a/_assets/styles/global.scss b/_assets/styles/global.css similarity index 69% rename from _assets/styles/global.scss rename to _assets/styles/global.css index 1ea7c0ed3..5cbb03754 100644 --- a/_assets/styles/global.scss +++ b/_assets/styles/global.css @@ -1,12 +1,16 @@ -@import 'variables'; -@import 'overrides'; -@import 'wrapper'; -@import 'masthead'; -@import 'hero'; -@import 'social'; -@import 'module'; -@import 'footer'; -@import 'photos'; +@import 'tailwindcss/base'; +@import 'tailwindcss/components'; +@import 'tailwindcss/utilities'; + +@import '_variables'; +@import '_overrides'; +@import '_wrapper'; +@import '_masthead'; +@import '_hero'; +@import '_social'; +@import '_module'; +@import '_footer'; +@import '_photos'; .no-wrap { white-space: nowrap; @@ -26,7 +30,7 @@ h4, h5, h6 { - color: $black; + color: var(--black); } } @@ -49,23 +53,34 @@ margin-top: 8px; } +.prose blockquote { + margin: 0; + padding: 0 1em; + border-left: 4px solid var(--primary); + font-style: italic; +} + +.prose cite:before { + content: '— '; +} + .prose .image { border: none; } .prose .bordered { padding: 4px; - border: 1px solid $grey-light; + border: 1px solid var(--grey-light); } .prose .bordered:hover { - border-color: $tertiary; + border-color: var(--tertiary); } .bordered { display: inline-block; padding: 4px; - border: 1px solid $grey-light; + border: 1px solid var(--grey-light); } .bordered img { @@ -87,21 +102,6 @@ vertical-align: text-top; } -.container { - max-width: 1024px; - margin: 0 auto; - padding: 0 16px; - - &::before, - &::after { - content: ''; - display: table; - width: 0; - height: 0; - clear: both; - } -} - .event-detail .lanyrd-link { top: 7px; left: 5px; @@ -208,37 +208,16 @@ color: #2e2e2e; } -.news { - margin-top: -20px; -} - -.news:first-of-type { - margin-top: 0; -} - -.news:after { - content: ""; - margin: 0; -} - -.news a { - display: block; - padding: 8px 0; - height: auto; - line-height: 1; -} - -.news:hover a { - color: $black; - border-color: $grey-medium; -} +.news-item .notifier { + color: var(--primary); -.news strong { - font-weight: normal; + svg { + fill: var(--primary); + } } .news-item .read-more { - margin-top: -16px; + margin-top: -8px; } .dharmafly { @@ -249,20 +228,15 @@ .dharmafly:hover, .dharmafly:focus { - color: $green-light; + color: var(--green-light); } .dharmafly:active { - color: $white; - background-color: $green-light; + color: var(--white); + background-color: var(--green-light); border-bottom: none; } -.extra .heading { - margin-bottom: 8px; - margin-top: 0; -} - .lanyrd-people { list-style-type: none; overflow: hidden; @@ -318,37 +292,7 @@ margin-bottom: 16px; } -.iframe-wrapper { - position: relative; - padding-bottom: 65%; - margin-bottom: 20px; -} - -.iframe { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - border: none; - max-width: 100%; -} - -.iframe-slides .iframe { - width: 200%; - height: 200%; - max-width: 200%; - transform: scale(0.62); - transform-origin: 0; -} - -#media-test { - display: none; - visibility: hidden; - width: 0; -} - -@media (min-width: $screen-xs) { +@media (min-width: theme(screens.sm)) { h2 { font-size: 36px; } @@ -362,10 +306,6 @@ margin: 4px 0 12px 12px; } - #media-test { - width: 1px; - } - .event-detail .sponsors { position: absolute; right: 0; @@ -380,7 +320,7 @@ } } -@media (min-width: $screen-md) { +@media (min-width: theme(screens.md)) { .column { float: left; box-sizing: border-box; @@ -399,21 +339,21 @@ body, .masthead p em { - font-family: $font-family-body; - font-weight: $font-weight-regular; + font-family: var(--font-family-body); + font-weight: var(--font-weight-regular); } i, em { - font-family: $font-family-body; + font-family: var(--font-family-body); } b, strong, .read-more a, .event-detail .vevent, .event-item .lanyrd-simple { - font-family: $font-family-body; - font-weight: $font-weight-bold; + font-family: var(--font-family-body); + font-weight: var(--font-weight-bold); } h1, @@ -424,7 +364,13 @@ h5, h6, .header, .news strong { - font-family: $font-family-header; - font-weight: $font-weight-regular; - text-rendering: optimizeLegibility; // May cause issues in Windows browsers + font-family: var(--font-family-header); + font-weight: var(--font-weight-regular); + /* May cause issues in Windows browsers */ + text-rendering: optimizeLegibility; +} + +img, +video { + width: auto; } diff --git a/_assets/styles/variables.scss b/_assets/styles/variables.scss deleted file mode 100644 index 4452f2751..000000000 --- a/_assets/styles/variables.scss +++ /dev/null @@ -1,32 +0,0 @@ -$screen-xs: 480px; -$screen-sm: 560px; -$screen-md: 768px; - -$white: #fff; -$grey-light: #ccc; -$grey-medium: #aaa; -$black: #333; - -$primary: #b144ab; -$secondary: #e97223; -$tertiary: #0da1b8; -$success: #2da73c; -$error: #e35050; - -$primary-dark: darken($primary, 30%); - -$meetup-color: #F64060; - -$green-light: #a6c664; - -$social-icon-size: 32px; -$masthead-height: 70px; -$footer-height: 250px; - -$font-weight-regular: 400; -$font-weight-bold: 600; - -$font-family-header: 'Varela Round', helvetica, arial, sans-serif; -$font-family-body: 'Open Sans', helvetica, arial, sans-serif; - -$image-transition: ease-in-out 0.1s all; diff --git a/_assets/styles/wrapper.scss b/_assets/styles/wrapper.scss deleted file mode 100644 index 9bc12fec9..000000000 --- a/_assets/styles/wrapper.scss +++ /dev/null @@ -1,9 +0,0 @@ -@import 'variables'; - -.wrapper { - position: relative; - box-sizing: border-box; - min-height: 100%; - padding-top: $masthead-height; - padding-bottom: $footer-height; -} diff --git a/_includes/helpers/iconmonstr.js b/_includes/helpers/iconmonstr.js index f44d27b3d..b176edbba 100644 --- a/_includes/helpers/iconmonstr.js +++ b/_includes/helpers/iconmonstr.js @@ -1,5 +1,4 @@ const fs = require('fs'); -const path = require('path'); const { images } = require('../../paths.json'); module.exports.iconmonstr = (input) => diff --git a/_includes/helpers/markdown.js b/_includes/helpers/markdown.js index 8b09cc331..3c539465f 100644 --- a/_includes/helpers/markdown.js +++ b/_includes/helpers/markdown.js @@ -1,3 +1,3 @@ 'use strict' -module.exports = require('marked').marked.parse +module.exports.markdown = require('marked').marked.parse diff --git a/_includes/helpers/striptags.js b/_includes/helpers/striptags.js index c01087b2f..3fd5a5c00 100644 --- a/_includes/helpers/striptags.js +++ b/_includes/helpers/striptags.js @@ -1,3 +1,3 @@ 'use strict' -module.exports = require('striptags') +module.exports.striptags = require('striptags') diff --git a/_includes/partials/eventDetail.html b/_includes/partials/eventDetail.hbs similarity index 94% rename from _includes/partials/eventDetail.html rename to _includes/partials/eventDetail.hbs index 4ab82a449..52afb5b7d 100644 --- a/_includes/partials/eventDetail.html +++ b/_includes/partials/eventDetail.hbs @@ -1,7 +1,7 @@
    -

    - {{ title }} +

    + {{ title }} {{> meetupLink link=meetup title=title}}

    diff --git a/_includes/partials/eventItem.html b/_includes/partials/eventItem.hbs similarity index 76% rename from _includes/partials/eventItem.html rename to _includes/partials/eventItem.hbs index d9220d0a4..f4f21869b 100644 --- a/_includes/partials/eventItem.html +++ b/_includes/partials/eventItem.hbs @@ -1,5 +1,5 @@ -

    - {{ title }} +

    + {{ title }} {{> meetupLink link=meetup title=title}}

    diff --git a/_includes/partials/footer.html b/_includes/partials/footer.hbs similarity index 94% rename from _includes/partials/footer.html rename to _includes/partials/footer.hbs index 7560bb39f..dd5194827 100644 --- a/_includes/partials/footer.html +++ b/_includes/partials/footer.hbs @@ -1,5 +1,5 @@

    -
    +
    -
    diff --git a/_includes/partials/head.html b/_includes/partials/head.hbs similarity index 93% rename from _includes/partials/head.html rename to _includes/partials/head.hbs index 69eb6fcd0..8d1de8d94 100644 --- a/_includes/partials/head.html +++ b/_includes/partials/head.hbs @@ -1,6 +1,6 @@ - + {{#if title}}{{ title }} – {{ site.title }}{{/if}}{{#unless title}}{{ site.title }} - {{{ striptags (markdown site.caption) }}}{{/unless}} diff --git a/_includes/partials/masthead.html b/_includes/partials/masthead.hbs similarity index 83% rename from _includes/partials/masthead.html rename to _includes/partials/masthead.hbs index ddd2567ff..4cab5b74b 100644 --- a/_includes/partials/masthead.html +++ b/_includes/partials/masthead.hbs @@ -17,6 +17,11 @@

    {{{ iconmonstr 'calendar' }}}Events +
  • + + {{{ iconmonstr 'news' }}}News + +
  • {{{ iconmonstr 'about' }}}About diff --git a/_includes/partials/meetupLink.hbs b/_includes/partials/meetupLink.hbs new file mode 100644 index 000000000..c4a005d23 --- /dev/null +++ b/_includes/partials/meetupLink.hbs @@ -0,0 +1,5 @@ +{{#if link}} + + {{ text }}{{{ iconmonstr 'meetup' }}} + +{{/if}} diff --git a/_includes/partials/meetupLink.html b/_includes/partials/meetupLink.html deleted file mode 100644 index a04d14798..000000000 --- a/_includes/partials/meetupLink.html +++ /dev/null @@ -1,5 +0,0 @@ -{{#if link}} - - {{ text }}{{{ iconmonstr 'meetup' }}} - -{{/if}} diff --git a/_includes/partials/nextEvent.html b/_includes/partials/nextEvent.hbs similarity index 77% rename from _includes/partials/nextEvent.html rename to _includes/partials/nextEvent.hbs index 982fd2354..0905b144d 100644 --- a/_includes/partials/nextEvent.html +++ b/_includes/partials/nextEvent.hbs @@ -1,5 +1,7 @@
    -

    {{{ iconmonstr 'calendar-tick' }}}Next Event

    +

    + {{{ iconmonstr 'calendar-tick' }}}Next Event +

    {{#each soonest}} {{> eventDetail site=../site }} {{/each}} diff --git a/_includes/partials/postDescription.html b/_includes/partials/postDescription.hbs similarity index 77% rename from _includes/partials/postDescription.html rename to _includes/partials/postDescription.hbs index fb942d734..ef809542c 100644 --- a/_includes/partials/postDescription.html +++ b/_includes/partials/postDescription.hbs @@ -6,7 +6,7 @@ {{/if}} {{{ contents }}} {{#if post.meetup}} -

    Thinking of coming? {{> meetupLink link=post.meetup title=post.title text='Join us on Meetup'}} +

    Thinking of coming? {{> meetupLink link=post.meetup title=post.title text='Join us on Meetup'}} {{/if}}

    {{> slackLink link=site.elsewhere.slack.link title='Join the Async Slack group' text='Stay up-to-date and join the community on Slack'}}

    diff --git a/_includes/partials/slackLink.hbs b/_includes/partials/slackLink.hbs new file mode 100644 index 000000000..386ef7bc6 --- /dev/null +++ b/_includes/partials/slackLink.hbs @@ -0,0 +1,5 @@ +{{#if link}} + + {{ text }}{{{ iconmonstr 'slack' }}} + +{{/if}} diff --git a/_includes/partials/slackLink.html b/_includes/partials/slackLink.html deleted file mode 100644 index 39f157922..000000000 --- a/_includes/partials/slackLink.html +++ /dev/null @@ -1,5 +0,0 @@ -{{#if link}} - - {{ text }}{{{ iconmonstr 'slack' }}} - -{{/if}} diff --git a/_includes/partials/socialIcons.html b/_includes/partials/socialIcons.hbs similarity index 100% rename from _includes/partials/socialIcons.html rename to _includes/partials/socialIcons.hbs diff --git a/_includes/partials/userList.html b/_includes/partials/userList.hbs similarity index 100% rename from _includes/partials/userList.html rename to _includes/partials/userList.hbs diff --git a/_layouts/archive.html b/_layouts/archive.hbs similarity index 67% rename from _layouts/archive.html rename to _layouts/archive.hbs index b90073b3e..5c4471e59 100644 --- a/_layouts/archive.html +++ b/_layouts/archive.hbs @@ -1,17 +1,17 @@ - + {{> head}} - -
    + +
    {{> masthead}} -
    -

    +
    +

    {{{ iconmonstr 'archive' }}}{{ title }}

    {{#if children}}