window.Essential = {
rootElement: document,
Core: {},
EssentialJS v0.5.0
Copyright (c)2014 Roberto Dip
Distributed under MIT license
http://roperzh.github.io/essential.js
window.Essential = {
rootElement: document,
Core: {},
since v0.1.0
A wrapper of #Essential.loadBehaviors
, this method is deprecated
direct usage of loadBehaviors
is encouraged.
Param application[Object
] an object containing behaviors names as a key
and behaviors objects as a value.
start: function(application) {
this.loadBehaviors({
application: application
});
},
since v0.5.0
Wakes up the engine, searching and attaching behaviors with their proper elements
Param options[Object
] allows the follwing values:
application
[Object
] an object containing behaviors names as a key
and behaviors objects as a valuecontext
[DOMElement
] context to look for behaviors.
If no context is provided the default is Essential.rootElement
Example
MyApp = {};
MyApp.Carousel = Essential.Behaviors.extend();
Essential.loadBehaviors({ application: MyApp, context: document });
// will attach the carousel behavior to proper elements
loadBehaviors: function(options) {
options.context = options.context || this.rootElement;
var initializedBehaviors =
this.initializeBehaviors(options.application, options.context);
this.launchBehaviors(initializedBehaviors);
},
Crawls an element looking for behaviors and call #new
on every behavior
found with lateStart = true
, so the behaviors are initialized, but
there is no event delegation
param application [Object
] object containing behaviors to be initialized
param element [DomeElement
] context to look for declared behaviors
initializeBehaviors: function(application, element) {
var behaviorsInDOM = this.Core.crawl(element),
rawBehaviorsNames = Object.keys(behaviorsInDOM),
initializedBehaviors = [],
i = -1;
while(rawBehaviorsNames[++i]) {
var rawName = rawBehaviorsNames[i],
name = this.Core.camelize(rawName),
behavior = application[name];
if(typeof behavior !== "undefined") {
var elementsWithBehavior = behaviorsInDOM[rawName],
j = -1;
while(elementsWithBehavior[++j]) {
var initializedBehavior = behavior.new(elementsWithBehavior[j], true);
initializedBehaviors.push(initializedBehavior);
}
}
}
return initializedBehaviors;
},
Given a list of behaviors, this method sort these based on their
priority
value, and then call #start
on every one
param behaviorList[Array<Object>
] an array containing behaviors already
initialized
launchBehaviors: function(behaviorList) {
var sortedBehaviors = behaviorList.sort(this.Core.SortMethods.byPriority),
i = -1;
while(sortedBehaviors[++i]) {
sortedBehaviors[i].start();
}
}
};
Represents a behavior of some element or group of elements. The objetive is define a set of rules and events which can be associated to an element and reutilized later on
When a behavior is defined, a hash of events must be defined too, and on initialization a DOM element must be provided
Also you can define an init
function, which is always called when the
behavior is initialized
Example
Carousel = Essential.Behavior.extend({
events: {
"click .next": "goToNextSlide"
},
init: function() {
// Called on behavior initialization
},
goToNextSlide: function(e) {
//...
}
});
var carousel = Carousel.new(domElement);
Essential.Behavior = Proto.extend({
constructor: function(domElement, lateStart) {
this.el = domElement;
A behavior can be initialized without attaching events with the lateStart
flag, if it is present the methods delegateEvents
and ìnit
are omitted
but can be called later with start
Example
carousel = new Carousel(domElement, true);
// delegateEvents and init not called
carousel.start();
// delegateEvents and init called
if(!lateStart) {
this.start();
}
},
start: function() {
this.delegateEvents();
this.listenChannels();
if(typeof this.init === "function") {
this.init();
}
},
delegateEvents: function() {
Essential.Core.mapEvents.call(this, this.events, this.el);
},
since v0.5.0
Attach event handlers to channels declared in this.channels using
document` as a context
listenChannels: function() {
Essential.Core.mapEvents.call(this, this.channels, document);
},
Facilitates the emission of custom events through the CustomEvent Interface. IE9 and IE10 are supported via polyfill
since v0.5.0
param dataset[Object
] valid dataset values are:
channel: [String
] name (identifier) of the channel
context: [DOMElement
] DOM context in which the event is triggered,
this parameter can be ommited. Default value is document
bubles: [Boolean
] defines if this event should bubble or not,
defaults to true
cancelable: [Boolean
] indecates whether the event is cancelable,
defaults to false
data: [Object
] data to be included in the "detail"
key of the
event can be accesed later via event.detail
(check the CustomEvent spec for more info)
emit: function(dataset) {
dataset.context = dataset.context || this.el;
dataset.data = dataset.data || {};
dataset.bubbles = dataset.bubbles || true;
dataset.cancelable = dataset.cancelable || false;
var customEvent = new CustomEvent(dataset.channel, {
"bubbles": dataset.bubbles,
"cancelable": dataset.cancelable,
"detail": dataset.data
});
dataset.context.dispatchEvent(customEvent);
},
priority: 0
});
since v0.5.0
Given a document context, maps a hash of events to all ocurrences in the context using the DOM Event Interface
param events[Object
] key-value map which follows some conventions:
key: must be a String, containing the event name. Optionally after the event
name a valid CSS selector must be placed, for example "click #element"
value: must be a name of a funciton pertaining to the current in which
mapEvents
its executed
param context[DOMElement
] element to search through
Example
var events = {
"click .next": "goToNextSlide"
};
Essential.Core.mapEvents(events, document);
Essential.Core.mapEvents = function(events, context) {
if(typeof events === "undefined") {
return;
}
var delegateEventSplitter = /^(\S+)\s*(.*)$/;
for(var key in events) {
var method = events[key];
var match = key.match(delegateEventSplitter);
var eventName = match[1],
selector = match[2],
nodeList = selector ? context.querySelectorAll(selector) : [context];
if(typeof this[method] === "undefined") {
continue;
}
Essential.Core.bind(eventName, nodeList, this[method].bind(this));
}
};
Binds an event to a node
Param eventName[String
] name of the event to be binded
Param callback[Function
] function to be called when the event is triggered
Param nodeList[NodeList
, Array
] node elements to be binded
Example
var nodeList = document.querySelectorAll("*");
Essential.Core.bind("hover", nodeList, function() {
alert("hover!");
});
// If the hover event is triggered for any of the
// elements in the nodeList the alert will appear
Essential.Core.bind = function(eventName, nodeList, callback) {
var i = -1;
while(nodeList[++i]) {
var currentElement = nodeList[i];
if(currentElement.addEventListener) {
nodeList[i].addEventListener(eventName, callback);
} else {
currentElement.attachEvent("on" + eventName, callback);
}
}
};
Looks for some of this characters :
-
_
the objetive is allow
to define behaviors like cool:carousel
or small-carousel
Essential.Core.SPECIAL_CHARS_REGEXP = /([\:\-\_]+(.))/g;
Finds the first letter of a given string
Essential.Core.FIRST_LETTER_REGEXP = /^[a-z]/g;
Converts strings to UpperCamelCase
Param name[String
] the name to be camelized
Returns String
camel case representation of the name
Example
Essential.Core.camelize("cool-carousel");
// => CoolCarousel
Essential.Core.camelize = function(name) {
return name.
replace(Essential.Core.FIRST_LETTER_REGEXP, function(letter) {
return letter.toUpperCase();
}).
replace(Essential.Core.SPECIAL_CHARS_REGEXP, function(_, separator, letter) {
return letter.toUpperCase();
});
};
Scans the DOM looking for behaviors
Return Array<Object>
an array of objects with the behavior name as
a key and an array of DOM nodes as a value
Example
<div behavior="carousel"></div>
Essential.Core.crawl();
// => [{ carousel: [<HTMLDivElement>, <HTMLDivElement>] }]
Essential.Core.crawl = function(rootElement) {
var all = rootElement.querySelectorAll("[data-behavior], [behavior]"),
i = -1,
result = {};
while(all[++i]) {
var currentElement = all[i],
rawBehaviors = currentElement.getAttribute("data-behavior") || currentElement.getAttribute("behavior"),
behaviorsList = rawBehaviors.split(" "),
j = -1;
while(behaviorsList[++j]) {
var currentBehavior = behaviorsList[j];
if(result[currentBehavior]) {
result[currentBehavior].push(currentElement);
} else {
result[currentBehavior] = [currentElement];
}
}
}
return result;
};
Namespace to hold sort methods
Essential.Core.SortMethods = {
This criteria allows to sort behaviors based on their respective priorities, in descending order, that means behaviors with bigger priority will appear first
byPriority: function(behaviorA, behaviorB) {
return behaviorB.priority - behaviorA.priority;
}
};