// #@ts-check (function(window, document){
// prerequisites if ( ! ( 'querySelectorAll' in document ) || ! ( 'filter' in [] ) || ! ( 'content' in document.createElement('template') ) ){ return; } if ( ! ( 'JekyllWebmentionIO' in window ) ){ window.JekyllWebmentionIO = {}; } // // Public Properties // JekyllWebmentionIO.existing_webmentions = []; // // Public Methods // JekyllWebmentionIO.processWebmentions = function( data ){ // console.log( 'incoming webmentions', data.links ); if ( data && ! ( 'error' in data ) ) { var webmentions = data.links.reverse(); webmentions = rationalizeIds( webmentions ); webmentions = removeDuplicates( webmentions ); // We may not need to proceed if we had them all if ( webmentions.length ) { webmentions = addMetadata( webmentions ); // hande them out doleOutWebmentions( webmentions ); // reset the counters if ( this.counter_update_event ) { document.dispatchEvent( this.counter_update_event ); } } else { // console.log( 'no new webmentions to add' ); } } }; // // Private Properties // var webmention_receivers = {}, templates = {}; // // Private Methods // // Gathers embewdded templates function collectTemplates() { var $templates = document.querySelectorAll( 'template[id^=webmention-]' ), t = $templates.length, $template; while ( t-- ) { $template = $templates[t]; // We only need the list (if one exists) if ( $template.content.querySelector('ol') ) { templates[$template.id] = $template.content.querySelector('ol'); } else { templates[$template.id] = $template.content; } } $template = null; } // Collects webmentions that are already on the page function collectExistingWebmentions() { var $existing_webmentions = document.querySelectorAll( '[id^=webmention-]' ), e = $existing_webmentions.length; while ( e-- ) { JekyllWebmentionIO.existing_webmentions.push( $existing_webmentions[e] .getAttribute( 'id' ) .replace( 'webmention-', '' ) ); } $existing_webmentions = null; } function identifyWebmentionCollections() { var $webmention_collections = document.querySelectorAll( '.webmentions' ), w = $webmention_collections.length, $webmention_collection, type, types, t; // Assign the type & template if we can determine it while ( w-- ) { $webmention_collection = $webmention_collections[w]; // Assign the type type = 'webmentions'; // Generic if ( $webmention_collection.className.indexOf('webmentions--') > -1 ) { type = $webmention_collection.className.match(/webmentions\-\-(.*)/)[1]; } $webmention_collection.type = type; // Assign the template if ( templates['webmention-' + type] ) { $webmention_collection.template = templates['webmention-' + type]; } // Add to the queues if ( 'dataset' in $webmention_collection && 'webmentionTypes' in $webmention_collection.dataset ) { types = $webmention_collection.dataset.webmentionTypes.split(','); } else { types = [ type ]; } t = types.length; while (t--) { type = types[t]; if ( ! ( type in webmention_receivers ) ) { webmention_receivers[type] = []; } webmention_receivers[type].push( $webmention_collection ); } } $webmention_collection = null; } // Divvies up the webmentions and populate the lists function doleOutWebmentions( webmentions ) { var i = 0, j, webmention, incoming = {}, queue_keys = Object.keys( webmention_receivers ), plural_type, typeFilter = function(key) { return JekyllWebmentionIO.types[key] === this.type; }, typeFilterLoop; // set up the queues i = queue_keys.length; while( i--) { incoming[queue_keys[i]] = []; } // Assign the webmentions to their respective queues i = webmentions.length; while ( i-- ) { webmention = webmentions[i]; // reverse lookup to get the plural from the singular typeFilterLoop = typeFilter.bind(webmention); plural_type = Object.keys(JekyllWebmentionIO.types) .filter(typeFilterLoop)[0]; // Is there a specific queue requesting this? if ( queue_keys.indexOf( plural_type ) > -1 ) { incoming[plural_type].push( webmention ); } // If there’s a generic, add it there too if ( incoming.webmentions ) { incoming.webmentions.push( webmention ); } } // Now hand them out i = queue_keys.length; while( i-- ) { j = webmention_receivers[queue_keys[i]].length; while ( j-- ) { // No point passing nothing if ( incoming[queue_keys[i]].length > 0 ) { addWebmentionsToCollection( incoming[queue_keys[i]], webmention_receivers[queue_keys[i]][j] ); } } } } function addWebmentionsToCollection( mentions, $webmention_collection ) { if ( mentions.length < 1 ) { console.warn( 'No webmentions to add, check your application code' ); return; } if ( ! $webmention_collection.template ) { console.error( 'No template found for this webmention group', $webmention_collection ); return; } if ( ! ( 'Liquid' in window ) ) { console.error( 'Liquid parsing engine is not available' ); return; } var $list = $webmention_collection.querySelector('.webmentions__list'), template = $webmention_collection.template, mode = 'append', html; // Already working with a list if ( $list ) { template = Liquid.parse( template.innerHTML ); } // Need a list else { template = Liquid.parse( template.outerHTML ); mode = 'replace'; } // append html = template.render({ 'webmentions': mentions }); if ( mode == 'append' ) { $list.innerHTML += html; } else { $webmention_collection.innerHTML = html; } // console.log( 'Successfully added', mentions.length ); } // Uses the ID attribute for everything except tweets function rationalizeIds( webmentions ) { // console.log( 'rationizing IDs' ); var i = webmentions.length, id, url; while ( i-- ) { id = webmentions[i].id; url = webmentions[i].data.url || webmentions[i].source; if ( url && url.indexOf( 'twitter.com/' ) > -1 ) { // Unique tweets gets unique IDs if ( url.indexOf( '#favorited-by' ) < 0 ) { id = url.replace( /^.*?status\/(\d+)$/, '$1' ); } } // coerce to a string webmentions[i].id = id + ''; } //console.log( webmentions.length, 'IDs rationalized' ); return webmentions; } // Removes duplicate webmentions function removeDuplicates( webmentions ) { // console.log( 'removing duplicates' ); // going backwards, so reverse things to start out webmentions.reverse(); var unique_webmentions = [], i = webmentions.length, id; while ( i-- ) { if ( JekyllWebmentionIO.existing_webmentions.indexOf( webmentions[i].id ) < 0 ) { unique_webmentions.push(webmentions[i]); JekyllWebmentionIO.existing_webmentions.push(webmentions[i].id); } } // console.log( 'removed', webmentions.length - unique_webmentions.length, 'duplicates', unique_webmentions ); return unique_webmentions; } // Adds the necessary metadata to each webmention object for the liquid templates function addMetadata( webmentions ) { // console.log( 'adding metadata' ); // going backwards, so reverse things to start out webmentions.reverse(); var i = webmentions.length, webmention, webmention_object, uri, source, pubdate, type, title, content, read = function( html_source ){ if ( html_source ) { updateTitle( this.id, this.uri, html_source ); } }, loop_read; while ( i-- ) { webmention = webmentions[i]; uri = webmention.data.url || webmention.source; source = false; if ( uri.indexOf('twitter.com/') ) { source = 'twitter'; } else if ( uri.indexOf('/googleplus/') > -1 ) { source = 'googleplus'; } pubdate = webmention.data.published_ts; if ( ! pubdate && webmention.verified_date ) { pubdate = webmention.verified_date; } if ( pubdate ) { pubdate = (new Date(pubdate)).getTime(); } webmention_object = { id: webmention.id, url: uri, source: source, pubdate: pubdate, raw: webmentions[i] }; if ( 'author' in webmention.data ) { webmention_object.author = webmentions[i].data.author; } type = webmentions[i].activity.type; if ( ! type ) { if ( source == 'googleplus' ) { if ( uri.indexOf('/like/') > -1 ) { type = 'like'; } else if ( uri.indexOf( '/repost/' ) > -1 ) { type = 'repost'; } else if ( uri.indexOf( '/comment/' ) > -1 ) { type = 'reply'; } else { type = 'link'; } } else { type = 'post'; } } webmention_object.type = type; // Posts title = false; if ( type == 'post' ) { loop_read = read.bind({ id: webmention_object.id, uri: uri }); readWebPage( uri, loop_read ); } content = webmention.data.content; if ( type != 'bookmark' && type != 'link' && type != 'post' && type != 'reply' ) { content = webmention.activity.sentence_html; } webmention_object.content = content; // replace the existing webmention webmentions[i] = webmention_object; } // console.log( 'added metadata to', webmentions.length, 'webmentions' ); return webmentions; } // Async update of the title function updateTitle( webmention_id, url, html_source ) { var $webmention = document.querySelector( '#webmention-' + webmention_id ), $current_title = $webmention.querySelector( '.webmention__title' ), $page = document.createElement('html'), title = '', $link_title, $title, $h1; $page.innerHTML = html_source; $title = $page.querySelector('title'); $h1 = $page.querySelector('h1'); if ( $current_title.length < 0 ) { $current_title = $webmention.querySelector( '.webmention__content' ); } if ( $current_title.length > 0 ) { if ( $title.length > 0 ) { title = $title.innerText; } else { if ( $h1.length > 0 ) { title = $h1.innerHTML; } else { title = 'No title available'; } } if ( title ) { // cleanup title = title.replace( /<\/?[^>]+?>}/, '' ); $link_title = document.createElement('a'); $link_title.href = url; $link_title.appendChild( document.createTextNode( title ) ); // replace title contents $current_title.innerHTML = $link_title.outerHTML; } } } // Synchromous XHR proxied through whateverorigin.org function readWebPage( uri, callback ) { // jshint -W021 if ( 'XMLHttpRequest' in window ) { var XHR = new XMLHttpRequest(); readWebPage = function( uri, callback ){ var done = false; uri = '//whateverorigin.org/get?url=' + encodeURIComponent( uri ); XHR.onreadystatechange = function() { if ( this.readyState == 4 && ! done ) { done = true; callback( XHR.responseText ); } }; XHR.onabort = function() { if ( ! done ) { done = true; callback( false ); } }; XHR.onerror = XHR.onabort; XHR.open( 'GET', uri ); XHR.send( null ); }; } else { readWebPage = function( uri, callback ){ callback( false ); }; } // jshint +W021 return readWebPage( uri, callback ); } // init collectTemplates(); collectExistingWebmentions(); identifyWebmentionCollections();
}(this, this.document));