Created
October 22, 2013 01:55
-
-
Save CrossEye/7094043 to your computer and use it in GitHub Desktop.
Another reworking of the code from @AutoSponge's article at http://autosponge.github.io/blog/2013/10/08/flow-toward-maintainability/
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function() { | |
var split = {}, intervalData, | |
timeComponent = { | |
seconds: function (split) {return split.seconds * 6;}, | |
minutes: function (split) {return (split.minutes + (split.seconds / 60)) * 6;}, | |
hours: function (split) {return (split.hours % 12 * 30 ) + split.minutes / 2;} | |
}; | |
var SVG = (function() { | |
var SVG = function(config) { | |
var prop; | |
this.elm = document.createElementNS("http://www.w3.org/2000/svg", config.type); | |
this.elm.setAttribute("class", config.class); | |
if (config.textContent ) { | |
this.elm.appendChild(document.createTextNode(config.textContent)); | |
} | |
for (prop in (config.data || {})) { | |
if (config.data.hasOwnProperty(prop) ) { | |
this.elm.setAttribute(prop, config.data[prop]); | |
} | |
} | |
var layer = SVG.layers[config.layer]; | |
if (!layer) { | |
SVG.layers[config.layer] = this.elm; | |
layer = SVG.layers[config.layer - 1]; | |
} | |
layer.appendChild(this.elm); | |
}; | |
SVG.prototype.rotate = function (degrees, transition) { | |
var origin = "transform-origin: 100 100;", | |
transform = "transform:rotate(" + degrees + "deg);", | |
style = origin + transform + | |
"-ms-" + origin + | |
"-ms-" + transform + | |
"-webkit-" + origin + | |
"-webkit-" + transform; | |
if (transition === false) { | |
style += "transition: none;"; | |
} | |
this.elm.setAttribute("style", style); | |
return this; | |
}; | |
SVG.layers = [document.getElementsByTagName("body")[0]]; | |
return SVG; | |
}()); | |
Emitter.on("start", function() { | |
Emitter.emit("init"); | |
Emitter.emit("animate", true); | |
}); | |
Emitter.on("init", function() { | |
[ | |
{type: "svg", layer: 1, class: "container", data: { | |
version: "1.2", baseProfile: "tiny", viewBox: "0 0 200 200", "enable-background": "0 0 200 200"}}, | |
{type: "circle", layer: 2, class: "clock", data: { | |
cx: 100, cy: 100, r: 90}}, | |
{type: "text", layer: 1, class: "text", textContent: "12", data: { | |
x: 92, y: 30}}, | |
{type: "text", layer: 1, class: "text", textContent: "3", data: { | |
x: 175, y: 106}}, | |
{type: "text", layer: 1, class: "text", textContent: "6", data: { | |
x: 96, y: 180}}, | |
{type: "text", layer: 1, class: "text", textContent: "9", data: { | |
x: 18, y: 106}}, | |
{type: "line", layer: 1, class: "hours", data: { | |
x1: 100, y1: 45, x2: 100, y2: 100}}, | |
{type: "line", layer: 1, class: "minutes", data: { | |
x1: 100, y1: 25, x2: 100, y2: 100}}, | |
{type: "line", layer: 1, class: "seconds", data: { | |
x1: 100, y1: 15, x2: 100, y2: 100}}, | |
{type: "circle", layer: 1, class: "pin", data: { | |
cx: 100, cy: 100, r: 2}} | |
].forEach(function(element) { | |
Emitter.emit("create", element); | |
}); | |
}); | |
Emitter.on("create", (function() { | |
return function(element) { | |
// TODO: Is the SVG constructor really the best object for a cache? | |
SVG[element["class"]] = new SVG(element); | |
}; | |
}())); | |
Emitter.on("animate", function (bool) { | |
intervalData = bool ? setInterval( function() { | |
Emitter.emit( "createDate", new Date() ); | |
}, 1000) : clearInterval( intervalData ); | |
}); | |
Emitter.on("createDate", function (date) { | |
Emitter.emit("split/seconds", date.getSeconds()); | |
Emitter.emit("split/minutes", date.getMinutes()); | |
Emitter.emit("split/hours", date.getHours()); | |
}); | |
Emitter.on("split/*", function(number, topic) { | |
var segment = topic.split("/")[1]; | |
if (split[segment] !== number ) { | |
split[segment] = number; | |
Emitter.emit( "draw/" + segment, timeComponent[segment](split)); | |
} | |
}); | |
Emitter.on("draw/*", function(degrees, topic) { | |
var segment = topic.split("/")[1]; | |
var hand = SVG[segment]; | |
if (degrees === 0 && hand.rotated) { | |
hand.rotate(359.9); | |
return setTimeout(function () { | |
hand.rotate(0, false); | |
}, 816); | |
} | |
if (!hand.rotated) { | |
hand.rotated = true; | |
return hand.rotate(degrees, false); | |
} | |
return hand.rotate(degrees); | |
}); | |
}()); | |
Emitter.emit("start"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var Emitter = (function() { | |
var global = this; | |
var sorter = function(first, second) { | |
return second.priority - first.priority; | |
}; | |
var each = function(fn, arr) { | |
for (var i = 0, len = arr.length; i < len; i++) {fn(arr[i], i);} | |
}; | |
var map = function(fn, list) { | |
var idx = -1, len = list.length, result = new Array(len); | |
while (++idx < len) {result[idx] = fn(list[idx]);} | |
return result; | |
}; | |
var reduce = function(fn, acc, list) { | |
var idx = -1, len = list.length; | |
while(++idx < len) {acc = fn(acc, list[idx]);} | |
return acc; | |
}; | |
var objKeys = Object.keys || function(obj) { | |
var prop, ks = []; | |
for (prop in obj) {if (obj.hasOwnProperty(prop)) { | |
ks.push(prop); | |
}} | |
return ks; | |
}; | |
var asyncSubCall = function(sub, delay, data, topic, onrData) { | |
setTimeout(function() { | |
sub.fn.call(sub.context, data, topic, onrData); | |
}, delay); | |
}; | |
var persistTopics = function(pubs, persist, data, onrData) { | |
var config = {data: data, onrData: onrData}; | |
each(function(topic) {(pubs[topic] || (pubs[topic] = [])).push(config);}, persist); | |
}; | |
var getTopics = (function() { | |
var getBasics = function(count) { | |
if (count === 1) { | |
return ["0", "*", "**"]; | |
} | |
return reduce (function(acc, base) { | |
acc.push(base + "/" + (count - 1)); | |
acc.push(base + "/*"); | |
acc.push(base + "/**"); | |
return acc; | |
}, [], getBasics(count - 1)); | |
}; | |
var addPartials = function(topic, obj) { | |
var index = topic.indexOf("*/*"), newTopic; | |
while (index > -1) { | |
newTopic = (topic.substring(0, index) + "**" + topic.substring(index + 2)).replace(/\*\*+/g, "**"); | |
obj[newTopic] = 1; | |
addPartials(newTopic, obj); | |
index = topic.indexOf("/*", index + 1); | |
} | |
}; | |
var buildMatches = function(count) { | |
var keys = {}, results; | |
each(function(topic) { | |
keys[topic] = 1; | |
addPartials(topic, keys); | |
}, getBasics(count)); | |
results = objKeys(keys); | |
return results; | |
}; | |
var cache = {}; | |
var getTopicFunction = function(count) { | |
return new Function("subtopics", " return [\n " + map(function(str) { | |
return '"' + str.replace(/\d+/g, function(digits) {return '"' + ' + subtopics[' + digits + '] + "';}) + '"'; | |
}, buildMatches(count)).join(",\n ") + "\n ];"); | |
}; | |
return function(topic) { | |
var subtopics = topic.split("/"), count = subtopics.length; | |
return (cache[count] || (cache[count] = getTopicFunction(count)))(subtopics); | |
}; | |
}()); | |
var createEmitter = function() { | |
var allSubscriptions = [], topicSubscriptions = {}, persistedPublications = {}; | |
return { | |
create: createEmitter, | |
clear: function() { | |
allSubscriptions = []; | |
topicSubscriptions = {}; | |
onrsToAll = []; | |
}, | |
on: function(topic, opts, fn) { | |
if (!topic) {return;} | |
if (!fn) { | |
fn = opts; | |
opts = null; | |
} | |
var id = allSubscriptions.length, subscription = { | |
fn: fn, | |
id: id, | |
status: "active", | |
async: (opts && opts.async) || false, | |
priority: (opts && opts.priority) || 0, | |
context: (opts && opts.context) || global | |
}; | |
allSubscriptions.push(subscription); | |
allSubscriptions.push(subscription); | |
(topicSubscriptions[topic] || (topicSubscriptions[topic] = [])).push(subscription); | |
if (!(opts && opts.persist === false) &&persistedPublications[topic]) { | |
for (var i = 0; i < persistedPublications[topic].length; i++) { | |
var config = persistedPublications[topic][i]; | |
fn.call(subscription.context, config.data, topic, config.onrData); | |
} | |
} | |
return id; | |
}, | |
emit: function(topic, data, opts) { | |
var subscriptions, onrData = {}, response, allTopics = getTopics(topic), | |
persist = opts && opts.persist && allTopics; | |
subscriptions = reduce(function(soFar, nextTopic) { | |
each(function(sub) {soFar.push(sub);}, topicSubscriptions[nextTopic] || []); | |
return soFar; | |
}, [], getTopics(topic)); | |
subscriptions.sort(sorter); | |
each(function(subscription, idx) { | |
if (subscription.status === "active" && (response !== false || (opts && opts.cancelable === false))) { | |
if ((opts && opts.async) || subscription.async) { | |
asyncSubCall(subscription, idx, data, topic, onrData); | |
} else { | |
response = subscription.fn.call(subscription.context, data, topic, onrData); | |
} | |
} | |
}, subscriptions); | |
if (persist) { | |
persistTopics(persistedPublications, persist, data, onrData); | |
} | |
}, | |
off: function(subId, permanent) { | |
if (typeof subId === "number" && allSubscriptions[subId]) { | |
allSubscriptions[subId].status = permanent ? "removed" : "inactive"; | |
} | |
}, | |
onAgain: function(subId) { | |
if (typeof subId === "number" && allSubscriptions[subId] && allSubscriptions[subId].status !== "removed") { | |
allSubscriptions[subId].status = "active"; | |
} | |
} | |
}; | |
}; | |
return createEmitter(); | |
}()); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment