social-likes-nojq/gh-pages/tamia/tamia/tamia.js

364 lines
9.8 KiB
JavaScript
Raw Normal View History

// Tâmia © 2013 Artem Sapegin http://sapegin.me
// https://github.com/sapegin/tamia
// JS core
// jQuery and Modernizr arent required but very useful
/*jshint newcap:false*/
/*global DEBUG:true, Modernizr:false, console:false*/
/**
* Debug mode.
*
* You can use DEBUG global variable in your scripts to hide some code from minified production version of JavaScript.
*
* To make it work add to your Gruntfile:
*
* uglify: {
* options: {
* compress: {
* global_defs: {
* DEBUG: !!grunt.option('debug')
* }
* }
* },
* ...
* }
*
* Then if you run `grunt --debug` DEBUG variable will be true and false if you run just `grunt`.
*/
if (typeof window.DEBUG === 'undefined') window.DEBUG = true;
;(function(window, jQuery, Modernizr, undefined) {
'use strict';
// IE8+
if (!document.querySelectorAll) return;
// Namespace
var tamia = window.tamia = {};
if (DEBUG) {
// Debug logger
var addBadge = function(args, name) {
// Color console badge
// Based on https://github.com/jbail/lumberjack
var ua = navigator.userAgent.toLowerCase();
if (ua.indexOf('chrome') !== -1 || ua.indexOf('firefox') !== -1) {
var format = '%c %s %c ' + args.shift();
args.unshift(format, 'background:#aa759f; color:#fff', name, 'background:inherit; color:inherit');
}
else {
args[0] = name + ': ' + args[0];
}
return args;
};
var logger = function() {
var args = Array.prototype.slice.call(arguments);
var func = args.shift();
console[func].apply(console, addBadge(args, 'Tâmia'));
};
var log = tamia.log = logger.bind(null, 'log');
var warn = tamia.warn = logger.bind(null, 'warn');
// Check optional dependencies
if (!jQuery) warn('jQuery not found.');
if (!jQuery.Transitions) warn('jQuery Transition Events plugin (tamia/vendor/transition-events.js) not found.');
if (!Modernizr) warn('Modernizr not found.');
// Check required Modernizr features
$.each([
'csstransitions',
'cssgradients',
'flexbox',
'touch',
], function(idx, feature) {
if (!(feature in Modernizr)) warn('Modernizr should be built with "' + feature + '" feautre.');
});
}
var _containersCache;
var _components = {};
var _initializedAttribute = '_tamia-yep';
function _getContainers(parent) {
return (parent || document).querySelectorAll('[data-component]');
}
/**
* Initialize components.
*
* @param {Object} components Initializers for each component.
*
* Examples:
*
* <div data-component="pony"></div>
*
* tamia.initComponents({
* // New style component
* pony: Pony, // class Pony extends Component {...}
* // Plain initializer
* pony: function(elem) {
* // $(elem) === <div data-component="pony">
* },
* // Initialize jQuery plugins (plain initializer)
* jquerypony: function(elem) {
* $(elem).pluginmethod({option1: 'val1', options2: 'val2'});
* $(elem).pluginmethod2();
* },
* // Initialize jQuery plugins (shortcut)
* jquerypony: {
* pluginmethod: {option1: 'val1', options2: 'val2'},
* pluginmethod2: ['attr1', 'attr2', 'attr3'],
* pluginmethod3: null
* }
* }
*
* Caveats:
*
* 1. To initialize components inside container that was hidden or inside dynamically created container use
* init.tamia event: `$('.js-container').trigger('init.tamia');`
* 2. No components will be initialized twice. Its safe to trigger init.tamia event multiple times: only new nodes
* or nodes that was hidden before will be affected.
*/
tamia.initComponents = function(components, parent) {
var containers;
if (parent === undefined) {
containers = _containersCache || (_containersCache = _getContainers());
}
else {
// Init all components inside DOM node
containers = _getContainers(parent);
components = _components;
}
// Init components
for (var containerIdx = 0, containerCnt = containers.length; containerIdx < containerCnt; containerIdx++) {
var container = containers[containerIdx];
var componentName = container.getAttribute('data-component');
var component = components[componentName];
if (!component || container.hasAttribute(_initializedAttribute)) continue;
var initialized = true;
if ('__tamia_cmpnt__' in component) {
// New style component
initialized = (new component(container)).initializable;
}
else if (typeof component === 'function') {
// Old style component
initialized = component(container);
}
else if (jQuery) {
// jQuery plugins shortcut
for (var method in component) {
var params = component[method];
var elem = jQuery(container);
if (DEBUG && !jQuery.isFunction(elem[method])) warn('jQuery method "%s" not found (used in "%s" component).', method, componentName);
if (jQuery.isArray(params)) {
elem[method].apply(elem, params);
}
else {
elem[method](params);
}
}
}
if (initialized !== false) {
container.setAttribute(_initializedAttribute, 'yes');
}
}
// Add new components to all components array
for (var name in components) {
_components[name] = components[name];
}
};
if (jQuery) {
var _doc = jQuery(document);
var _hiddenClass = 'is-hidden';
var _transitionClass = 'is-transit';
var _appearedEvent = 'appeared.tamia';
var _disappearedEvent = 'disappeared.tamia';
var _fallbackTimeout = 1000;
/**
* Registers Tâmia events (eventname.tamia) on document.
*
* Example:
*
* // Registers enable.tamia event.
* tamia.registerEvents({
* enable: function(elem) {
* }
* });
*
* @param {Object} handlers Handlers list.
*/
tamia.registerEvents = function(handlers) {
var events = $.map(handlers, _tamiaze).join(' ');
_doc.on(events, function(event) {
if (DEBUG) log('Event "%s":', event.type, event.target);
handlers[event.type](event.target);
});
};
var _tamiaze = function (handler, name) {
return name + '.tamia';
};
/**
* Events
*/
var _handlers = {};
/**
* Init components inside any jQuery node.
*
* Examples:
*
* $(document).trigger('init.tamia');
* $('.js-container').trigger('init.tamia');
*/
_handlers.init = function(elem) {
tamia.initComponents(undefined, elem);
};
/**
* Show element with CSS transition.
*
* appeared.tamia event will be fired the moment transition ends.
*
* Example:
*
* .dialog
* transition: opacity .5s ease-in-out
* ...
* &.is-hidden
* opacity: 0
*
* <div class="dialog is-hidden js-dialog">...</div>
*
* $('.js-dialog').trigger('appear.tamia');
*/
_handlers.appear = function(elem) {
elem = $(elem);
if (Modernizr && Modernizr.csstransitions) {
if (elem.hasClass(_transitionClass) && !elem.hasClass(_hiddenClass)) return;
elem.addClass(_transitionClass);
setTimeout(function() {
elem.removeClass(_hiddenClass);
elem.afterTransition(function() {
elem.removeClass(_transitionClass);
elem.trigger(_appearedEvent);
});
}, 0);
}
else {
elem.removeClass(_hiddenClass);
elem.trigger(_appearedEvent);
}
};
/**
* Hide element with CSS transition.
*
* disappeared.tamia event will be fired the moment transition ends.
*
* Opposite of `appear.tamia` event.
*/
_handlers.disappear = function(elem) {
elem = $(elem);
if (Modernizr && Modernizr.csstransitions) {
if (elem.hasClass(_transitionClass) && elem.hasClass(_hiddenClass)) return;
elem.addClass(_transitionClass);
elem.addClass(_hiddenClass);
elem.afterTransition(function() {
elem.removeClass(_transitionClass);
elem.trigger(_disappearedEvent);
});
}
else {
elem.addClass(_hiddenClass);
elem.trigger(_disappearedEvent);
}
};
/**
* Toggles elements visibility with CSS transition.
*
* See `appear.tamia` event for details.
*/
_handlers.toggle = function(elem) {
elem = $(elem);
if (elem.hasClass(_hiddenClass)) {
_handlers.appear(elem);
}
else {
_handlers.disappear(elem);
}
};
tamia.registerEvents(_handlers);
/**
* Controls.
*
* Fires jQuery event to specified element on click at this element.
*
* @param data-fire Event name.
* @param [data-target] Target element selector.
* @param [data-closest] Target element selector: search only through element ancestors.
* @param [data-attrs] Comma separated attributes list.
*
* Either of data-target or data-closest is required.
*
* Example:
*
* <span data-fire="slider-next" data-target=".portfolio" data-attrs="1,2,3">Next</span>
* <!-- $('.portfolio').trigger('slider-next', [1, 2, 3]); -->
*/
_doc.on('click', '[data-fire]', function(event) {
var elem = jQuery(event.currentTarget);
var data = elem.data();
if (DEBUG) if (!data.target && !data.closest) return log('You should define either data-target or data-closest on', elem[0]);
var target = data.target && jQuery(data.target) || elem.closest(data.closest);
if (DEBUG) if (!target.length) return log('Target element %s not found for', data.target || data.closest, elem[0]);
var attrs = data.attrs;
if (DEBUG) log('Fire "%s" with attrs [%s] on', data.fire, attrs || '', target);
target.trigger(data.fire, attrs ? attrs.split(/[;, ]/) : undefined);
event.preventDefault();
});
/**
* Grid helper.
*
* Example:
*
* <div data-component="grid"></div>
*/
if (DEBUG) tamia.initComponents({
grid: function(elem) {
elem = $(elem);
elem
.addClass('g-row')
.html(
new Array((elem.data('columns') || 12) + 1).join('<b class="g-debug-col" style="height:'+document.documentElement.scrollHeight+'px"></b>')
)
;
}
});
}
}(window, window.jQuery, window.Modernizr));