closedquestion-8.x-3.x-dev/assets/js/libraries/jquery/jquery.simpleTableEdit.js
assets/js/libraries/jquery/jquery.simpleTableEdit.js
/**
* simpleTableEdit
* based on boilerplate version 1.3
* @param {object} $ A jQuery object
**/
(function ($) {
"use strict"; //ECMA5 strict modus
$.simpleTableEdit = function (element, settings) {
/* define vars
*/
/* this object will be exposed to other objects */
var publicObj = this;
//the version number of the plugin
publicObj.version = "1.0";
/* this object holds functions used by the plugin boilerplate */
var _helper = {
/**
* Call hooks, additinal parameters will be passed on to registered plugins
* @param {string} name
*/
doHook: function (name) {
var i;
var pluginFunctionArgs = [];
/* remove first two arguments */
for (i = 1; i < arguments.length; i++) {
pluginFunctionArgs.push(arguments[i]);
}
/* call plugin functions */
if (_globals.plugins !== undefined) {
/* call plugins */
$.each(_globals.plugins, function (simpleTableEdit, extPlugin) {
if (
extPlugin.__hooks !== undefined &&
extPlugin.__hooks[name] !== undefined
) {
extPlugin.__hooks[name].apply(publicObj, pluginFunctionArgs);
}
});
}
/* trigger event on main element */
_globals.$element.trigger(name, pluginFunctionArgs);
},
/**
* Initializes the plugin
*/
doInit: function () {
_helper.doHook(
"simpleTableEdit.beforeInit",
publicObj,
element,
settings
);
publicObj.init();
_helper.doHook("simpleTableEdit.init", publicObj);
},
/**
* Loads an external script
* @param {string} libName
* @param {string} errorMessage
*/
loadScript: function (libName, errorMessage) {
/* remember libname */
_cdnFilesToBeLoaded.push(libName);
/* load script */
$.ajax({
type: "GET",
url: _globals.dependencies[libName].cdnUrl,
success: function () {
/* forget libname */
_cdnFilesToBeLoaded.splice(_cdnFilesToBeLoaded.indexOf(libName), 1); //remove element from _cdnFilesToBeLoaded array
/* call init function when all scripts are loaded */
if (_cdnFilesToBeLoaded.length === 0) {
_helper.doInit();
}
},
fail: function () {
console.error(errorMessage);
},
dataType: "script",
cache: "cache"
});
},
/**
* Registers a plugin
* @param {string} name Name of plugin, must be unique
* @param {object} object An object {("functions": {},) (, "hooks: {})}
*/
registerPlugin: function (name, object) {
var plugin;
var hooks;
/* reorder plugin */
hooks = $.extend(true, {}, object.hooks);
plugin = object.functions !== undefined ? object.functions : {};
plugin.__hooks = hooks;
/* add plugin */
_globals.plugins[name] = plugin;
},
/**
* Calls a plugin function, all additional arguments will be passed on
* @param {string} simpleTableEdit
* @param {string} pluginFunctionName
*/
callPluginFunction: function (simpleTableEdit, pluginFunctionName) {
var i;
/* remove first two arguments */
var pluginFunctionArgs = [];
for (i = 2; i < arguments.length; i++) {
pluginFunctionArgs.push(arguments[i]);
}
/* call function */
_globals.plugins[simpleTableEdit][pluginFunctionName].apply(
null,
pluginFunctionArgs
);
},
/**
* Checks dependencies based on the _globals.dependencies object
* @returns {boolean}
*/
checkDependencies: function () {
var dependenciesPresent = true;
for (var libName in _globals.dependencies) {
var errorMessage =
"jquery.simpleTableEdit: Library " +
libName +
" not found! This may give unexpected results or errors.";
var doesExist = $.isFunction(_globals.dependencies[libName])
? _globals.dependencies[libName]
: _globals.dependencies[libName].doesExist;
if (doesExist.call() === false) {
if (
$.isFunction(_globals.dependencies[libName]) === false &&
_globals.dependencies[libName].cdnUrl !== undefined
) {
/* cdn url provided: Load script from external source */
_helper.loadScript(libName, errorMessage);
}
else {
console.error(errorMessage);
dependenciesPresent = false;
}
}
}
return dependenciesPresent;
}
};
/* keeps track of external libs loaded via their CDN */
var _cdnFilesToBeLoaded = [];
/* this object holds all global variables */
var _globals = {};
/* handle settings */
var defaultSettings = {};
_globals.settings = {};
if ($.isPlainObject(settings) === true) {
_globals.settings = $.extend(true, {}, defaultSettings, settings);
}
else {
_globals.settings = defaultSettings;
}
/* this object contains a number of functions to test for dependencies,
* doesExist function should return TRUE if the library/browser/etc is present
*/
_globals.dependencies = {
/* check for jQuery 1.6+ to be present */
"jquery1.6+": {
doesExist: function () {
var jqv, jqv_main, jqv_sub;
if (window.jQuery) {
jqv = jQuery().jquery.split(".");
jqv_main = parseInt(jqv[0], 10);
jqv_sub = parseInt(jqv[1], 10);
if (jqv_main > 1 || (jqv_main === 1 && jqv_sub >= 6)) {
return true;
}
}
return false;
},
cdnUrl: "http://code.jquery.com/jquery-git1.js"
}
};
_helper.checkDependencies();
//this object holds all plugins
_globals.plugins = {};
/* register DOM elements
* jQuerified elements start with $
*/
_globals.$element = $(element);
_globals.tableTemplate = "<table border=1><tr><th>Header 1</th><th>Header 2</th></tr><tr><td>Cell A</td><td>Cell B</td></tr></table><p> </p>";
/**
* Init function
**/
publicObj.init = function () {
};
/**
* Adds a table.
*/
publicObj.addTable = function () {
var $activeTable = getActiveTable();
if ($activeTable.length === 0) {
addEmptyTable();
return;
}
}
/**
* Adds a row.
*/
publicObj.addRow = function () {
var $activeTable = getActiveTable();
var $activeRow = getActiveRow();
var $newRow = getRowClone();
// No table.
if ($activeTable.length === 0) {
return;
}
if ($activeRow.length === 1) {
// Add after active row.
$activeRow.after($newRow);
}
else {
// Add at end of table.
$activeTable.append($newRow);
}
};
/**
* Changes the selected cell into a header cell or vice versa.
*/
publicObj.toggleHeaderCell = function () {
var $activeNode = getActiveNode();
var $replacer, $cell;
// Determine what to replace with what.
$cell = $activeNode.closest("td", _globals.$element[0]);
if ($cell.length > 0) {
$replacer = $('<th />');
}
else {
$cell = $activeNode.closest("th", _globals.$element[0]);
if ($cell.length > 0) {
$replacer = $('<td />');
}
}
// Do the trick.
if ($replacer) {
$replacer.append($cell.html());
$cell.replaceWith($replacer);
setCaret($replacer[0]); // Set caret in new cell.
}
};
/**
* Removes the currently selected row.
*/
publicObj.removeRow = function () {
var $activeRow = getActiveRow();
var $activeTable = getActiveTable();
var $nextRow, $prevRow;
if ($activeTable.length === 0) {
return;
}
if ($activeRow.length === 1) {
/* Set cursor in different cell than current one */
$nextRow = $activeRow.next('tr');
$prevRow = $activeRow.prev('tr');
if ($nextRow.length === 1) {
setCaret($nextRow.find('td,th').eq(0)[0]);
}
else if ($prevRow.length === 1) {
setCaret($prevRow.find('td,th').eq(0)[0]);
}
else {
setCaret($activeTable.find('td').eq(0)[0]);
}
/* Remove row */
$activeRow.remove();
}
removeTableIfEmpty();
};
/**
* Adds a column to the active table
*/
publicObj.addColumn = function () {
var activeColumnIndex = getActiveColumnIndex() + 1;
var $activeTable = getActiveTable();
if ($activeTable.length === 0) {
return;
}
if ($activeTable.length === 1) {
if (activeColumnIndex >= 0) {
/* Add headers */
$activeTable.find("tr th:nth-child(" + activeColumnIndex + ")").each(function () {
$(this).after("<th> </th>");
});
/* Add cells */
$activeTable.find("tr td:nth-child(" + activeColumnIndex + ")").each(function () {
$(this).after("<td> </td>");
});
}
}
};
/**
* Adds a column to the active table
*/
publicObj.removeColumn = function () {
var $activeTable = getActiveTable();
var activeColumnIndex = getActiveColumnIndex();
var $activeNode = getActiveNode();
var $prevCell, $nextCell;
if ($activeTable.length === 0) {
return;
}
if ($activeTable.length === 1) {
/* Set cursor in different cell than current one */
$prevCell = $activeNode.prev('td,th');
$nextCell = $activeNode.next('td,th');
if ($prevCell.length === 1) {
setCaret($prevCell[0]);
}
else if ($nextCell.length === 1) {
setCaret($nextCell[0]);
}
else {
setCaret($activeTable.find('td').eq(0)[0]);
}
/* Remove headers */
$activeTable.find("tr").each(function () {
$(this)
.find("th")
.eq(activeColumnIndex)
.remove();
});
/* Remove cells */
$activeTable.find("tr").each(function () {
$(this)
.find("td")
.eq(activeColumnIndex)
.remove();
});
}
removeTableIfEmpty();
};
/**
* Returns whether cursor is inside table.
*
* @returns {Boolean}
*/
publicObj.inTable = function () {
return getActiveTable().length !== 0
};
/**
* Adds an empty table.
*/
function addEmptyTable() {
var $activeNode = getActiveNode();
if ($activeNode[0] === element) {
var sel = window.getSelection();
var range = sel.getRangeAt(0);
range.insertNode(jQuery(_globals.tableTemplate)[0]);
}
else {
// Add table after active node.
if (isBlockLevelElement($activeNode[0])) {
$activeNode.after(_globals.tableTemplate);
}
else {
// Fixes IE bug sometimes inserting the table inside an inline element.
$activeNode.parents().filter(function () {
return $(this).css("display") === "block";
}).first().after(_globals.tableTemplate);
}
}
}
/**
* Returns whether DOM element is block-level or inline.
*
* @param {object} domEl
* @returns {Boolean}
*/
function isBlockLevelElement(domEl) {
var displayStyle;
if (domEl) { // Only if the element exists
if (window.getComputedStyle) {
displayStyle = window.getComputedStyle(domEl, null).getPropertyValue('display');
}
else {
displayStyle = domEl.currentStyle.display;
}
}
return displayStyle === 'block';
}
/**
* Removes an empty table.
*/
function removeTableIfEmpty() {
var $activeTable = getActiveTable();
if ($activeTable.length === 1 && (($activeTable.find('td').length === 0 && $activeTable.find('th').length === 0) || $activeTable.find('tr').length === 0)) {
$activeTable.remove();
}
}
/**
* Get a row clone
*
* @return {object} A jQuery object.
*/
function getRowClone() {
var $activeTable = getActiveTable();
if ($activeTable.length !== 0) {
var $newTr = jQuery("tr", $activeTable).last().clone();
$newTr.find('td,th').html(' ');
return $newTr;
}
else {
return jQuery("");
}
}
/**
* Returns the active table in a contenteditable field.
*
* @return {object} A jQuery object.
**/
function getActiveTable() {
var $activeNode = getActiveNode();
if ($activeNode) {
return $activeNode.closest("table", _globals.$element[0]);
}
return jQuery("");
}
/**
* Returns the active row in a contenteditable field.
*
* @return {object} A jQuery object.
**/
function getActiveRow() {
var $activeNode = getActiveNode();
if ($activeNode) {
return $activeNode.closest("tr", _globals.$element[0]);
}
return jQuery("");
}
/**
* Returns the active table in a contenteditable field.
*
* @return {mixed} Column index or FALSE.
**/
function getActiveColumnIndex() {
var $activeNode = getActiveNode();
if ($activeNode) {
return $activeNode.closest("td,th", _globals.$element[0]).index();
}
return false;
}
/**
* Returns the active node in a contenteditable field.
*
* $return {object} A JQuery object.
**/
function getActiveNode() {
var selection;
var returnNode;
if (window.getSelection) {
selection = window.getSelection();
}
else if (document.selection && document.selection.type != "Control") {
selection = document.selection;
}
if (selection.anchorNode && selection.anchorNode.nodeType === 3) {
returnNode = selection.anchorNode.parentNode;
}
else {
returnNode = selection.anchorNode;
}
return jQuery(returnNode);
}
/**
* Sets the caret inside a DOM element
* @param {object} domEl
* A DOM object.
* @credits
* https://stackoverflow.com/questions/6249095/how-to-set-caretcursor-position-in-contenteditable-element-div?answertab=active#tab-top
* @returns {undefined}
*/
function setCaret(domEl) {
if (domEl) {
var range = document.createRange();
var sel = window.getSelection();
range.setStart(domEl, 0);
range.collapse(true);
sel.removeAllRanges();
sel.addRange(range);
}
_globals.$element[0].focus();
}
/* initialize simpleTableEdit
*/
if (_cdnFilesToBeLoaded.length === 0) {
_helper.doInit();
}
};
$.fn.simpleTableEdit = function (settings) {
return this.each(function () {
if (undefined === $(this).data("simpleTableEdit")) {
var plugin = new $.simpleTableEdit(this, settings);
$(this).data("simpleTableEdit", plugin);
}
});
};
})(jQuery);
