Skip to content

Instantly share code, notes, and snippets.

@jakobo
Created May 21, 2010 22:04
Show Gist options
  • Save jakobo/409496 to your computer and use it in GitHub Desktop.
Save jakobo/409496 to your computer and use it in GitHub Desktop.
A "backwards" class library for JavaScript
/**
* Sslac - A "backwards" class library for JavaScript
* Provides a consistent way to declare classes to ease 1st and 3rd
* party development by using closures.
* Features:
* - auto extension: Objects can be set to extend by default, allowing for
* easy inheritance of this.* properties as well as prototyped methods
* - consistent privileged interface: this.* assignments are handled by the
* framework.
* - static and instance functionality: Can create both statuc and instance
* level objects
* @class Sslac
* @author Jakob Heuser <jheuser@linkedin.com>
*/
(function() {
var NAMESPACE = "Sslac";
window[NAMESPACE] = window[NAMESPACE] || {};
/**
* extend an object and assign parent to .superclass
* @license BSD
* @author YUI
*/
var extend = function(subc, superc, overrides) {
var F = function() {};
F.prototype = superc.prototype;
subc.prototype = new F();
subc.prototype.constructor = subc;
subc.superclass = superc.prototype;
if (superc.prototype.constructor == Object.prototype.constructor) {
superc.prototype.constructor = superc;
}
if (overrides) {
for (i in overrides) {
if (overrides.hasOwnProperty(overrides, i)) {
subc.prototype[i]=overrides[i];
}
}
}
};
/**
* get the namespace object of a given ns
* @param {String} namespace
* @return {Object} the parent NS (for insertion)
*/
var namespaceOf = function(ns) {
var i,
piece,
scope,
pieces = ns.split(/\./),
len = pieces.length;
// loop through all pieces
for (i = 0, scope = window; i < len; i++) {
piece = pieces[i];
// if not a match, drill down one further
if (i + 1 == len) {
return scope;
}
scope[piece] = scope[piece] || {};
scope = scope[piece];
}
};
/**
* Get the endpoint name of a namespace
* for example: Foo.Bar.Baz => Baz
* @param {String} the namespace
* @param {String} the endpoint name
*/
var nameOf = function(ns) {
var pieces = ns.split(/\./),
last = pieces[pieces.length - 1];
return last;
};
// The "root" of all evil. Really, just an object that gaurentees a base heirarchy
var Class = function() {};
/**
* Root chaining object for Sslac. Takes a namespace and isStatic
* this is the base object for construction of Sslac classes
* @class ObjectRef
*/
var ObjectRef = function(ns) {
var parent = null;
var localConstructor = function() {};
var privilegedMethods = {};
var F = function() {
var namespace = ns;
if (parent) {
this.Parent = parent.constructor.apply(this, arguments);
}
for (name in privilegedMethods) {
this[name] = privilegedMethods[name];
}
return localConstructor.apply(this, arguments);
};
/**
* defines a constructor
* @method Constructor
* @param {Function} the function to set
* @return this
*/
this.Constructor = function(fn) {
localConstructor = fn;
return this;
};
/**
* defines a method (this.* syntax)
* @method Method
* @param {String} the name to store
* @param {Function} the function to set
* @return this
*/
this.Method = function(name, fn) {
privilegedMethods[name] = fn;
return this;
};
/**
* Explicitly put something on the prototype
* @method Prototype
* @param {String} the name to store
* @param {Function} the function to set
* @return this
*/
this.Prototype = function(name, fn) {
F.prototype[name] = fn;
return this;
};
/**
* Explicitly put something on the static object
* @method Static
* @param {String} the name to store
* @param {Function} the function to set
* @return this
*/
this.Static = function(name, fn) {
F[name] = fn;
return this;
};
/**
* define the superclass of this object
* @method Extends
* @param {String|Object} the object to extend
* @return this
*/
this.Extends = function(name) {
if (typeof(name) == "string") {
name = namespaceOf(name)[nameOf(name)];
}
extend(F, name);
parent = F.superclass;
return this;
};
// extend default class
this.Extends(Class);
var placeNS = namespaceOf(ns);
var placeName = nameOf(ns);
placeNS[placeName] = F;
};
// create object
var createObject = function(ns) {
return new ObjectRef(ns);
};
// create static object
var createStaticObject = function(ns) {
// todo: add protection against delcaring memebrs in static mode
return new ObjectRef(ns);
};
// helper to create a function
var createFunction = function(ns, fn) {
var placeNS = namespaceOf(ns);
var placeName = nameOf(ns);
placeNS[placeName] = fn;
};
// helper to just define a namespace
var defineNamespace = function(ns) {
var placeNS = namespaceOf(ns);
var placeName = nameOf(ns);
placeNS[placeName] = placeNS[placeName] || {};
};
// assign outward
window[NAMESPACE].Class = createObject;
window[NAMESPACE].Static = createStaticObject;
window[NAMESPACE].Function = createFunction;
window[NAMESPACE].Define = defineNamespace;
window[NAMESPACE].ClassObject = Class;
})();
// licensing block
// extend()
/*
Copyright (c) 2010, Yahoo! Inc.
All rights reserved.
Redistribution and use of this software in source and binary forms, with or
without modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of Yahoo! Inc. nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior
written permission of Yahoo! Inc.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
// ----------------------------------------------------------------------------------------------------
// Main Connect Framework
// ----------------------------------------------------------------------------------------------------
/**
* TagParser responsible for finding the LI Connect tags and handling them
* @class LIx.TagParser
* @constructor
*/
Sslac.Class("LIx.TagParser")
.Constructor(function() {
this.tagRegistry = {};
})
/**
* process a match for a tag
* @param node {HTMLElement}
* @private
* @method processTagMatch
*/
.Method("processTagMatch", function(node) {
var tagName = node.tagName.toLowerCase();
var name;
// normalize tags for entry, handle non qualifying tags
switch(tagName) {
case "li:connect":
name = node.getAttribute("name");
break;
case "embed":
if (!node.getAttribute("data-li")) {
return;
}
name = node.getAttribute("name");
break;
}
if (!this.tagRegistry[name]) {
return false;
}
// extract the attributes
var obj, container, attr = {};
for(var i = 0, attrs = node.attributes, len = attrs.length; i < len; i++) {
if (LIx.Util.support.li_attributesSpecified) {
attr[attrs.item(i).nodeName] = attrs.item(i).nodeValue;
}
else {
if (attrs.item(i).specified) {
attr[attrs.item(i).nodeName] = attrs.item(i).nodeValue;
}
}
}
// replace the node with a container
container = document.createElement("div");
node.parentNode.insertBefore(container, node);
node.parentNode.removeChild(node);
// invoke
obj = new this.tagRegistry[name](container, attr);
})
/**
* parse the document
* @privileged
* @method parse
*/
.Method("parse", function() {
// collect all tags from the page
for (var i = 0, tags = [].slice.apply(LIx.$Tag('li:connect')), len = tags.length; i < len; i++) {
this.processTagMatch(tags[i]);
}
// if using html5 compliant tags
if (LIx.ENV.html5) {
for (var i = 0, tags = [].slice.apply(LIx.$Tag('embed')), len = tags.length; i < len; i++) {
this.processTagMatch(tags[i]);
}
}
})
/**
* add a new tag to the parser engine
* @privileged
* @param name {String} the tag name to handle
* @param obj {Object} the object to create for this match
* @method add
*/
.Method("add", function(name, obj) {
this.tagRegistry[name] = obj;
});
// auto executing to create a single tp, map the "add" function to it
// and then on dom ready trigger a parse
(function() {
var tp = new LIx.TagParser();
// alias this instance for readability
window.LIx.addTag = function() {
tp.add.apply(tp, arguments);
};
LIx.Event.onDOMReady(function() {
tp.parse();
});
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment