UNPKG

@xeokit/xeokit-sdk

Version:

3D BIM IFC Viewer SDK for AEC engineering applications. Open Source JavaScript Toolkit based on pure WebGL for top performance, real-world coordinates and full double precision

454 lines (415 loc) 11.2 kB
/** * @private */ import {core} from "./core.js"; function xmlToJson(node, attributeRenamer) { if (node.nodeType === node.TEXT_NODE) { var v = node.nodeValue; if (v.match(/^\s+$/) === null) { return v; } } else if (node.nodeType === node.ELEMENT_NODE || node.nodeType === node.DOCUMENT_NODE) { var json = {type: node.nodeName, children: []}; if (node.nodeType === node.ELEMENT_NODE) { for (var j = 0; j < node.attributes.length; j++) { var attribute = node.attributes[j]; var nm = attributeRenamer[attribute.nodeName] || attribute.nodeName; json[nm] = attribute.nodeValue; } } for (var i = 0; i < node.childNodes.length; i++) { var item = node.childNodes[i]; var j = xmlToJson(item, attributeRenamer); if (j) json.children.push(j); } return json; } } /** * @private */ function clone(ob) { return JSON.parse(JSON.stringify(ob)); } /** * @private */ var guidChars = [["0", 10], ["A", 26], ["a", 26], ["_", 1], ["$", 1]].map(function (a) { var li = []; var st = a[0].charCodeAt(0); var en = st + a[1]; for (var i = st; i < en; ++i) { li.push(i); } return String.fromCharCode.apply(null, li); }).join(""); /** * @private */ function b64(v, len) { var r = (!len || len === 4) ? [0, 6, 12, 18] : [0, 6]; return r.map(function (i) { return guidChars.substr(parseInt(v / (1 << i)) % 64, 1) }).reverse().join(""); } /** * @private */ function compressGuid(g) { var bs = [0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30].map(function (i) { return parseInt(g.substr(i, 2), 16); }); return b64(bs[0], 2) + [1, 4, 7, 10, 13].map(function (i) { return b64((bs[i] << 16) + (bs[i + 1] << 8) + bs[i + 2]); }).join(""); } /** * @private */ function findNodeOfType(m, t) { var li = []; var _ = function (n) { if (n.type === t) li.push(n); (n.children || []).forEach(function (c) { _(c); }); }; _(m); return li; } /** * @private */ function timeout(dt) { return new Promise(function (resolve, reject) { setTimeout(resolve, dt); }); } /** * @private */ function httpRequest(args) { return new Promise(function (resolve, reject) { var xhr = new XMLHttpRequest(); xhr.open(args.method || "GET", args.url, true); xhr.onload = function (e) { if (xhr.readyState === 4) { if (xhr.status === 200) { resolve(xhr.responseXML); } else { reject(xhr.statusText); } } }; xhr.send(null); }); } /** * @private */ const queryString = function () { // This function is anonymous, is executed immediately and // the return value is assigned to QueryString! var query_string = {}; var query = window.location.search.substring(1); var vars = query.split("&"); for (var i = 0; i < vars.length; i++) { var pair = vars[i].split("="); // If first entry with this name if (typeof query_string[pair[0]] === "undefined") { query_string[pair[0]] = decodeURIComponent(pair[1]); // If second entry with this name } else if (typeof query_string[pair[0]] === "string") { var arr = [query_string[pair[0]], decodeURIComponent(pair[1])]; query_string[pair[0]] = arr; // If third or later entry with this name } else { query_string[pair[0]].push(decodeURIComponent(pair[1])); } } return query_string; }(); /** * @private */ function loadJSON(url, ok, err) { // Avoid checking ok and err on each use. var defaultCallback = (_value) => undefined; ok = ok || defaultCallback; err = err || defaultCallback; var request = new XMLHttpRequest(); request.overrideMimeType("application/json"); request.open('GET', url, true); request.addEventListener('load', function (event) { var response = event.target.response; if (this.status === 200) { var json; try { json = JSON.parse(response); } catch (e) { err(`utils.loadJSON(): Failed to parse JSON response - ${e}`); } ok(json); } else if (this.status === 0) { // Some browsers return HTTP Status 0 when using non-http protocol // e.g. 'file://' or 'data://'. Handle as success. console.warn('loadFile: HTTP Status 0 received.'); try { ok(JSON.parse(response)); } catch (e) { err(`utils.loadJSON(): Failed to parse JSON response - ${e}`); } } else { err(event); } }, false); request.addEventListener('error', function (event) { err(event); }, false); request.send(null); } /** * @private */ function loadArraybuffer(url, ok, err) { // Check for data: URI var defaultCallback = (_value) => undefined; ok = ok || defaultCallback; err = err || defaultCallback; const dataUriRegex = /^data:(.*?)(;base64)?,(.*)$/; const dataUriRegexResult = url.match(dataUriRegex); if (dataUriRegexResult) { // Safari can't handle data URIs through XMLHttpRequest const isBase64 = !!dataUriRegexResult[2]; var data = dataUriRegexResult[3]; data = window.decodeURIComponent(data); if (isBase64) { data = window.atob(data); } try { const buffer = new ArrayBuffer(data.length); const view = new Uint8Array(buffer); for (var i = 0; i < data.length; i++) { view[i] = data.charCodeAt(i); } core.scheduleTask(() => { ok(buffer); }); } catch (error) { core.scheduleTask(() => { err(error); }); } } else { const request = new XMLHttpRequest(); request.open('GET', url, true); request.responseType = 'arraybuffer'; request.onreadystatechange = function () { if (request.readyState === 4) { if (request.status === 200) { ok(request.response); } else { err('loadArrayBuffer error : ' + request.response); } } }; request.send(null); } } /** Tests if the given object is an array @private */ function isArray(value) { return value && !(value.propertyIsEnumerable('length')) && typeof value === 'object' && typeof value.length === 'number'; } /** Tests if the given value is a string @param value @returns {Boolean} @private */ function isString(value) { return (typeof value === 'string' || value instanceof String); } /** Tests if the given value is a number @param value @returns {Boolean} @private */ function isNumeric(value) { return !isNaN(parseFloat(value)) && isFinite(value); } /** Tests if the given value is an ID @param value @returns {Boolean} @private */ function isID(value) { return utils.isString(value) || utils.isNumeric(value); } /** Tests if the given components are the same, where the components can be either IDs or instances. @param c1 @param c2 @returns {Boolean} @private */ function isSameComponent(c1, c2) { if (!c1 || !c2) { return false; } const id1 = (utils.isNumeric(c1) || utils.isString(c1)) ? `${c1}` : c1.id; const id2 = (utils.isNumeric(c2) || utils.isString(c2)) ? `${c2}` : c2.id; return id1 === id2; } /** Tests if the given value is a function @param value @returns {Boolean} @private */ function isFunction(value) { return (typeof value === "function"); } /** Tests if the given value is a JavaScript JSON object, eg, ````{ foo: "bar" }````. @param value @returns {Boolean} @private */ function isObject(value) { const objectConstructor = {}.constructor; return (!!value && value.constructor === objectConstructor); } /** Returns a shallow copy */ function copy(o) { return utils.apply(o, {}); } /** Add properties of o to o2, overwriting them on o2 if already there */ function apply(o, o2) { for (const name in o) { if (o.hasOwnProperty(name)) { o2[name] = o[name]; } } return o2; } /** Add non-null/defined properties of o to o2 @private */ function apply2(o, o2) { for (const name in o) { if (o.hasOwnProperty(name)) { if (o[name] !== undefined && o[name] !== null) { o2[name] = o[name]; } } } return o2; } /** Add properties of o to o2 where undefined or null on o2 @private */ function applyIf(o, o2) { for (const name in o) { if (o.hasOwnProperty(name)) { if (o2[name] === undefined || o2[name] === null) { o2[name] = o[name]; } } } return o2; } /** Returns true if the given map is empty. @param obj @returns {Boolean} @private */ function isEmptyObject(obj) { for (const name in obj) { if (obj.hasOwnProperty(name)) { return false; } } return true; } /** Returns the given ID as a string, in quotes if the ID was a string to begin with. This is useful for logging IDs. @param {Number| String} id The ID @returns {String} @private */ function inQuotes(id) { return utils.isNumeric(id) ? (`${id}`) : (`'${id}'`); } /** Returns the concatenation of two typed arrays. @param a @param b @returns {*|a} @private */ function concat(a, b) { const c = new a.constructor(a.length + b.length); c.set(a); c.set(b, a.length); return c; } function flattenParentChildHierarchy(root) { var list = []; function visit(node) { node.id = node.uuid; delete node.oid; list.push(node); var children = node.children; if (children) { for (var i = 0, len = children.length; i < len; i++) { const child = children[i]; child.parent = node.id; visit(children[i]); } } node.children = []; } visit(root); return list; } /** * @private */ const utils = { xmlToJson: xmlToJson, clone: clone, compressGuid: compressGuid, findNodeOfType: findNodeOfType, timeout: timeout, httpRequest: httpRequest, loadJSON: loadJSON, loadArraybuffer: loadArraybuffer, queryString: queryString, isArray: isArray, isString: isString, isNumeric: isNumeric, isID: isID, isSameComponent: isSameComponent, isFunction: isFunction, isObject: isObject, copy: copy, apply: apply, apply2: apply2, applyIf: applyIf, isEmptyObject: isEmptyObject, inQuotes: inQuotes, concat: concat, flattenParentChildHierarchy: flattenParentChildHierarchy }; export {utils};