Skip to content

Instantly share code, notes, and snippets.

@CrossEye
Created October 22, 2013 01:55
Show Gist options
  • Save CrossEye/7094043 to your computer and use it in GitHub Desktop.
Save CrossEye/7094043 to your computer and use it in GitHub Desktop.
(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");
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