git.fiddlerwoaroof.com
Raw Blame History
/**
 * This file is part of JIM <https://github.com/fiddlerwoaroof/jim/> (c) 2016 Edward Langley
 *
 * See the COPYING document in the root of the source distribution for licensing information.
 */
PresentationType = (function () {
    var presentationTypes = {};

    // TODO: think through selector better: right now, I think we assume that it
    //       is a class selector and things will break if it isn't we should,
    //       however, probably let it be more general.
    function PresentationType(name, selector) {
	if (this instanceof PresentationType) {
	    this.name = name;
	    if (selector === undefined) {
		selector = '.'+this.name;
	    }
	    this.selector = selector;
	    this.receivers = [];
	} else {
	    var result = presentationTypes[name];
	    if (result === undefined) {
		result = new PresentationType(name);
		presentationTypes[result.name] = result;
	    }
	    return result;
	}
    }

    PresentationType.prototype = {
	presentationsTypes: presentationTypes,

	isOfType(name) {
	    var type = presentationTypes[name];
	    //console.log(name);
	    //console.log(type);
	    return type.isPrototypeOf(this);
	},

	addReceiver(receiver) {
	    this.receivers.push(receiver);
	    return this.receivers.length - 1;
	},

	removeReceiver(el) {
	    var elIdx = this.receivers.indexOf(el);
	    var result = elIdx !== -1;
	    if (result) {
		this.receivers.splice(elIdx,1);
	    }
	    return result;
	},

	notifyReceivers(theEvent, self) {
	    if (self === undefined) {
		self = this;
	    }

	    var outerArgs = Array.prototype.slice.call(arguments);
	    outerArgs.unshift(theEvent, self, self.value);

	    this.receivers.forEach((function (theReceiver) {
		theReceiver.receive.apply(theReceiver, outerArgs);
	    }));

	    var parent = Object.getPrototypeOf(this);
	    if (parent instanceof PresentationType && parent.receivers !== undefined && parent.receivers !== []) {
		parent.notifyReceivers(theEvent, self);
	    }
	},

	selectAllTags() {
	    return Array.prototype.slice.call(
		document.querySelectorAll(this.selector)
	    );
	},

	selectAll() {
	    var allTags = this.selectAllTags();
	    return Array.prototype.map.call(allTags, function (el) {
		return el.ptObject;
	    });
	},

	bindTags() {
	    var els = this.selectAllTags().filter(function (theEl) {
		return theEl.ptObject === undefined;
	    });

	    els.forEach(this.bindEl, this);

	    return this;
	},

	bindEl(theEl) {
	    var content = theEl.textContent.trim();
	    var value = theEl.value;

	    var bindTypes = theEl.dataset.bind;
	    if (bindTypes !== undefined) {
		bindTypes = bindTypes.split(',');
	    } else {
		bindTypes = ['click'];
	    }

	    //console.log('bt',bindTypes);

	    if (! theEl.hasAttribute('value') ) {
		var tmpVal = theEl.dataset.value;
		value = tmpVal === undefined ? content : theEl.dataset.value;
	    }

	    return this.wrapTag(theEl, value, bindTypes, content);
	},

	wrapTag(tag, value, bindTypes, content) {
	    var result = Object.assign(
		Object.create(this), {
		    tag: tag,
		    value: value,
		    display: content,
		}
	    );
	    tag.ptObject = result;

	    this.classes.forEach(function (cls) {
		tag.classList.add(cls);
	    });

	    bindTypes.forEach(function (event) {
		//console.log(event);
		tag.addEventListener(event, result, false);
	    });

	    if (result.validate()) {
		return result;
	    } else {
		this.doError(result);
	    }
	},

	get classes() {
	    var current = this,
		classes = new Set();
	    while (current instanceof PresentationType) {
		if (current.name !== undefined) {
		    classes.add(current.name);
		}
		current = Object.getPrototypeOf(current);
	    }

	    return classes;
	},

	extend(name, props) {
	    if (this.children === undefined) {
		this.children = [];
	    }

	    if (props === undefined) {
		props = {};
	    }

	    props.name = name;
	    if (props.selector === undefined) {
		props.selector = '.' + name;
	    }
	    props.receivers = [];
	    var newType = Object.assign(Object.create(this), props);
	    presentationTypes[name] = newType;
	    this.children.push(newType);
	    return newType;
	},

	makeTag(value, tagName, bindTypes, content) {
	    var newTag = document.createElement(tagName);

	    this.classes.forEach(function (cls) { newTag.classList.add(cls); });

	    if (bindTypes === undefined) {
		bindTypes = [];
	    } else if (!(bindTypes instanceof Array)) {
		content = bindTypes;
		bindTypes = [];
	    }

	    if (content === undefined) {
		content = value;
	    }

	    newTag.textContent = content;
	    return this.wrapTag(newTag, value, bindTypes, content);
	},

	handleEvent(theEvent) {
	    //console.log('handling: ',theEvent);
	    var handler = this[theEvent.type];
	    var result = true;
	    if (handler !== undefined) {
		result = handler.bind(this)(theEvent);
	    }
	    this.notifyReceivers(theEvent);

	    return result;
	},

	doError(object) {
	    return;
	},

	validate() {
	    return true;
	},

	toggleAll() {
	    var els = this.selectAllTags();

	    els.forEach(function (el) {
		el.classList.toggle('activated');
	    });
	},

	deactivateAll() {
	    var els = this.selectAllTags();

	    els.forEach(function (el) {
		el.classList.remove('activated');
	    });
	},

	activateAll() {
	    this.bindTags();
	    var els = this.selectAllTags();

	    els.forEach(function (el) {
		el.classList.add('activated');
	    });

	    return els;
	},


    };

    var typeType = PresentationType('type');
    typeType.generateTags = function (tagName) {
	if (tagName === undefined) {
	    tagName = 'span';
	}

	return Object.keys(presentationTypes).map(
	    function (type) {
		var tag = document.createElement(tagName);
		return this.wrapTag(tag, type, ['click'], type);
	    }, this
	);
    };
    return PresentationType;
})();