Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring by @valexr #5

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions cash.min.js

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions deploy/build_js.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ java -jar $JSCOMP --jscomp_off checkTypes --language_in ECMASCRIPT5_STRICT --js
java -jar $JSCOMP --jscomp_off checkTypes --language_in ECMASCRIPT5_STRICT --js $ROOT/src_js/third_party/selectize.js >> $JSTARGET
java -jar $JSCOMP --jscomp_off checkTypes --language_in ECMASCRIPT5_STRICT --js $ROOT/src_js/third_party/selectize_preserve_on_blur.js >> $JSTARGET
java -jar $JSCOMP --jscomp_off checkTypes --language_in ECMASCRIPT_NEXT --js $ROOT/src_js/third_party/optimade_mpds_nlp.js >> $JSTARGET
java -jar $JSCOMP --jscomp_off checkTypes --language_in ECMASCRIPT_NEXT --js $ROOT/src_js/wmgui$.js >> $JSTARGET
java -jar $JSCOMP --jscomp_off checkTypes --language_in ECMASCRIPT_NEXT --js $ROOT/src_js/clean.js >> $JSTARGET
java -jar $JSCOMP --jscomp_off checkTypes --language_in ECMASCRIPT5_STRICT --js $ROOT/src_js/wmsettings.js >> $JSTARGET
java -jar $JSCOMP --jscomp_off checkTypes --language_in ECMASCRIPT_NEXT --js $ROOT/src_js/wmcore.js >> $JSTARGET
java -jar $JSCOMP --jscomp_off checkTypes --language_in ECMASCRIPT5_STRICT --js $ROOT/src_js/sliders.js >> $JSTARGET
Expand Down
84 changes: 47 additions & 37 deletions example_dev.html
Original file line number Diff line number Diff line change
@@ -1,41 +1,51 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="webassets/ermac.css" />
<script type="text/javascript" src="jquery.min.js"></script>
</head>
<body class="noscroll">

<script type="text/javascript">
// a very simple env loader
var to_load = [
'src_js/third_party/jquery.tablesorter.js',
'src_js/third_party/wNumb.js',
'src_js/third_party/nouislider.js',
'src_js/third_party/selectize.js',
'src_js/third_party/selectize_preserve_on_blur.js',
'src_js/third_party/optimade_mpds_nlp.js',
'src_js/wmsettings.js',
'src_js/wmcore.js',
'src_js/sliders.js',
'src_js/third_party/autocomplete.js',
'src_js/router.js',
'src_js/events.js',
'src_js/markup.js',
'src_js/main_logic.js',
'src_js/startup.js',
'src_js/execute.js'
];
<head>
<meta charset="utf-8" />
<link rel="stylesheet" type="text/css" href="webassets/ermac.css" />
<!-- <script type="text/javascript" src="jquery.min.js"></script> -->
<script type="text/javascript" src="https://unpkg.com/[email protected]"></script>
</head>

var head = document.getElementsByTagName('head')[0];
for (var i = 0; i < to_load.length; i++){
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = to_load[i];
script.async = false;
head.appendChild(script);
}
</script>
</body>
</html>
<body class="noscroll">

<script defer type="text/javascript">
// a very simple env loader
var to_load = [
'src_js/wmgui$.js',
'src_js/lemon.js',
'src_js/log.js',
'src_js/clean.js',
// 'src_js/$.js',

'src_js/third_party/jquery.tablesorter.js',
'src_js/third_party/wNumb.js',
'src_js/third_party/nouislider.js',
'src_js/third_party/selectize.js',
'src_js/third_party/selectize_preserve_on_blur.js',
'src_js/third_party/optimade_mpds_nlp.js',
'src_js/wmsettings.js',
'src_js/wmcore.js',
'src_js/sliders.js',
'src_js/third_party/autocomplete.js',
'src_js/router.js',
'src_js/events.js',
'src_js/markup.js',
'src_js/main_logic.js',
'src_js/startup.js',
'src_js/execute.js',
];

var head = document.getElementsByTagName('head')[0];
for (var i = 0; i < to_load.length; i++) {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = to_load[i];
script.async = false;
head.appendChild(script);
}
</script>
</body>

</html>
210 changes: 210 additions & 0 deletions src_js/$.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
(function () {
$l = window.$l = function (object) {
if (typeof object === 'function') {
var functionsToInvokes = functionsToInvokes || [];
functionsToInvokes.push(object);
document.addEventListener('DOMContentLoaded', function () {
for (var i = 0; i < functionsToInvokes.length; i++) {
functionsToInvokes[i]();
}
});
return;
}

if (object instanceof HTMLElement) {
return new DOMNodeCollection([object]); // DOMNodeCollection
}

var selector = object; // a string that represents a selector

var HTMLElements = document.querySelectorAll(selector); // grab all HTMLElements on the page
HTMLElements = nodeCollectionToArray(HTMLElements); // convert from a NodeList to an Array
return new DOMNodeCollection(HTMLElements); // pass into constructor and return a DOMNodeCollection object;
};

$l.ajax = function (options) { // options is an object
var defaultFn = function () { };

var masterOptions = {
success: defaultFn,
error: defaultFn,
url: '',
method: 'GET',
data: null,
contentType: 'application/x-www-form-urlencoded; charset=UTF-8'
};

$l.extend(masterOptions, options);

var success = masterOptions.success;
var error = masterOptions.error;
var url = masterOptions.url;
var method = masterOptions.method;
var data = masterOptions.data;
var contentType = masterOptions.contentType;

var oReq = new XMLHttpRequest();
oReq.addEventListener('load', success);
oReq.addEventListener('error', error);
oReq.open(method, url);
if (data === null) {
oReq.send();
} else {
oReq.send(data);
}

};

// helper function for use within $l.ajax()
$l.extend = function () {
if (arguments.length === 0) {
return new Error('No arguments passed');
}
var master = arguments[0];
for (var i = 1; i < arguments.length; i++) {
var object = arguments[i];
for (prop in object) {
master[prop] = object[prop];
}
}
};

function nodeCollectionToArray(nodeCollection) {
var convertedArr = [];

for (var i = 0; i < nodeCollection.length; i++) {
var node = nodeCollection[i];
convertedArr.push(node);
}
return convertedArr;
}

DOMNodeCollection = window.DOMNodeCollection = function (HTMLElements) {
this.HTMLElements = nodeCollectionToArray(HTMLElements);
};

DOMNodeCollection.prototype.addClass = function (className) {
for (var i = 0; i < this.HTMLElements.length; i++) {
this.HTMLElements[i].classList.add(className);
}
};

DOMNodeCollection.prototype.append = function (object) {
// object is a DOMNodeCollection, HTMLElement, or string
if (object instanceof DOMNodeCollection) {
if (object.HTMLElements.length > 1) {
try {
throw new Error('DOMNodeCollection is greater than one');
} catch (e) {
console.log(e.message);
return;
}
}
}

if (object instanceof HTMLElement) {
this.HTMLElements.forEach(function (HTMLElement, idx, array) {
HTMLElement.appendChild(object);
});
return;
}

this.HTMLElements.forEach(function (HTMLElement, idx, array) {
HTMLElement.innerHTML = object;
});
};

DOMNodeCollection.prototype.attr = function (attrName, value) {
var HTMLElement = this.HTMLElements[0];
if (value === undefined) {
// gets the value of a given attribute for the first node within the DOMNodeCollection
return HTMLElement.getAttribute(attrName);
} else {
// sets the value of a given attribute for the first node within the DOMNodeCollection
return HTMLElement.setAttribute(attrName, value);
}
};

// returns DOMNodeCollection of all children of all nodes within the DOMNodeCollection
DOMNodeCollection.prototype.children = function () {
var children = [];
for (var i = 0; i < this.HTMLElements.length; i++) {
children.push(this.HTMLElements[i].children);
}
return new DOMNodeCollection(children);
};

// returns a DOMNodeCollection of all nodes that match the selector
DOMNodeCollection.prototype.find = function (selector) {
var foundElements = [];
for (var i = 0; i < this.HTMLElements.length; i++) {
var HTMLElement = this.HTMLElements[i];
var HTMLElements = HTMLElement.querySelectorAll(selector);

// move HTMLElements from NodeList to an array.
for (var j = 0; j < HTMLElements.length; j++) {
foundElements.push(HTMLElements[i]);
}
}

return new DOMNodeCollection(foundElements);
};

// clears all HTML from all nodes within the DOMNodeCollection
DOMNodeCollection.prototype.empty = function () {
this.HTMLElements.forEach(function (HTMLElement, idx, array) {
HTMLElement.innerHTML = '';
});
};

// replaces the HTML property of all nodes within the DOMNodeCollection
DOMNodeCollection.prototype.html = function (str) {
if (str === undefined) {
return this.HTMLElements[0].innerHTML;
}

this.HTMLElements.forEach(function (HTMLElement, idx, array) {
HTMLElement.innerHTML = str;
});
};

// removes the eventListener from all nodes within the DOMNodeCollection
DOMNodeCollection.prototype.off = function (eventType, callback) {
for (var i = 0; i < this.HTMLElements.length; i++) {
this.HTMLElements[i].removeEventListener(eventType, callback);
}
};

// adds an eventListener to all nodes within the DOMNodeCollection
DOMNodeCollection.prototype.on = function (eventType, callback) {
for (var i = 0; i < this.HTMLElements.length; i++) {
this.HTMLElements[i].addEventListener(eventType, callback);
}
};

// returns collection of all parents of all nodes within the DOMNodeCollection
DOMNodeCollection.prototype.parent = function () {
var parents = [];
for (var i = 0; i < this.HTMLElements.length; i++) {
var HTMLElement = this.HTMLElements[i];
parents.push(HTMLElement.parentElement);
}

return new DOMNodeCollection(parents);
};

// removes all nodes within the DOMNodeCollection from the DOM
DOMNodeCollection.prototype.remove = function () {
for (var i = 0; i < this.HTMLElements.length; i++) {
var HTMLElement = this.HTMLElements[i];
HTMLElement.parentNode.removeChild(HTMLElement);
}
};

// removes a class from all of the nodes within the DOMNodeCollection
DOMNodeCollection.prototype.removeClass = function (className) {
for (var i = 0; i < this.HTMLElements.length; i++) {
this.HTMLElements[i].classList.remove(className);
}
};
})();
85 changes: 85 additions & 0 deletions src_js/clean.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*!
* Sanitize an HTML string
* (c) 2021 Chris Ferdinandi, MIT License, https://gomakethings.com
* @param {String} str The HTML string to sanitize
* @param {Boolean} nodes If true, returns HTML nodes instead of a string
* @return {String|NodeList} The sanitized string or nodes
*/
wmgui.clean = function (string, nodes) {

/**
* Convert the string to an HTML document
* @return {Node} An HTML document
*/
function stringToHTML() {
let parser = new DOMParser();
let doc = parser.parseFromString(string, 'text/html');
return doc.body || document.createElement('body');
}

/**
* Remove <script> elements
* @param {Node} html The HTML
*/
function removeScripts(html) {
let scripts = html.querySelectorAll('script');
for (let script of scripts) {
script.remove();
}
}

/**
* Check if the attribute is potentially dangerous
* @param {String} name The attribute name
* @param {String} value The attribute value
* @return {Boolean} If true, the attribute is potentially dangerous
*/
function isPossiblyDangerous(name, value) {
let val = value.replace(/\s+/g, '').toLowerCase();
if (['src', 'href', 'xlink:href'].includes(name)) {
if (val.includes('javascript:') || val.includes('data:')) return true;
}
if (name.startsWith('on')) return true;
}

/**
* Remove potentially dangerous attributes from an element
* @param {Node} elem The element
*/
function removeAttributes(elem) {

// Loop through each attribute
// If it's dangerous, remove it
let atts = elem.attributes;

for (let { name, value } of atts) {
if (!isPossiblyDangerous(name, value)) continue;
elem.removeAttribute(name);
}

}

/**
* Remove dangerous stuff from the HTML document's nodes
* @param {Node} html The HTML document
*/
function clean(html) {
let nodes = html.children;
for (let node of nodes) {
removeAttributes(node);
clean(node);
}
}

// Convert the string to HTML
let html = stringToHTML();

// Sanitize it
removeScripts(html);
clean(html);

// If the user wants HTML nodes back, return them
// Otherwise, pass a sanitized string back
return nodes ? html.childNodes : html.innerHTML;

};
Loading