mirror of
https://github.com/hyperion-project/hyperion.ng.git
synced 2023-10-10 13:36:59 +02:00
1ff8528597
* Initial WebUI with sample functions * Changed folder structure * Light Reset Button and Translation fixing in Links * Indentation fixed * Reorganized menu and new function for setting effects * Styling fix
595 lines
18 KiB
JavaScript
595 lines
18 KiB
JavaScript
//
|
|
// ____ _ _
|
|
// / ___|| |_ __ _ _ __ ___ ___ (_)___ (*)
|
|
// \___ \| __/ _` | '_ \ / _ \/ __| | / __|
|
|
// ___) | || (_| | |_) | __/\__ \_ | \__ \
|
|
// |____/ \__\__,_| .__/ \___||___(_)/ |___/
|
|
// |_| |__/
|
|
//
|
|
// (*) a (really) tiny Javascript MVC microframework
|
|
//
|
|
// (c) Hay Kranen < hay@bykr.org >
|
|
// Released under the terms of the MIT license
|
|
// < http://en.wikipedia.org/wiki/MIT_License >
|
|
//
|
|
// Stapes.js : http://hay.github.com/stapes
|
|
(function() {
|
|
'use strict';
|
|
|
|
var VERSION = "0.8.0";
|
|
|
|
// Global counter for all events in all modules (including mixed in objects)
|
|
var guid = 1;
|
|
|
|
// Makes _.create() faster
|
|
if (!Object.create) {
|
|
var CachedFunction = function(){};
|
|
}
|
|
|
|
// So we can use slice.call for arguments later on
|
|
var slice = Array.prototype.slice;
|
|
|
|
// Private attributes and helper functions, stored in an object so they
|
|
// are overwritable by plugins
|
|
var _ = {
|
|
// Properties
|
|
attributes : {},
|
|
|
|
eventHandlers : {
|
|
"-1" : {} // '-1' is used for the global event handling
|
|
},
|
|
|
|
guid : -1,
|
|
|
|
// Methods
|
|
addEvent : function(event) {
|
|
// If we don't have any handlers for this type of event, add a new
|
|
// array we can use to push new handlers
|
|
if (!_.eventHandlers[event.guid][event.type]) {
|
|
_.eventHandlers[event.guid][event.type] = [];
|
|
}
|
|
|
|
// Push an event object
|
|
_.eventHandlers[event.guid][event.type].push({
|
|
"guid" : event.guid,
|
|
"handler" : event.handler,
|
|
"scope" : event.scope,
|
|
"type" : event.type
|
|
});
|
|
},
|
|
|
|
addEventHandler : function(argTypeOrMap, argHandlerOrScope, argScope) {
|
|
var eventMap = {},
|
|
scope;
|
|
|
|
if (typeof argTypeOrMap === "string") {
|
|
scope = argScope || false;
|
|
eventMap[ argTypeOrMap ] = argHandlerOrScope;
|
|
} else {
|
|
scope = argHandlerOrScope || false;
|
|
eventMap = argTypeOrMap;
|
|
}
|
|
|
|
for (var eventString in eventMap) {
|
|
var handler = eventMap[eventString];
|
|
var events = eventString.split(" ");
|
|
|
|
for (var i = 0, l = events.length; i < l; i++) {
|
|
var eventType = events[i];
|
|
_.addEvent.call(this, {
|
|
"guid" : this._guid || this._.guid,
|
|
"handler" : handler,
|
|
"scope" : scope,
|
|
"type" : eventType
|
|
});
|
|
}
|
|
}
|
|
},
|
|
|
|
addGuid : function(object, forceGuid) {
|
|
if (object._guid && !forceGuid) return;
|
|
|
|
object._guid = guid++;
|
|
|
|
_.attributes[object._guid] = {};
|
|
_.eventHandlers[object._guid] = {};
|
|
},
|
|
|
|
// This is a really small utility function to save typing and produce
|
|
// better optimized code
|
|
attr : function(guid) {
|
|
return _.attributes[guid];
|
|
},
|
|
|
|
clone : function(obj) {
|
|
var type = _.typeOf(obj);
|
|
|
|
if (type === 'object') {
|
|
return _.extend({}, obj);
|
|
}
|
|
|
|
if (type === 'array') {
|
|
return obj.slice(0);
|
|
}
|
|
},
|
|
|
|
create : function(proto) {
|
|
if (Object.create) {
|
|
return Object.create(proto);
|
|
} else {
|
|
CachedFunction.prototype = proto;
|
|
return new CachedFunction();
|
|
}
|
|
},
|
|
|
|
createSubclass : function(props, includeEvents) {
|
|
props = props || {};
|
|
includeEvents = includeEvents || false;
|
|
|
|
var superclass = props.superclass.prototype;
|
|
|
|
// Objects always have a constructor, so we need to be sure this is
|
|
// a property instead of something from the prototype
|
|
var realConstructor = props.hasOwnProperty('constructor') ? props.constructor : function(){};
|
|
|
|
function constructor() {
|
|
// Be kind to people forgetting new
|
|
if (!(this instanceof constructor)) {
|
|
throw new Error("Please use 'new' when initializing Stapes classes");
|
|
}
|
|
|
|
// If this class has events add a GUID as well
|
|
if (this.on) {
|
|
_.addGuid( this, true );
|
|
}
|
|
|
|
realConstructor.apply(this, arguments);
|
|
}
|
|
|
|
if (includeEvents) {
|
|
_.extend(superclass, Events);
|
|
}
|
|
|
|
constructor.prototype = _.create(superclass);
|
|
constructor.prototype.constructor = constructor;
|
|
|
|
_.extend(constructor, {
|
|
extend : function() {
|
|
return _.extendThis.apply(this, arguments);
|
|
},
|
|
|
|
// We can't call this 'super' because that's a reserved keyword
|
|
// and fails in IE8
|
|
'parent' : superclass,
|
|
|
|
proto : function() {
|
|
return _.extendThis.apply(this.prototype, arguments);
|
|
},
|
|
|
|
subclass : function(obj) {
|
|
obj = obj || {};
|
|
obj.superclass = this;
|
|
return _.createSubclass(obj);
|
|
}
|
|
});
|
|
|
|
// Copy all props given in the definition to the prototype
|
|
for (var key in props) {
|
|
if (key !== 'constructor' && key !== 'superclass') {
|
|
constructor.prototype[key] = props[key];
|
|
}
|
|
}
|
|
|
|
return constructor;
|
|
},
|
|
|
|
emitEvents : function(type, data, explicitType, explicitGuid) {
|
|
explicitType = explicitType || false;
|
|
explicitGuid = explicitGuid || this._guid;
|
|
|
|
// #30: make a local copy of handlers to prevent problems with
|
|
// unbinding the event while unwinding the loop
|
|
var handlers = slice.call(_.eventHandlers[explicitGuid][type]);
|
|
|
|
for (var i = 0, l = handlers.length; i < l; i++) {
|
|
// Clone the event to prevent issue #19
|
|
var event = _.extend({}, handlers[i]);
|
|
var scope = (event.scope) ? event.scope : this;
|
|
|
|
if (explicitType) {
|
|
event.type = explicitType;
|
|
}
|
|
|
|
event.scope = scope;
|
|
event.handler.call(event.scope, data, event);
|
|
}
|
|
},
|
|
|
|
// Extend an object with more objects
|
|
extend : function() {
|
|
var args = slice.call(arguments);
|
|
var object = args.shift();
|
|
|
|
for (var i = 0, l = args.length; i < l; i++) {
|
|
var props = args[i];
|
|
for (var key in props) {
|
|
object[key] = props[key];
|
|
}
|
|
}
|
|
|
|
return object;
|
|
},
|
|
|
|
// The same as extend, but uses the this value as the scope
|
|
extendThis : function() {
|
|
var args = slice.call(arguments);
|
|
args.unshift(this);
|
|
return _.extend.apply(this, args);
|
|
},
|
|
|
|
// from http://stackoverflow.com/a/2117523/152809
|
|
makeUuid : function() {
|
|
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
|
return v.toString(16);
|
|
});
|
|
},
|
|
|
|
removeAttribute : function(keys, silent) {
|
|
silent = silent || false;
|
|
|
|
// Split the key, maybe we want to remove more than one item
|
|
var attributes = _.trim(keys).split(" ");
|
|
|
|
// Actually delete the item
|
|
for (var i = 0, l = attributes.length; i < l; i++) {
|
|
var key = _.trim(attributes[i]);
|
|
|
|
if (key) {
|
|
delete _.attr(this._guid)[key];
|
|
|
|
// If 'silent' is set, do not throw any events
|
|
if (!silent) {
|
|
this.emit('change', key);
|
|
this.emit('change:' + key);
|
|
this.emit('remove', key);
|
|
this.emit('remove:' + key);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
removeEventHandler : function(type, handler) {
|
|
var handlers = _.eventHandlers[this._guid];
|
|
|
|
if (type && handler) {
|
|
// Remove a specific handler
|
|
handlers = handlers[type];
|
|
if (!handlers) return;
|
|
|
|
for (var i = 0, l = handlers.length, h; i < l; i++) {
|
|
h = handlers[i].handler;
|
|
if (h && h === handler) {
|
|
handlers.splice(i--, 1);
|
|
l--;
|
|
}
|
|
}
|
|
} else if (type) {
|
|
// Remove all handlers for a specific type
|
|
delete handlers[type];
|
|
} else {
|
|
// Remove all handlers for this module
|
|
_.eventHandlers[this._guid] = {};
|
|
}
|
|
},
|
|
|
|
setAttribute : function(key, value, silent) {
|
|
silent = silent || false;
|
|
|
|
// We need to do this before we actually add the item :)
|
|
var itemExists = this.has(key);
|
|
var oldValue = _.attr(this._guid)[key];
|
|
|
|
// Is the value different than the oldValue? If not, ignore this call
|
|
if (value === oldValue) {
|
|
return;
|
|
}
|
|
|
|
// Actually add the item to the attributes
|
|
_.attr(this._guid)[key] = value;
|
|
|
|
// If 'silent' flag is set, do not throw any events
|
|
if (silent) {
|
|
return;
|
|
}
|
|
|
|
// Throw a generic event
|
|
this.emit('change', key);
|
|
|
|
// And a namespaced event as well, NOTE that we pass value instead of
|
|
// key here!
|
|
this.emit('change:' + key, value);
|
|
|
|
// Throw namespaced and non-namespaced 'mutate' events as well with
|
|
// the old value data as well and some extra metadata such as the key
|
|
var mutateData = {
|
|
"key" : key,
|
|
"newValue" : value,
|
|
"oldValue" : oldValue || null
|
|
};
|
|
|
|
this.emit('mutate', mutateData);
|
|
this.emit('mutate:' + key, mutateData);
|
|
|
|
// Also throw a specific event for this type of set
|
|
var specificEvent = itemExists ? 'update' : 'create';
|
|
|
|
this.emit(specificEvent, key);
|
|
|
|
// And a namespaced event as well, NOTE that we pass value instead of key
|
|
this.emit(specificEvent + ':' + key, value);
|
|
},
|
|
|
|
trim : function(str) {
|
|
return str.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
|
|
},
|
|
|
|
typeOf : function(val) {
|
|
if (val === null || typeof val === "undefined") {
|
|
// This is a special exception for IE, in other browsers the
|
|
// method below works all the time
|
|
return String(val);
|
|
} else {
|
|
return Object.prototype.toString.call(val).replace(/\[object |\]/g, '').toLowerCase();
|
|
}
|
|
},
|
|
|
|
updateAttribute : function(key, fn, silent) {
|
|
var item = this.get(key);
|
|
|
|
// In previous versions of Stapes we didn't have the check for object,
|
|
// but still this worked. In 0.7.0 it suddenly doesn't work anymore and
|
|
// we need the check. Why? I have no clue.
|
|
var type = _.typeOf(item);
|
|
|
|
if (type === 'object' || type === 'array') {
|
|
item = _.clone(item);
|
|
}
|
|
|
|
var newValue = fn.call(this, item, key);
|
|
_.setAttribute.call(this, key, newValue, silent || false);
|
|
}
|
|
};
|
|
|
|
// Can be mixed in later using Stapes.mixinEvents(object);
|
|
var Events = {
|
|
emit : function(types, data) {
|
|
data = (typeof data === "undefined") ? null : data;
|
|
|
|
var splittedTypes = types.split(" ");
|
|
|
|
for (var i = 0, l = splittedTypes.length; i < l; i++) {
|
|
var type = splittedTypes[i];
|
|
|
|
// First 'all' type events: is there an 'all' handler in the
|
|
// global stack?
|
|
if (_.eventHandlers[-1].all) {
|
|
_.emitEvents.call(this, "all", data, type, -1);
|
|
}
|
|
|
|
// Catch all events for this type?
|
|
if (_.eventHandlers[-1][type]) {
|
|
_.emitEvents.call(this, type, data, type, -1);
|
|
}
|
|
|
|
if (typeof this._guid === 'number') {
|
|
// 'all' event for this specific module?
|
|
if (_.eventHandlers[this._guid].all) {
|
|
_.emitEvents.call(this, "all", data, type);
|
|
}
|
|
|
|
// Finally, normal events :)
|
|
if (_.eventHandlers[this._guid][type]) {
|
|
_.emitEvents.call(this, type, data);
|
|
}
|
|
}
|
|
}
|
|
},
|
|
|
|
off : function() {
|
|
_.removeEventHandler.apply(this, arguments);
|
|
},
|
|
|
|
on : function() {
|
|
_.addEventHandler.apply(this, arguments);
|
|
}
|
|
};
|
|
|
|
_.Module = function() {
|
|
|
|
};
|
|
|
|
_.Module.prototype = {
|
|
each : function(fn, ctx) {
|
|
var attr = _.attr(this._guid);
|
|
for (var key in attr) {
|
|
var value = attr[key];
|
|
fn.call(ctx || this, value, key);
|
|
}
|
|
},
|
|
|
|
extend : function() {
|
|
return _.extendThis.apply(this, arguments);
|
|
},
|
|
|
|
filter : function(fn) {
|
|
var filtered = [];
|
|
var attributes = _.attr(this._guid);
|
|
|
|
for (var key in attributes) {
|
|
if ( fn.call(this, attributes[key], key)) {
|
|
filtered.push( attributes[key] );
|
|
}
|
|
}
|
|
|
|
return filtered;
|
|
},
|
|
|
|
get : function(input) {
|
|
if (typeof input === "string") {
|
|
return this.has(input) ? _.attr(this._guid)[input] : null;
|
|
} else if (typeof input === "function") {
|
|
var items = this.filter(input);
|
|
return (items.length) ? items[0] : null;
|
|
}
|
|
},
|
|
|
|
getAll : function() {
|
|
return _.clone( _.attr(this._guid) );
|
|
},
|
|
|
|
getAllAsArray : function() {
|
|
var arr = [];
|
|
var attributes = _.attr(this._guid);
|
|
|
|
for (var key in attributes) {
|
|
var value = attributes[key];
|
|
|
|
if (_.typeOf(value) === "object" && !value.id) {
|
|
value.id = key;
|
|
}
|
|
|
|
arr.push(value);
|
|
}
|
|
|
|
return arr;
|
|
},
|
|
|
|
has : function(key) {
|
|
return (typeof _.attr(this._guid)[key] !== "undefined");
|
|
},
|
|
|
|
map : function(fn, ctx) {
|
|
var mapped = [];
|
|
this.each(function(value, key) {
|
|
mapped.push( fn.call(ctx || this, value, key) );
|
|
}, ctx || this);
|
|
return mapped;
|
|
},
|
|
|
|
// Akin to set(), but makes a unique id
|
|
push : function(input, silent) {
|
|
if (_.typeOf(input) === "array") {
|
|
for (var i = 0, l = input.length; i < l; i++) {
|
|
_.setAttribute.call(this, _.makeUuid(), input[i], silent || false);
|
|
}
|
|
} else {
|
|
_.setAttribute.call(this, _.makeUuid(), input, silent || false);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
remove : function(input, silent) {
|
|
if (typeof input === 'undefined') {
|
|
// With no arguments, remove deletes all attributes
|
|
_.attributes[this._guid] = {};
|
|
this.emit('change remove');
|
|
} else if (typeof input === "function") {
|
|
this.each(function(item, key) {
|
|
if (input(item)) {
|
|
_.removeAttribute.call(this, key, silent);
|
|
}
|
|
});
|
|
} else {
|
|
// nb: checking for exists happens in removeAttribute
|
|
_.removeAttribute.call(this, input, silent || false);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
set : function(objOrKey, valueOrSilent, silent) {
|
|
if (typeof objOrKey === "object") {
|
|
for (var key in objOrKey) {
|
|
_.setAttribute.call(this, key, objOrKey[key], valueOrSilent || false);
|
|
}
|
|
} else {
|
|
_.setAttribute.call(this, objOrKey, valueOrSilent, silent || false);
|
|
}
|
|
|
|
return this;
|
|
},
|
|
|
|
size : function() {
|
|
var size = 0;
|
|
var attr = _.attr(this._guid);
|
|
|
|
for (var key in attr) {
|
|
size++;
|
|
}
|
|
|
|
return size;
|
|
},
|
|
|
|
update : function(keyOrFn, fn, silent) {
|
|
if (typeof keyOrFn === "string") {
|
|
_.updateAttribute.call(this, keyOrFn, fn, silent || false);
|
|
} else if (typeof keyOrFn === "function") {
|
|
this.each(function(value, key) {
|
|
_.updateAttribute.call(this, key, keyOrFn);
|
|
});
|
|
}
|
|
|
|
return this;
|
|
}
|
|
};
|
|
|
|
var Stapes = {
|
|
"_" : _, // private helper functions and properties
|
|
|
|
"extend" : function() {
|
|
return _.extendThis.apply(_.Module.prototype, arguments);
|
|
},
|
|
|
|
"mixinEvents" : function(obj) {
|
|
obj = obj || {};
|
|
|
|
_.addGuid(obj);
|
|
|
|
return _.extend(obj, Events);
|
|
},
|
|
|
|
"on" : function() {
|
|
_.addEventHandler.apply(this, arguments);
|
|
},
|
|
|
|
"subclass" : function(obj, classOnly) {
|
|
classOnly = classOnly || false;
|
|
obj = obj || {};
|
|
obj.superclass = classOnly ? function(){} : _.Module;
|
|
return _.createSubclass(obj, !classOnly);
|
|
},
|
|
|
|
"version" : VERSION
|
|
};
|
|
|
|
// This library can be used as an AMD module, a Node.js module, or an
|
|
// old fashioned global
|
|
if (typeof exports !== "undefined") {
|
|
// Server
|
|
if (typeof module !== "undefined" && module.exports) {
|
|
exports = module.exports = Stapes;
|
|
}
|
|
exports.Stapes = Stapes;
|
|
} else if (typeof define === "function" && define.amd) {
|
|
// AMD
|
|
define(function() {
|
|
return Stapes;
|
|
});
|
|
} else {
|
|
// Global scope
|
|
window.Stapes = Stapes;
|
|
}
|
|
})();
|