Merge branch 'master' into mcaskill-scss

This commit is contained in:
Dominic Lord
2017-03-02 12:28:01 -05:00
committed by GitHub
21 changed files with 3038 additions and 169 deletions

View File

@@ -1,3 +1,4 @@
{
"presets": [ "es2015" ]
"presets": [ "es2015" ],
"plugins": [ "babel-plugin-transform-export-extensions" ]
}

View File

@@ -1,9 +1,11 @@
/* jshint esnext: true */
import { $document } from './utils/environment';
import { getNodeData } from './utils/html';
// Global functions and tools
import globals from './utils/globals';
import globals from './globals';
import { arrayContains, removeFromArray } from './utils/array';
import { getNodeData } from './utils/html';
import { isFunction } from './utils/is';
// Basic modules
import * as modules from './modules';
@@ -15,23 +17,52 @@ class App {
$document.on('initModules.App', (event) => {
this.initGlobals(event.firstBlood)
.deleteModules()
.initModules();
.deleteModules(event)
.initModules(event);
});
$document.on('initScopedModules.App', (event) => {
this.initModules(event);
});
$document.on('deleteScopedModules.App', (event) => {
this.deleteModules(event);
});
}
/**
* Destroy all existing modules
* @return {Object} this Allows chaining
* Destroy all existing modules or a specific scope of modules
* @param {Object} event The event being triggered.
* @return {Object} Self (allows chaining)
*/
deleteModules() {
// Loop modules
var i = this.currentModules.length;
deleteModules(event) {
let destroyAll = true;
let moduleIds = [];
// Check for scope first
if (event.$scope instanceof jQuery && event.$scope.length > 0) {
// Modules within scope
const $modules = event.$scope.find('[data-module]');
// Determine their uids
moduleIds = $.makeArray($modules.map(function(index) {
return $modules.eq(index).data('uid');
}));
if (moduleIds.length > 0) {
destroyAll = false;
}
}
// Loop modules and destroying all of them, or specific ones
let i = this.currentModules.length;
// Destroy all modules
while (i--) {
this.currentModules[i].destroy();
this.currentModules.splice(i);
if (destroyAll || arrayContains(moduleIds, this.currentModules[i].uid)) {
removeFromArray(moduleIds, this.currentModules[i].uid);
this.currentModules[i].destroy();
this.currentModules.splice(i);
}
}
return this;
@@ -41,7 +72,7 @@ class App {
* Execute global functions and settings
* Allows you to initialize global modules only once if you need
* (ex.: when using Barba.js or SmoothState.js)
* @return {Object} this Allows chaining
* @return {Object} Self (allows chaining)
*/
initGlobals(firstBlood) {
globals(firstBlood);
@@ -50,27 +81,39 @@ class App {
/**
* Find modules and initialize them
* @return {Object} this Allows chaining
* @param {Object} event The event being triggered.
* @return {Object} Self (allows chaining)
*/
initModules() {
initModules(event) {
// Elements with module
var moduleEls = document.querySelectorAll('[data-module]');
let $moduleEls = [];
// If first blood, load all modules in the DOM
// If scoped, render elements with modules
// If Barba, load modules contained in Barba container
if (event.firstBlood) {
$moduleEls = $document.find('[data-module]');
} else if (event.$scope instanceof jQuery && event.$scope.length > 0) {
$moduleEls = event.$scope.find('[data-module]');
} else if (event.isBarba) {
$moduleEls = $('#js-barba-wrapper').find('[data-module]');
}
// Loop through elements
var i = 0;
var elsLen = moduleEls.length;
let i = 0;
const elsLen = $moduleEls.length;
for (; i < elsLen; i++) {
// Current element
let el = moduleEls[i];
let el = $moduleEls[i];
// All data- attributes considered as options
let options = getNodeData(el);
// Add current DOM element and jQuery element
options.el = el;
options.$el = $(el);
options.$el = $moduleEls.eq(i);
// Module does exist at this point
let attr = options.module;
@@ -88,6 +131,7 @@ class App {
if (typeof this.modules[moduleAttr] === 'function') {
let module = new this.modules[moduleAttr](options);
this.currentModules.push(module);
module.init();
}
}
}
@@ -99,8 +143,8 @@ class App {
// IIFE for loading the application
// ==========================================================================
(function() {
window.App = new App();
$document.trigger({
new App();
$document.triggerHandler({
type: 'initModules.App',
firstBlood: true
});

10
assets/scripts/globals.js Normal file
View File

@@ -0,0 +1,10 @@
/* jshint esnext: true */
import TransitionManager from './transitions/TransitionManager';
export default function(firstBlood) {
svg4everybody();
if (firstBlood) {
const transitionManager = new TransitionManager();
}
}

View File

@@ -1,3 +1 @@
/* jshint esnext: true */
export {default as Button} from './modules/Button';
export {default as Title} from './modules/Title';

View File

@@ -1,17 +1,28 @@
/* jshint esnext: true */
import { $document, $window, $html, $body } from '../utils/environment';
let uid = 0;
/**
* Abstract module
* Gives access to generic jQuery nodes
* Abstract Module
*/
export default class {
constructor(options) {
this.$document = $document;
this.$window = $window;
this.$html = $html;
this.$body = $body;
this.$el = options.$el;
this.el = options.el;
export default class
{
constructor(options)
{
this.$el = options.$el || null;
this.el = options.el || null;
// Generate a unique module identifier
this.uid = 'm-' + uid++;
// Use jQuery's data API to "store it in the DOM"
this.$el.data('uid', this.uid);
}
init() {}
destroy()
{
if (this.$el) {
this.$el.off();
}
}
}

View File

@@ -1,16 +0,0 @@
/* jshint esnext: true */
import AbstractModule from './AbstractModule';
export default class extends AbstractModule {
constructor(options) {
super(options);
this.$el.on('click.Button', (event) => {
this.$document.trigger('Title.changeLabel', [$(event.currentTarget).val()]);
});
}
destroy() {
this.$el.off('.Button');
}
}

View File

@@ -1,58 +0,0 @@
/* jshint esnext: true */
import { visibilityApi } from '../utils/visibility';
import AbstractModule from './AbstractModule';
export default class extends AbstractModule {
constructor(options) {
super(options);
this.$label = this.$el.find('.js-label');
this.$document.on('Title.changeLabel', (event, value) => {
this.changeLabel(value);
this.destroy();
});
this.hiddenCallbackIdent = visibilityApi({
action: 'addCallback',
state: 'hidden',
callback: this.logHidden
});
this.visibleCallbackIdent = visibilityApi({
action: 'addCallback',
state: 'visible',
callback: this.logVisible
});
}
logHidden() {
console.log('Title is hidden');
}
logVisible() {
console.log('Title is visible');
}
changeLabel(value) {
this.$label.text(value);
}
destroy() {
this.$document.off('Title.changeLabel');
visibilityApi({
action: 'removeCallback',
state: 'hidden',
ident: this.hiddenCallbackIdent
});
visibilityApi({
action: 'removeCallback',
state: 'visible',
ident: this.visibleCallbackIdent
});
this.$el.off('.Title');
}
}

View File

@@ -0,0 +1,51 @@
/* jshint esnext: true */
import { $document, $html } from '../utils/environment';
function DefaultTransition(options) {
options = options || {};
const startCallback = (typeof options.startCallback === 'function') ? options.startCallback : function(){};
const overrideClass = (typeof options.overrideClass === 'string') ? options.overrideClass : '';
return Barba.BaseTransition.extend({
start: function() {
$html
.removeClass('dom-is-loaded dom-is-animated')
.addClass(`dom-is-loading ${overrideClass}`);
startCallback();
/* Close any overlays */
setTimeout(() => {
Promise
.all([this.newContainerLoading])
.then(this.finish.bind(this));
}, 1000);
},
finish: function() {
this.done();
const $el = $(this.newContainer);
// Get the template name of the new container and set it to the DOM
$html.attr('data-template', $el.data('template'));
$document.triggerHandler({
type: 'initModules.App',
isBarba: true
});
$html
.addClass('dom-is-loaded')
.removeClass('dom-is-loading');
setTimeout(() => {
$html
.removeClass(overrideClass)
.addClass('dom-is-animated');
}, 1000);
}
});
}
export default DefaultTransition;

View File

@@ -0,0 +1,86 @@
/* jshint esnext: true */
import { $document, $html, isDebug } from '../utils/environment';
import DefaultTransition from './DefaultTransition';
export default class {
constructor() {
let clickedLink = undefined;
let transition = '';
// jQuery ondomready
$(() => {
this.load()
});
$document.on('goTo.PageTransitionManager', (event) => {
if (!window.history.pushState) {
window.location = event.options.location;
} else {
transition = event.options.transition;
Barba.Pjax.goTo(event.options.location);
}
});
// Define different page transitions
Barba.Pjax.getTransition = function() {
transition = (clickedLink instanceof Node) ? clickedLink.getAttribute('data-transition') : (typeof transition === 'string' ? transition : '');
let TransitionObject;
switch (transition) {
default:
TransitionObject = DefaultTransition();
}
clickedLink = undefined;
transition = '';
return TransitionObject;
}
Barba.Dispatcher.on('linkClicked', (currentStatus, oldStatus, container) => {
clickedLink = currentStatus;
});
Barba.Dispatcher.on('newPageReady', (currentStatus, prevStatus, container, currentHTML) => {
// Fetch any inline script elements.
const scripts = container.querySelectorAll('script.js-inline');
if (scripts instanceof window.NodeList) {
let i = 0;
let len = scripts.length;
for (; i < len; i++) {
eval(scripts[i].innerHTML);
}
}
/**
* Execute any third party features.
*/
// Google Analytics
if (window.ga && !isDebug) {
ga('send', 'pageview');
}
});
Barba.Pjax.Dom.containerClass = 'js-barba-container';
Barba.Pjax.Dom.wrapperId = 'js-barba-wrapper';
Barba.Pjax.start();
}
/**
* DOM is loaded
*
* @return {void}
*/
load() {
$html.addClass('dom-is-loaded');
$html.removeClass('dom-is-loading');
setTimeout(() => {
$html.addClass('dom-is-animated');
}, 1000)
}
}

View File

@@ -1,7 +1,7 @@
import { isArray } from './is';
export function addToArray ( array, value ) {
var index = array.indexOf( value );
const index = array.indexOf( value );
if ( index === -1 ) {
array.push( value );
@@ -19,7 +19,7 @@ export function arrayContains ( array, value ) {
}
export function arrayContentsMatch ( a, b ) {
var i;
let i;
if ( !isArray( a ) || !isArray( b ) ) {
return false;
@@ -68,7 +68,8 @@ export function removeFromArray ( array, member ) {
}
export function toArray ( arrayLike ) {
var array = [], i = arrayLike.length;
const array = [];
let i = arrayLike.length;
while ( i-- ) {
array[i] = arrayLike[i];
}

View File

@@ -0,0 +1,15 @@
export default function(func, wait, immediate) {
let timeout;
return function() {
const context = this;
const args = arguments;
const later = function() {
timeout = null;
if (!immediate) func.apply(context, args);
};
const callNow = immediate && !timeout;
clearTimeout(timeout);
timeout = setTimeout(later, wait);
if (callNow) func.apply(context, args);
};
}

View File

@@ -1,6 +1,11 @@
const $document = $(document);
const $window = $(window);
const $html = $(document.documentElement);
const $body = $(document.body);
const APP_NAME = 'boilerplate';
const DATA_API_KEY = '.data-api';
export { $document, $window, $html, $body };
const $document = $(document);
const $window = $(window);
const $html = $(document.documentElement);
const $body = $(document.body);
const isDebug = !!$html.data('debug');
export { APP_NAME, DATA_API_KEY, $document, $window, $html, $body, isDebug };

View File

@@ -1,5 +0,0 @@
/* jshint esnext: true */
export default function() {
svg4everybody();
}

View File

@@ -27,13 +27,13 @@ export function unescapeHtml(str) {
*/
export function getNodeData(node) {
// All attributes
var attributes = node.attributes;
const attributes = node.attributes;
// Regex Pattern
var pattern = /^data\-(.+)$/;
const pattern = /^data\-(.+)$/;
// Output
var data = {};
const data = {};
for (let i in attributes) {
if (!attributes[i]) {
@@ -61,7 +61,7 @@ export function getNodeData(node) {
return data;
}
var rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/;
const rbrace = /^(?:\{[\w\W]*\}|\[[\w\W]*\])$/;
/**
* Parse value to data type.

View File

@@ -1,5 +1,5 @@
var toString = Object.prototype.toString,
arrayLikePattern = /^\[object (?:Array|FileList)\]$/;
const toString = Object.prototype.toString;
const arrayLikePattern = /^\[object (?:Array|FileList)\]$/;
// thanks, http://perfectionkills.com/instanceof-considered-harmful-or-how-to-write-a-robust-isarray/
export function isArray ( thing ) {
@@ -32,6 +32,6 @@ export function isObject ( thing ) {
}
export function isFunction( thing ) {
var getType = {};
const getType = {};
return thing && getType.toString.call(thing) === '[object Function]';
}

View File

@@ -1,7 +1,9 @@
/* jshint esnext: true */
var isAnimating = false;
import { isNumeric } from './is'
var defaults = {
let isAnimating = false;
const defaults = {
easing: 'swing',
headerOffset: 60,
speed: 300
@@ -14,7 +16,7 @@ var defaults = {
* @param {object} options
*/
export function scrollTo($element, options) {
var deferred = $.Deferred();
const deferred = $.Deferred();
// Drop everything if this ain't a jQuery object
if ($element instanceof jQuery && $element.length > 0) {
@@ -27,21 +29,30 @@ export function scrollTo($element, options) {
isAnimating = true;
// Default container that we'll be scrolling
var $container = $('html, body');
var elementOffset = 0;
let $container = $('html, body');
let elementOffset = 0;
// Testing container in options for jQuery-ness
// If we're not using a custom container, we take the top document offset
// If we are, we use the elements position relative to the container
if (typeof options.$container !== 'undefined' && options.$container instanceof jQuery && options.$container.length > 0) {
$container = options.$container;
elementOffset = $element.position().top
if (typeof options.scrollTop !== 'undefined' && isNumeric(options.scrollTop) && options.scrollTop !== 0) {
scrollTop = options.scrollTop;
} else {
scrollTop = $element.position().top - options.headerOffset;
}
} else {
elementOffset = $element.offset().top
if (typeof options.scrollTop !== 'undefined' && isNumeric(options.scrollTop) && options.scrollTop !== 0) {
scrollTop = options.scrollTop;
} else {
scrollTop = $element.offset().top - options.headerOffset;
}
}
$container.animate({
scrollTop: elementOffset - options.headerOffset
scrollTop: scrollTop
}, options.speed, options.easing, function() {
isAnimating = false;
deferred.resolve();

View File

@@ -5,7 +5,8 @@ module.exports = {
},
src : [
'<%= paths.js.src %>/vendors/**/*.js',
'<%= paths.npm %>/svg4everybody/dist/svg4everybody.min.js'
'<%= paths.npm %>/barba.js/dist/barba.js',
'<%= paths.npm %>/svg4everybody/dist/svg4everybody.js'
],
dest : '<%= paths.js.dist %>/vendors.js'
}

View File

@@ -5,30 +5,32 @@
"version": "1.0.0",
"author": "Locomotive <info@locomotive.ca>",
"dependencies": {
"svg4everybody": "0.0.0"
"barba.js": "",
"svg4everybody": ""
},
"devDependencies": {
"autoprefixer": "0.0.0",
"babel-preset-es2015": "0.0.0",
"babel-plugin-transform-es2015-classes": "0.0.0",
"babelify": "0.0.0",
"eslint-tap": "0.0.0",
"glob": "0.0.0",
"grunt": "0.0.0",
"grunt-browser-sync": "0.0.0",
"grunt-browserify": "0.0.0",
"grunt-contrib-concat": "0.0.0",
"grunt-contrib-cssmin": "0.0.0",
"grunt-contrib-uglify": "0.0.0",
"grunt-contrib-watch": "0.0.0",
"grunt-csscomb": "0.0.0",
"grunt-eslint": "0.0.0",
"grunt-notify": "0.0.0",
"grunt-phplint": "0.0.0",
"grunt-postcss": "0.0.0",
"grunt-sass": "0.0.0",
"grunt-svg-sprite": "0.0.0",
"load-grunt-config": "0.0.0",
"postcss-banner": "0.0.0"
"autoprefixer": "",
"babel-preset-es2015": "",
"babel-plugin-transform-es2015-classes": "",
"babel-plugin-transform-export-extensions": "",
"babelify": "",
"eslint-tap": "",
"glob": "",
"grunt": "",
"grunt-browser-sync": "",
"grunt-browserify": "",
"grunt-contrib-concat": "",
"grunt-contrib-cssmin": "",
"grunt-contrib-uglify": "",
"grunt-contrib-watch": "",
"grunt-csscomb": "",
"grunt-eslint": "",
"grunt-notify": "",
"grunt-phplint": "",
"grunt-postcss": "",
"grunt-sass": "",
"grunt-svg-sprite": "",
"load-grunt-config": "",
"postcss-banner": ""
}
}

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@@ -15,11 +15,11 @@
<link rel="stylesheet" href="assets/styles/main.css">
</head>
<body>
<div data-module="Title">
<h1 class="js-label">Locomotive's Boilerplate</h1>
</div>
<div id="js-barba-wrapper">
<div class="js-barba-container" data-template="home">
<button data-module="Button" type="button" value="Clicked! Title destroyed">Change value and destroy Title()</button>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
<script>window.jQuery || document.write('<script src="assets/scripts/jquery-3.0.0.min.js"><\/script>')</script>