/**
* A generator for standard elements
* @namespace Generator
*/
/* global documentExportFormats */
/* global getTranslation */
/* global setInnerText */
var generator = {
/**
* Add a data-description attribute to an element
*
* @param {object} element the object to update
* @param {string} label the label of the description
* @param {boolean} isDescription if we want a description of the label which is predefined in i18n
* @memberof Generator
*/
addDescription: function (element, label, isDescription) {
if ( isDescription ) { label += '.description'; }
if (getTranslation(label, 'empty') != 'empty') {
element.setAttribute('data-description', getTranslation(label));
} else {
element.removeAttribute('data-description');
}
},
/**
* Generate a label entry and return it.
*
* @param {string} label the string label
* @returns {object} the label element
* @memberof Generator
*/
getEntryLabel: function (label) {
var entry = document.createElement('label');
var element = document.createElement("span");
element.addClassName( "label" );
setInnerText( element, getTranslation(label) + ':');
entry.appendChild(element);
generator.addDescription(entry, label, true);
return entry;
},
/**
* Evaluate a value if it is a function and return the response.
* If it is not a function the contenxt will be returned.
*
* @param {object} value function or object
* @returns {object} the result of the evaluation
* @memberof Generator
*/
evalValue: function (value) {
return typeof value == 'function' ? value() : value;
},
/*******************************************************************************
* A Combobox that has a dropdown
*
* @param {object} parentElement the element where the submenu will be added
* @param {boolean} replaceElement if the original parentElement should be replaced
******************************************************************************/
submenuContainer: function (parentElement, replaceParent) {
/** @class Generator~submenuContainer */
var self = this;
this.parentElement = parentElement;
this.replaceParent = replaceParent || false;
this.submenu = null;
this.menuContainer = null;
this.items = [];
this.offsetWidth = null;
this.openMenuContainer = null;
/**
* Event to finish closing the submenu
*
* @memberof Generator~submenuContainer
*/
this.finishClose = function () {
self.menuContainer.removeClassName('closeNow');
self.submenu.removeEvent('mouseout', self.finishClose);
};
/**
* Trigger closing the submenu
*
* @memberof Generator~submenuContainer
*/
this.triggerClose = function () {
self.menuContainer.addClassName('closeNow');
self.submenu.addEvent('mouseout', self.finishClose);
};
/**
* Add an entry to the submenu
*
* @param {(object|string)} value the entry to add
* @param {function} action function to execute when selected
* @param {boolean} addInFront if the element shuld be added in front of all others
* @param {boolean} clickable if the element triggers a close of the container
* @param {boolean} hoverable if the element can be highlighted
* @returns {object} the entry that has been created
* @memberof Generator~submenuContainer
*/
this.addEntry = function (value, action, addInFront, clickable, hoverable) {
if (addInFront == null) {
addInFront = false;
}
var item = document.createElement('li');
item.object = value;
if (hoverable) {
item.addClassName("__over");
}
if (typeof value == 'string') {
var textValue = value;
value = document.createElement("span");
setInnerText( value, textValue);
}
item.appendChild(value);
if (typeof action == 'function') {
item.addEvent('click', function (event) {
action.call(this, event);
if (clickable == null || clickable !== false) {
// Close popUp.
self.triggerClose();
}
});
}
if (addInFront && this.items.length > 0) {
this.menuContainer.insertBefore(item, this.items[0]);
this.items.unshift(item);
} else {
this.menuContainer.appendChild(item);
this.items.push(item);
}
return item;
};
/**
* Remove an entry by its position
* @param {object|number} entryNr either the elment number or the object
* @returns {object|null} the object if it has been removed
* @memberof Generator~submenuContainer
*/
this.removeEntry = function (entryNr) {
if (isNaN(entryNr)) {
entryNr = this.items.indexOf(entryNr);
}
if (this.items.length > entryNr && entryNr >= 0) {
this.items[entryNr].parentNode.removeChild(this.items[entryNr]);
var object = this.items[entryNr].object;
this.items.splice(entryNr, 1);
return object;
}
return null;
};
/**
* Create a submenu selector. Requires a SubmenuContainer as entry point.
*
* <p>The staticContent object has the following structure:
*
* <pre>
* {
* type: {documentExportFormats} // type of the entry. Should be SUBMENU
* optionName: {string|function}, // name of the option, function has to return string
* defaultValue: {string}, // default value from `list`
* list: {array}, // array of objects
* action: {function} // the action to execute on click of an entry
* }
* </pre>
*
* the `list` contains objects in the following form:
*
* <pre>
* {
* name: {string}, // the unique name
* value: {string} // an optional custom value
* }
* </pre>
*
* @param {string} label the label
* @param {object} staticContent the content object
* @returns {object} the Entry
* @memberof Generator~submenuContainer
*/
this.addSubmenuSelector = function (label, staticContent) {
var entry = generator.getEntryLabel(label);
var combobox = null;
var action = function () {
if (!entry.isEnabled()) {
return;
}
var isOpen = combobox.menuContainer.hasClassName('open');
if (!isOpen && self.openMenuContainer != null) {
self.openMenuContainer.removeClassName('open');
} else {
self.openMenuContainer = null;
}
combobox.menuContainer.addRemoveClass('open', !isOpen);
self.menuContainer.addRemoveClass('subMenuOpen', !isOpen);
self.openMenuContainer = combobox.menuContainer;
};
var item = self.addEntry(entry, action, null, false, ((staticContent && staticContent.list && staticContent.list.length) > 1 || !staticContent));
combobox = new generator.submenuContainer(entry, true);
combobox.submenu.style['float'] = 'none';
combobox.submenu.addClassName('right __menuGroup');
combobox.menuContainer.addClassName('__over');
var currentValueHolder = document.createElement('span');
entry.appendChild(currentValueHolder);
entry.currentValueHolder = currentValueHolder;
entry.submenu = combobox;
if (staticContent && staticContent.list) {
currentValueHolder.setAttribute("name", generator.evalValue(staticContent.optionName) || '');
var setValue = function (value) {
if (typeof staticContent.action == 'function') {
staticContent.action(value, item);
}
// This should be saved later on
entry.currentValueHolder.fullValue = value;
$.jStorage.set(label, value);
if (typeof value == 'string') {
setInnerText(entry.currentValueHolder, value);
generator.addDescription(entry.currentValueHolder, value, true);
} else if (typeof value == 'object' && value.name) {
setInnerText(entry.currentValueHolder, getTranslation(value.name));
generator.addDescription(entry.currentValueHolder, value.name, true);
value = value.value; // Value to be send
}
// This is what will be send to server
entry.currentValueHolder.value = value;
};
setValue($.jStorage.get(label, staticContent.list[0]));
if (staticContent.list.length > 1) {
staticContent.list.forEach(function (value) {
var item = combobox.addEntry(getTranslation(value.name) || getTranslation(value), function() {
setValue(value);
}, null, false);
generator.addDescription(item, value.name, true);
});
} else {
entry.setEnabled(false);
}
}
return entry;
};
/**
* Create a submenu element with a label and an input field
*
* <p>The element has the following structure:
*
* <pre>
* {
* type: {documentExportFormats} // type of the entry. Should be INPUT, PASSWORD or COLOR
* optionName: {string|function}, // name of the option, function has to return string
* defaultValue: {string}, // default value from `list`
* }
* </pre>
*
* @param {string} label the label of the element
* @param {object} element the input field type
* @returns {object} the created entry
* @memberof Generator~submenuContainer
*/
this.addInputValueElement = function (label, element) {
var entry = generator.getEntryLabel(label);
entry.addClassName('right');
var group = document.createElement('div');
group.addClassName("__comboBox __menuDropDown");
group.style['float'] = 'none';
entry.appendChild(group);
var item = self.addEntry(entry, null, null, false, false);
var currentValueHolder = document.createElement('input');
currentValueHolder.setAttribute("name", typeof element.optionName == 'function' ? element.optionName(item) : element.optionName || '');
currentValueHolder.value = $.jStorage.get(label, element.defaultValue || '');
currentValueHolder.style['float'] = 'none';
group.appendChild(currentValueHolder);
entry.currentValueHolder = currentValueHolder;
try {
currentValueHolder.type = element.type == documentExportFormats.COLOR ? 'color' : 'text';
} catch (e) {
// IE does not accept color. Really. What???
currentValueHolder.type = 'text';
}
currentValueHolder.className = "__comboInput";
currentValueHolder.addEvent("blur", function () {
$.jStorage.set(label, currentValueHolder.value);
});
return entry;
};
/**
* clears the whole list
*
* @memberof Generator~submenuContainer
*/
this.clear = function () {
this.items = [];
this.menuContainer.innerHTML = '';
};
/**
* returns an entry by its number
*
* @param {number} entryNr the entry number
* @returns {object} the element
* @memberof Generator~submenuContainer
*/
this.getEntry = function (entryNr) {
if (this.items.length > entryNr) {
return this.items[entryNr];
}
return null;
};
/**
* Finds the index of an entry
*
* @param {object} entry the object to look for
* @returns {number} the index
* @memberof Generator~submenuContainer
*/
this.getIndex = function (entry) {
var i = -1;
this.items.forEach(function (item, index) {
if (item === entry) {
i = index;
return 'break';
}
});
return i >= this.items.length ? -1 : i;
};
/**
* the amount of entries in the list
*
* @returns {number} the amount of entries
* @memberof Generator~submenuContainer
*/
this.numberOfEntries = function () {
return this.items.length;
};
/**
* Open the submenu
*
* @param {boolean} visible true to show the subemnu
* @memberof Generator~submenuContainer
*/
this.show = function( visible ) {
this.submenu.addRemoveClass('open', typeof visible == 'undefined' || visible);
};
/**
* Create the submenu structure
*
* @memberof Generator~submenuContainer
* @inner
*/
this.init = function () {
this.submenu = document.createElement('div');
// this.submenu.style.display = 'none'; // Hide at first. - class visible
this.submenu.addClassName('__submenuContainer');
this.submenu.addClassName('visible');
this.offsetWidth = this.parentElement.offsetWidth;
this.menuContainer = document.createElement('ul');
this.menuContainer.addClassName('__submenu');
this.submenu.appendChild(this.menuContainer);
this.submenu.addClassName(this.parentElement.id);
// this.submenu.setAttribute( 'data-left', this.parentElement.absoluteOffsetLeft() );
// this.submenu.setAttribute( 'data-top', this.parentElement.absoluteOffsetTop() );
if (this.replaceParent) {
this.parentElement.parentNode.insertBefore(this.submenu, this.parentElement);
}
this.parentElement.parentNode.removeChild(this.parentElement);
this.submenu.appendChild(this.parentElement);
};
this.init();
},
progressCircle: function( element ) {
var self = this;
this.radius = 35;
this.margin = 5;
this.element = element;
this.svgElement = function( element ) {
return document.createElementNS('http://www.w3.org/2000/svg', element);
};
this.progressContainer = this.svgElement('svg');
this.incomplete = this.svgElement('circle');
this.complete = this.svgElement('circle');
this.init = function() {
this.element && this.element.appendChild( this.progressContainer );
this.progressContainer.appendChild( this.incomplete );
this.progressContainer.appendChild( this.complete );
this.progressContainer.setAttribute('class', 'progress');
this.incomplete.setAttribute('class', 'incomplete');
this.complete.setAttribute('class', 'complete');
var boxSize = (this.radius + this.margin) * 2;
this.progressContainer.setAttribute('viewBox', [0, 0, boxSize, boxSize ].join(' '));
this.incomplete.setAttribute('cx', this.radius + this.margin);
this.incomplete.setAttribute('cy', this.radius + this.margin);
this.incomplete.setAttribute('r', this.radius);
this.complete.setAttribute('cx', this.radius + this.margin);
this.complete.setAttribute('cy', this.radius + this.margin);
this.complete.setAttribute('r', this.radius);
this.setProgress( 0 );
};
this.setProgress = function( progress ) {
var circumference = 2 * Math.PI * this.radius;
var dashOffset = circumference - ((progress * circumference) / 100);
this.complete.setAttribute('style', 'stroke-dashoffset: ' + dashOffset);
};
this.dissolve = function() {
window.setTimeout( function() {
self.progressContainer && self.progressContainer.setAttribute('class', 'progress dissolve');
window.setTimeout( function() {
self.element && self.element.parentElement.removeChild( self.element );
self = null;
}, 1000);
}, 2000);
};
this.init();
}
};