1
0
mirror of https://github.com/Jermolene/TiddlyWiki5 synced 2024-11-14 05:44:50 +00:00
TiddlyWiki5/core/modules/utils/dom/dom.js

272 lines
7.2 KiB
JavaScript

/*\
title: $:/core/modules/utils/dom.js
type: application/javascript
module-type: utils
Various static DOM-related utility functions.
\*/
(function(){
/*jslint node: true, browser: true */
/*global $tw: false */
"use strict";
/*
Determines whether element 'a' contains element 'b'
Code thanks to John Resig, http://ejohn.org/blog/comparing-document-position/
*/
exports.domContains = function(a,b) {
return a.contains ?
a !== b && a.contains(b) :
!!(a.compareDocumentPosition(b) & 16);
};
exports.removeChildren = function(node) {
while(node.hasChildNodes()) {
node.removeChild(node.firstChild);
}
};
exports.hasClass = function(el,className) {
return el && el.className && el.className.toString().split(" ").indexOf(className) !== -1;
};
exports.addClass = function(el,className) {
var c = el.className.split(" ");
if(c.indexOf(className) === -1) {
c.push(className);
}
el.className = c.join(" ");
};
exports.removeClass = function(el,className) {
var c = el.className.split(" "),
p = c.indexOf(className);
if(p !== -1) {
c.splice(p,1);
el.className = c.join(" ");
}
};
exports.toggleClass = function(el,className,status) {
if(status === undefined) {
status = !exports.hasClass(el,className);
}
if(status) {
exports.addClass(el,className);
} else {
exports.removeClass(el,className);
}
};
/*
Get the first parent element that has scrollbars or use the body as fallback.
*/
exports.getScrollContainer = function(el) {
var doc = el.ownerDocument;
while(el.parentNode) {
el = el.parentNode;
if(el.scrollTop) {
return el;
}
}
return doc.body;
};
/*
Get the scroll position of the viewport
Returns:
{
x: horizontal scroll position in pixels,
y: vertical scroll position in pixels
}
*/
exports.getScrollPosition = function() {
if("scrollX" in window) {
return {x: window.scrollX, y: window.scrollY};
} else {
return {x: document.documentElement.scrollLeft, y: document.documentElement.scrollTop};
}
};
/*
Adjust the height of a textarea to fit its content, preserving scroll position, and return the height
*/
exports.resizeTextAreaToFit = function(domNode,minHeight) {
// Get the scroll container and register the current scroll position
var container = $tw.utils.getScrollContainer(domNode),
scrollTop = container.scrollTop;
// Measure the specified minimum height
domNode.style.height = minHeight;
var measuredHeight = domNode.offsetHeight || parseInt(minHeight,10);
// Set its height to auto so that it snaps to the correct height
domNode.style.height = "auto";
// Calculate the revised height
var newHeight = Math.max(domNode.scrollHeight + domNode.offsetHeight - domNode.clientHeight,measuredHeight);
// Only try to change the height if it has changed
if(newHeight !== domNode.offsetHeight) {
domNode.style.height = newHeight + "px";
// Make sure that the dimensions of the textarea are recalculated
$tw.utils.forceLayout(domNode);
// Set the container to the position we registered at the beginning
container.scrollTop = scrollTop;
}
return newHeight;
};
/*
Gets the bounding rectangle of an element in absolute page coordinates
*/
exports.getBoundingPageRect = function(element) {
var scrollPos = $tw.utils.getScrollPosition(),
clientRect = element.getBoundingClientRect();
return {
left: clientRect.left + scrollPos.x,
width: clientRect.width,
right: clientRect.right + scrollPos.x,
top: clientRect.top + scrollPos.y,
height: clientRect.height,
bottom: clientRect.bottom + scrollPos.y
};
};
/*
Saves a named password in the browser
*/
exports.savePassword = function(name,password) {
try {
if(window.localStorage) {
localStorage.setItem("tw5-password-" + name,password);
}
} catch(e) {
}
};
/*
Retrieve a named password from the browser
*/
exports.getPassword = function(name) {
try {
return window.localStorage ? localStorage.getItem("tw5-password-" + name) : "";
} catch(e) {
return "";
}
};
/*
Force layout of a dom node and its descendents
*/
exports.forceLayout = function(element) {
var dummy = element.offsetWidth;
};
/*
Pulse an element for debugging purposes
*/
exports.pulseElement = function(element) {
// Event handler to remove the class at the end
element.addEventListener($tw.browser.animationEnd,function handler(event) {
element.removeEventListener($tw.browser.animationEnd,handler,false);
$tw.utils.removeClass(element,"pulse");
},false);
// Apply the pulse class
$tw.utils.removeClass(element,"pulse");
$tw.utils.forceLayout(element);
$tw.utils.addClass(element,"pulse");
};
/*
Attach specified event handlers to a DOM node
domNode: where to attach the event handlers
events: array of event handlers to be added (see below)
Each entry in the events array is an object with these properties:
handlerFunction: optional event handler function
handlerObject: optional event handler object
handlerMethod: optionally specifies object handler method name (defaults to `handleEvent`)
*/
exports.addEventListeners = function(domNode,events) {
$tw.utils.each(events,function(eventInfo) {
var handler;
if(eventInfo.handlerFunction) {
handler = eventInfo.handlerFunction;
} else if(eventInfo.handlerObject) {
if(eventInfo.handlerMethod) {
handler = function(event) {
eventInfo.handlerObject[eventInfo.handlerMethod].call(eventInfo.handlerObject,event);
};
} else {
handler = eventInfo.handlerObject;
}
}
domNode.addEventListener(eventInfo.name,handler,false);
});
};
/*
Get the computed styles applied to an element as an array of strings of individual CSS properties
*/
exports.getComputedStyles = function(domNode) {
var textAreaStyles = window.getComputedStyle(domNode,null),
styleDefs = [],
name;
for(var t=0; t<textAreaStyles.length; t++) {
name = textAreaStyles[t];
styleDefs.push(name + ": " + textAreaStyles.getPropertyValue(name) + ";");
}
return styleDefs;
};
/*
Apply a set of styles passed as an array of strings of individual CSS properties
*/
exports.setStyles = function(domNode,styleDefs) {
domNode.style.cssText = styleDefs.join("");
};
/*
Copy the computed styles from a source element to a destination element
*/
exports.copyStyles = function(srcDomNode,dstDomNode) {
$tw.utils.setStyles(dstDomNode,$tw.utils.getComputedStyles(srcDomNode));
};
/*
Copy plain text to the clipboard on browsers that support it
*/
exports.copyToClipboard = function(text,options) {
options = options || {};
var textArea = document.createElement("textarea");
textArea.style.position = "fixed";
textArea.style.top = 0;
textArea.style.left = 0;
textArea.style.fontSize = "12pt";
textArea.style.width = "2em";
textArea.style.height = "2em";
textArea.style.padding = 0;
textArea.style.border = "none";
textArea.style.outline = "none";
textArea.style.boxShadow = "none";
textArea.style.background = "transparent";
textArea.value = text;
document.body.appendChild(textArea);
textArea.select();
textArea.setSelectionRange(0,text.length);
var succeeded = false;
try {
succeeded = document.execCommand("copy");
} catch (err) {
}
if(!options.doNotNotify) {
$tw.notifier.display(succeeded ? "$:/language/Notifications/CopiedToClipboard/Succeeded" : "$:/language/Notifications/CopiedToClipboard/Failed");
}
document.body.removeChild(textArea);
};
exports.getLocationPath = function() {
return window.location.toString().split("#")[0];
};
})();