├── .babelrc ├── .classpath ├── .gitignore ├── .project ├── .settings ├── org.eclipse.core.resources.prefs ├── org.eclipse.jdt.core.prefs ├── org.eclipse.m2e.core.prefs └── org.eclipse.wst.common.project.facet.core.xml ├── Gruntfile.js ├── README.md ├── bimserverapipromise.js ├── bimserverapiwebsocket.js ├── bimserverclient.js ├── geometry.js ├── ifc2x3tc1.js ├── ifc4.js ├── index.html ├── license.txt ├── model.js ├── package-lock.json ├── package.json ├── plugin ├── icon.png ├── plugin.xml └── version.properties ├── pom.xml ├── rollup.config.js ├── tonicExample.js └── translations_en.js /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "env", 5 | { 6 | "modules": false 7 | } 8 | ] 9 | ], 10 | "plugins": [ 11 | "external-helpers" 12 | ] 13 | } -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /bin/ 2 | /target/ 3 | /node_modules/ 4 | /build/ -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | JavaScriptApi 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.wst.common.project.facet.core.builder 15 | 16 | 17 | 18 | 19 | org.eclipse.m2e.core.maven2Builder 20 | 21 | 22 | 23 | 24 | 25 | org.eclipse.jdt.core.javanature 26 | org.eclipse.m2e.core.maven2Nature 27 | org.eclipse.wst.common.project.facet.core.nature 28 | 29 | 30 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding/=UTF-8 3 | encoding/plugin=UTF-8 4 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 3 | org.eclipse.jdt.core.compiler.compliance=1.5 4 | org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled 5 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 6 | org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=ignore 7 | org.eclipse.jdt.core.compiler.release=disabled 8 | org.eclipse.jdt.core.compiler.source=1.5 9 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.project.facet.core.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | module.exports = function(grunt) { 2 | 3 | grunt.initConfig({ 4 | pkg : grunt.file.readJSON("package.json"), 5 | concat : { 6 | js : { 7 | files : { 8 | "output/bimserverapi-%VERSION%.js" : [ "bimserverapipromise.js", "bimserverapiwebsocket.js", "geometry.js", "ifc4.js", "ifc2x3tc1.js", "model.js", "translations_en.js", "bimserverclient.js" ] 9 | }, 10 | } 11 | }, 12 | uglify : { 13 | dist : { 14 | files : { 15 | "output/bimserverapi-%VERSION%.min.js" : [ "output/bimserverapi-%VERSION%.js" ] 16 | } 17 | } 18 | }, 19 | copy : { 20 | main : { 21 | files : [{ 22 | expand : true, 23 | src : [ "plugin/*" ], 24 | dest : "output/" 25 | }] 26 | } 27 | }, 28 | zip: { 29 | "output/bimserverapi-source-%VERSION%.zip": ["bimserverapipromise.js", "bimserverapiwebsocket.js", "geometry.js", "ifc4.js", "ifc2x3tc1.js", "model.js", "translations_en.js", "bimserverclient.js"] 30 | } 31 | }); 32 | 33 | grunt.loadNpmTasks("grunt-contrib-concat"); 34 | grunt.loadNpmTasks("grunt-contrib-uglify"); 35 | grunt.loadNpmTasks("grunt-zip"); 36 | 37 | grunt.registerTask("default", [ "concat", "uglify"]); 38 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # BIMserver-JavaScript-API 2 | 3 | A JavaScript API for the OpenSource BIMserver. 4 | 5 | ## Usage 6 | ### Static import 7 | Download the [combined minified library](https://raw.githubusercontent.com/opensourceBIM/BIMserver-JavaScript-API/master/build/bimserverapi.js) and include it in your HTML. 8 | 9 | ```html 10 | 11 | ``` 12 | 13 | The ``?_v=%VERSION%`` addition is there for efficient caching purposes. Any server system serving these files can tell the client to cache these files indefinitely. 14 | 15 | ### Import as an ES6 module 16 | If you are using a transpiler such as [Typescript](https://www.typescriptlang.org/) or [Babel](http://babeljs.io/), or a bundler such as [Webpack](https://webpack.js.org/) or [Rollup](https://rollupjs.org/), you can import the module using the [Import](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import) syntax. 17 | 18 | It is also possible to load the ES6 module directly into the browser, however not all browser support this yet. 19 | 20 | ```javascript 21 | import { BimServerClient } from './js/bimserverclient.js'; 22 | const api = new BimServerClient("../.."); 23 | api.init((client, version) => { 24 | console.log(version.version); 25 | }); 26 | ``` 27 | 28 | ### Dynamic import 29 | When the location on the API is not known in advance, you can use dynamic loading, in most browsers you'll need to use a "dev" version for this to work (Chrome 64 for example). 30 | 31 | ```javascript 32 | var address = "http://addressofapi"; 33 | Promise.all([ 34 | address + "/bimserverclient.js", 35 | address + "/bimserverapipromise.js" 36 | ].map(x => import(x))) 37 | .then(([BimServerClient, BimServerApiPromise]) => { 38 | var api = new BimServerClient.BimServerClient("../.."); 39 | api.init((client, version) => { 40 | document.getElementById("version").innerHTML = JSON.stringify(version.version, 0, 2); 41 | }); 42 | }); 43 | ``` 44 | 45 | ### Version returned from BIMserver instance 46 | To do. 47 | 48 | ## API documentation 49 | To do. 50 | 51 | ## Build the library 52 | * Install [Node.js](https://nodejs.org/) 53 | * Clone (or download and unzip) the project to your file system: 54 | ``` 55 | git clone https://github.com/opensourceBIM/BIMserver-JavaScript-API.git 56 | ``` 57 | * Go to the project directory 58 | ``` 59 | cd BIMserver-JavaScript-API 60 | ``` 61 | * Install build dependencies 62 | ``` 63 | npm install 64 | ``` 65 | * Run the build script 66 | ``` 67 | npm run build 68 | ``` 69 | The compiled file is located at ``build/bimserverapi.js`` 70 | -------------------------------------------------------------------------------- /bimserverapipromise.js: -------------------------------------------------------------------------------- 1 | export class BimServerApiPromise { 2 | constructor(counter = null) { 3 | this.isDone = false; 4 | this.chains = []; 5 | this.callback = null; 6 | this.counter = counter; 7 | } 8 | 9 | done(callback) { 10 | if (this.isDone) { 11 | callback(); 12 | } else { 13 | if (this.callback != null) { 14 | if (this.callback instanceof Array) { 15 | this.callback.push(callback); 16 | } else { 17 | this.callback = [this.callback, callback]; 18 | } 19 | } else { 20 | this.callback = callback; 21 | } 22 | } 23 | return this; 24 | } 25 | 26 | inc() { 27 | if (this.counter == null) { 28 | this.counter = 0; 29 | } 30 | this.counter++; 31 | } 32 | 33 | dec() { 34 | if (this.counter == null) { 35 | this.counter = 0; 36 | } 37 | this.counter--; 38 | if (this.counter === 0) { 39 | this.done = true; 40 | this.fire(); 41 | } 42 | } 43 | 44 | fire() { 45 | if (this.isDone) { 46 | console.log("Promise already fired, not triggering again..."); 47 | return; 48 | } 49 | this.isDone = true; 50 | if (this.callback != null) { 51 | if (this.callback instanceof Array) { 52 | this.callback.forEach((cb) => { 53 | cb(); 54 | }); 55 | } else { 56 | this.callback(); 57 | } 58 | } 59 | } 60 | 61 | chain(otherPromise) { 62 | let promises; 63 | if (otherPromise instanceof Array) { 64 | promises = otherPromise; 65 | } else { 66 | promises = [otherPromise]; 67 | } 68 | promises.forEach((promise) => { 69 | if (!promise.isDone) { 70 | this.chains.push(promise); 71 | promise.done(() => { 72 | for (let i = this.chains.length - 1; i >= 0; i--) { 73 | if (this.chains[i] == promise) { 74 | this.chains.splice(i, 1); 75 | } 76 | } 77 | if (this.chains.length === 0) { 78 | this.fire(); 79 | } 80 | }); 81 | } 82 | }); 83 | if (this.chains.length === 0) { 84 | this.fire(); 85 | } 86 | } 87 | } -------------------------------------------------------------------------------- /bimserverapiwebsocket.js: -------------------------------------------------------------------------------- 1 | export class BimServerApiWebSocket { 2 | constructor(baseUrl, bimServerApi) { 3 | this.connected = false; 4 | this.openCallbacks = []; 5 | this.endPointId = null; 6 | this.listener = null; 7 | this.tosend = []; 8 | this.tosendAfterConnect = []; 9 | this.messagesReceived = 0; 10 | this.intervalId = null; 11 | this.baseUrl = baseUrl; 12 | this.bimServerApi = bimServerApi; 13 | } 14 | 15 | connect(callback = null) { 16 | if (this.connected) { 17 | if (callback != null) { 18 | callback(); 19 | } 20 | return Promise.resolve(); 21 | } 22 | console.info("Connecting websocket"); 23 | var promise = new Promise((resolve, reject) => { 24 | this.openCallbacks.push(() => { 25 | resolve(); 26 | }); 27 | if (callback != null) { 28 | if (typeof callback === "function") { 29 | this.openCallbacks.push(callback); 30 | } else { 31 | console.error("Callback was not a function", callback); 32 | } 33 | } 34 | 35 | // Concatenate in case of relative URL 36 | let hostname = this.bimServerApi.baseUrl.toString(); 37 | if (!hostname.startsWith("http:") && !hostname.startsWith("https:")) { 38 | if (hostname.startsWith('//')) { 39 | hostname = window.location.protocol + hostname; 40 | } else if (hostname.startsWith('/')) { 41 | hostname = window.location.origin + hostname; 42 | } else { 43 | hostname = window.location.href + hostname; 44 | } 45 | } 46 | 47 | const location = hostname.replace('http://', 'ws://').replace('https://', 'wss://') + "/stream"; 48 | 49 | try { 50 | this._ws = new WebSocket(location); 51 | this._ws.binaryType = "arraybuffer"; 52 | this._ws.onopen = this._onopen.bind(this); 53 | this._ws.onmessage = this._onmessage.bind(this); 54 | this._ws.onclose = this._onclose.bind(this); 55 | this._ws.onerror = this._onerror.bind(this); 56 | } catch (err) { 57 | console.error(err); 58 | this.bimServerApi.notifier.setError("WebSocket error" + (err.message !== undefined ? (": " + err.message) : "")); 59 | } 60 | }); 61 | return promise; 62 | } 63 | 64 | _onerror(err) { 65 | console.log(err); 66 | this.bimServerApi.notifier.setError("WebSocket error" + (err.message !== undefined ? (": " + err.message) : "")); 67 | } 68 | 69 | _onopen() { 70 | this.intervalId = setInterval(() => { 71 | this.send({"hb": true}); 72 | }, 30 * 1000); // Send hb every 30 seconds 73 | while (this.tosendAfterConnect.length > 0 && this._ws.readyState == 1) { 74 | const messageArray = this.tosendAfterConnect.splice(0, 1); 75 | this._sendWithoutEndPoint(messageArray[0]); 76 | } 77 | } 78 | 79 | _sendWithoutEndPoint(message) { 80 | if (this._ws && this._ws.readyState == 1) { 81 | this._ws.send(message); 82 | } else { 83 | this.tosendAfterConnect.push(message); 84 | } 85 | } 86 | 87 | _send(message) { 88 | if (this._ws && this._ws.readyState == 1 && this.endPointId != null) { 89 | this._ws.send(message); 90 | } else { 91 | console.log("Waiting", message); 92 | this.tosend.push(message); 93 | } 94 | } 95 | 96 | send(object) { 97 | const str = JSON.stringify(object); 98 | this.bimServerApi.log("Sending", str); 99 | this._send(str); 100 | } 101 | 102 | _onmessage(message) { 103 | this.messagesReceived++; 104 | if (this.messagesReceived % 10 === 0) { 105 | // console.log(this.messagesReceived); 106 | } 107 | if (message.data instanceof ArrayBuffer) { 108 | this.listener(message.data); 109 | } else { 110 | const incomingMessage = JSON.parse(message.data); 111 | if (incomingMessage.id != null) { 112 | var id = incomingMessage.id; 113 | if (this.bimServerApi.websocketCalls.has(id)) { 114 | var fn = this.bimServerApi.websocketCalls.get(id); 115 | fn(incomingMessage); 116 | this.bimServerApi.websocketCalls.delete(id); 117 | } 118 | } else { 119 | this.bimServerApi.log("incoming", incomingMessage); 120 | if (incomingMessage.welcome !== undefined) { 121 | this._sendWithoutEndPoint(JSON.stringify({"token": this.bimServerApi.token})); 122 | } else if (incomingMessage.endpointid !== undefined) { 123 | this.endPointId = incomingMessage.endpointid; 124 | this.connected = true; 125 | this.openCallbacks.forEach((callback) => { 126 | callback(); 127 | }); 128 | while (this.tosend.length > 0 && this._ws.readyState == 1) { 129 | const messageArray = this.tosend.splice(0, 1); 130 | console.log(messageArray[0]); 131 | this._send(messageArray[0]); 132 | } 133 | this.openCallbacks = []; 134 | } else { 135 | if (incomingMessage.request !== undefined) { 136 | this.listener(incomingMessage.request); 137 | } else if (incomingMessage.requests !== undefined) { 138 | incomingMessage.requests.forEach((request) => { 139 | this.listener(request); 140 | }); 141 | } 142 | } 143 | } 144 | } 145 | } 146 | 147 | _onclose(m) { 148 | console.log("WebSocket closed", m); 149 | clearInterval(this.intervalId); 150 | this._ws = null; 151 | this.connected = false; 152 | this.openCallbacks = []; 153 | this.endpointid = null; 154 | } 155 | } -------------------------------------------------------------------------------- /bimserverclient.js: -------------------------------------------------------------------------------- 1 | import {BimServerApiPromise} from './bimserverapipromise.js'; 2 | import {BimServerApiWebSocket} from './bimserverapiwebsocket.js'; 3 | import {geometry} from './geometry.js'; 4 | import {ifc2x3tc1} from './ifc2x3tc1.js'; 5 | import {ifc4} from './ifc4.js'; 6 | import {Model} from './model.js'; 7 | import {translations} from './translations_en.js'; 8 | 9 | //export { default as BimServerApiPromise } from './bimserverapipromise.js'; 10 | //export { default as BimServerApiWebSocket } from './bimserverapiwebsocket.js'; 11 | //export { default as Model } from './model.js'; 12 | 13 | //import {XMLHttpRequest from 'xhr2'; 14 | 15 | // Where does this come frome? The API crashes on the absence of this 16 | // member function? 17 | String.prototype.firstUpper = function () { 18 | return this.charAt(0).toUpperCase() + this.slice(1); 19 | }; 20 | 21 | export class BimServerClient { 22 | constructor(baseUrl, notifier = null, translate = null) { 23 | this.interfaceMapping = { 24 | "ServiceInterface": "org.bimserver.ServiceInterface", 25 | "NewServicesInterface": "org.bimserver.NewServicesInterface", 26 | "AuthInterface": "org.bimserver.AuthInterface", 27 | "OAuthInterface": "org.bimserver.OAuthInterface", 28 | "SettingsInterface": "org.bimserver.SettingsInterface", 29 | "AdminInterface": "org.bimserver.AdminInterface", 30 | "PluginInterface": "org.bimserver.PluginInterface", 31 | "MetaInterface": "org.bimserver.MetaInterface", 32 | "LowLevelInterface": "org.bimserver.LowLevelInterface", 33 | "NotificationRegistryInterface": "org.bimserver.NotificationRegistryInterface", 34 | }; 35 | 36 | // translate function override 37 | this.translateOverride = translate; 38 | 39 | // Current BIMserver token 40 | this.token = null; 41 | 42 | // Base URL of the BIMserver 43 | this.baseUrl = baseUrl; 44 | if (this.baseUrl.substring(this.baseUrl.length - 1) == "/") { 45 | this.baseUrl = this.baseUrl.substring(0, this.baseUrl.length - 1); 46 | } 47 | 48 | // JSON endpoint on BIMserver 49 | this.address = this.baseUrl + "/json"; 50 | 51 | // Notifier, default implementation does nothing 52 | this.notifier = notifier; 53 | if (this.notifier == null) { 54 | this.notifier = { 55 | setInfo: function (message) { 56 | console.log("[default]", message); 57 | }, 58 | setSuccess: function () {}, 59 | setError: function () {}, 60 | resetStatus: function () {}, 61 | resetStatusQuick: function () {}, 62 | clear: function () {} 63 | }; 64 | } 65 | 66 | // ID -> Resolve method 67 | this.websocketCalls = new Map(); 68 | 69 | // The websocket client 70 | this.webSocket = new BimServerApiWebSocket(baseUrl, this); 71 | this.webSocket.listener = this.processNotification.bind(this); 72 | 73 | // Cached user object 74 | this.user = null; 75 | 76 | // Keeps track of the unique ID's required to handle websocket calls that return something 77 | this.idCounter = 0; 78 | 79 | this.listeners = {}; 80 | 81 | // this.autoLoginTried = false; 82 | 83 | // Cache for serializers, PluginClassName(String) -> Serializer 84 | this.serializersByPluginClassName = []; 85 | 86 | // Whether debugging is enabled, just a lot more logging 87 | this.debug = false; 88 | 89 | // Mapping from ChannelId -> Listener (function) 90 | this.binaryDataListener = {}; 91 | 92 | // This mapping keeps track of the prototype objects per class, will be lazily popuplated by the getClass method 93 | this.classes = {}; 94 | 95 | // Schema name (String) -> Schema 96 | this.schemas = {}; 97 | } 98 | 99 | init(callback) { 100 | var promise = new Promise((resolve, reject) => { 101 | this.call("AdminInterface", "getServerInfo", {}, (serverInfo) => { 102 | this.version = serverInfo.version; 103 | //const versionString = this.version.major + "." + this.version.minor + "." + this.version.revision; 104 | 105 | this.schemas.geometry = geometry.classes; 106 | this.addSubtypesToSchema(this.schemas.geometry); 107 | 108 | this.schemas.ifc2x3tc1 = ifc2x3tc1.classes; 109 | this.addSubtypesToSchema(this.schemas.ifc2x3tc1); 110 | 111 | this.schemas.ifc4 = ifc4.classes; 112 | this.addSubtypesToSchema(this.schemas.ifc4); 113 | 114 | if (callback != null) { 115 | callback(this, serverInfo); 116 | } 117 | resolve(serverInfo); 118 | }, (error) => { reject(error); }); 119 | }); 120 | return promise; 121 | } 122 | 123 | addSubtypesToSchema(classes) { 124 | for (let typeName in classes) { 125 | const type = classes[typeName]; 126 | if (type.superclasses != null) { 127 | type.superclasses.forEach((superClass) => { 128 | let directSubClasses = classes[superClass].directSubClasses; 129 | if (directSubClasses == null) { 130 | directSubClasses = []; 131 | classes[superClass].directSubClasses = directSubClasses; 132 | } 133 | directSubClasses.push(typeName); 134 | }); 135 | } 136 | } 137 | } 138 | 139 | getAllSubTypes(schema, typeName, callback) { 140 | const type = schema[typeName]; 141 | if (type.directSubClasses != null) { 142 | type.directSubClasses.forEach((subTypeName) => { 143 | callback(subTypeName); 144 | this.getAllSubTypes(schema, subTypeName, callback); 145 | }); 146 | } 147 | } 148 | 149 | log(message, message2) { 150 | if (this.debug) { 151 | console.log(message, message2); 152 | } 153 | } 154 | 155 | translate(key) { 156 | if (this.translateOverride !== null) { 157 | return this.translateOverride(key); 158 | } 159 | key = key.toUpperCase(); 160 | if (translations != null) { 161 | const translated = translations[key]; 162 | if (translated == null) { 163 | console.warn("translation for " + key + " not found, using key"); 164 | return key; 165 | } 166 | return translated; 167 | } 168 | this.error("no translations"); 169 | return key; 170 | } 171 | 172 | login(username, password, callback, errorCallback, options) { 173 | if (options == null) { 174 | options = {}; 175 | } 176 | const request = { 177 | username: username, 178 | password: password 179 | }; 180 | this.call("AuthInterface", "login", request, (data) => { 181 | this.token = data; 182 | if (options.done !== false) { 183 | this.notifier.setInfo(this.translate("LOGIN_DONE"), 2000); 184 | } 185 | this.resolveUser(callback); 186 | }, errorCallback, options.busy === false ? false : true, options.done === false ? false : true, options.error === false ? false : true); 187 | } 188 | 189 | downloadViaWebsocket(msg) { 190 | msg.action = "download"; 191 | msg.token = this.token; 192 | this.webSocket.send(msg); 193 | } 194 | 195 | setBinaryDataListener(topicId, listener) { 196 | this.binaryDataListener[topicId] = listener; 197 | } 198 | 199 | clearBinaryDataListener(topicId) { 200 | delete this.binaryDataListener[topicId]; 201 | } 202 | 203 | processNotification(message) { 204 | if (message instanceof ArrayBuffer) { 205 | if (message == null || message.byteLength == 0) { 206 | return; 207 | } 208 | const view = new DataView(message, 0, 8); 209 | const topicId = view.getUint32(0, true) + 0x100000000 * view.getUint32(4, true); // TopicId's are of type long (64 bit) 210 | const listener = this.binaryDataListener[topicId]; 211 | if (listener != null) { 212 | listener(message); 213 | } else { 214 | console.error("No listener for topicId", topicId, message); 215 | } 216 | } else { 217 | const intf = message["interface"]; 218 | if (this.listeners[intf] != null) { 219 | if (this.listeners[intf][message.method] != null) { 220 | let ar = null; 221 | this.listeners[intf][message.method].forEach((listener) => { 222 | if (ar == null) { 223 | // Only parse the arguments once, or when there are no listeners, not even once 224 | ar = []; 225 | let i = 0; 226 | for (let key in message.parameters) { 227 | ar[i++] = message.parameters[key]; 228 | } 229 | } 230 | listener.apply(null, ar); 231 | }); 232 | } else { 233 | console.log("No listeners on interface " + intf + " for method " + message.method); 234 | } 235 | } else { 236 | console.log("No listeners for interface " + intf); 237 | } 238 | } 239 | } 240 | 241 | resolveUser(callback) { 242 | this.call("AuthInterface", "getLoggedInUser", {}, (data) => { 243 | this.user = data; 244 | if (callback != null) { 245 | callback(this.user); 246 | } 247 | }); 248 | } 249 | 250 | logout(callback) { 251 | this.call("AuthInterface", "logout", {}, () => { 252 | this.notifier.setInfo(this.translate("LOGOUT_DONE")); 253 | callback(); 254 | }); 255 | } 256 | 257 | generateRevisionDownloadUrl(settings) { 258 | return this.baseUrl + "/download?token=" + this.token + (settings.zip ? "&zip=on" : "") + "&topicId=" + settings.topicId; 259 | } 260 | 261 | generateExtendedDataDownloadUrl(edid) { 262 | return this.baseUrl + "/download?token=" + this.token + "&action=extendeddata&edid=" + edid; 263 | } 264 | 265 | getJsonSerializer(callback) { 266 | this.getSerializerByPluginClassName("org.bimserver.serializers.JsonSerializerPlugin").then((serializer) => { 267 | callback(serializer); 268 | }); 269 | } 270 | 271 | getJsonStreamingSerializer(callback) { 272 | this.getSerializerByPluginClassName("org.bimserver.serializers.JsonStreamingSerializerPlugin").then((serializer) => { 273 | callback(serializer); 274 | }); 275 | } 276 | 277 | getMinimalJsonStreamingSerializer(callback) { 278 | this.getSerializerByPluginClassName("org.bimserver.serializers.MinimalJsonStreamingSerializerPlugin").then((serializer) => { 279 | callback(serializer); 280 | }); 281 | } 282 | 283 | getSerializerByPluginClassName(pluginClassName) { 284 | if (this.serializersByPluginClassName[pluginClassName] != null) { 285 | return this.serializersByPluginClassName[pluginClassName]; 286 | } else { 287 | var promise = new Promise((resolve, reject) => { 288 | this.call("PluginInterface", "getSerializerByPluginClassName", { 289 | pluginClassName: pluginClassName 290 | }, (serializer) => { 291 | resolve(serializer); 292 | }); 293 | }); 294 | 295 | this.serializersByPluginClassName[pluginClassName] = promise; 296 | 297 | return promise; 298 | } 299 | } 300 | 301 | getMessagingSerializerByPluginClassName(pluginClassName, callback) { 302 | if (this.serializersByPluginClassName[pluginClassName] == null) { 303 | this.call("PluginInterface", "getMessagingSerializerByPluginClassName", { 304 | pluginClassName: pluginClassName 305 | }, (serializer) => { 306 | this.serializersByPluginClassName[pluginClassName] = serializer; 307 | callback(serializer); 308 | }); 309 | } else { 310 | callback(this.serializersByPluginClassName[pluginClassName]); 311 | } 312 | } 313 | 314 | register(interfaceName, methodName, callback, registerCallback) { 315 | if (callback == null) { 316 | throw "Cannot register null callback"; 317 | } 318 | if (this.listeners[interfaceName] == null) { 319 | this.listeners[interfaceName] = {}; 320 | } 321 | if (this.listeners[interfaceName][methodName] == null) { 322 | this.listeners[interfaceName][methodName] = new Set(); 323 | } 324 | this.listeners[interfaceName][methodName].add(callback); 325 | if (registerCallback != null) { 326 | registerCallback(); 327 | } 328 | } 329 | 330 | registerNewRevisionOnSpecificProjectHandler(poid, handler, callback) { 331 | this.register("NotificationInterface", "newRevision", handler, () => { 332 | this.call("NotificationRegistryInterface", "registerNewRevisionOnSpecificProjectHandler", { 333 | endPointId: this.webSocket.endPointId, 334 | poid: poid 335 | }, () => { 336 | if (callback != null) { 337 | callback(); 338 | } 339 | }); 340 | }); 341 | } 342 | 343 | registerNewExtendedDataOnRevisionHandler(roid, handler, callback) { 344 | this.register("NotificationInterface", "newExtendedData", handler, () => { 345 | this.call("NotificationRegistryInterface", "registerNewExtendedDataOnRevisionHandler", { 346 | endPointId: this.webSocket.endPointId, 347 | roid: roid 348 | }, () => { 349 | if (callback != null) { 350 | callback(); 351 | } 352 | }); 353 | }); 354 | } 355 | 356 | registerNewUserHandler(handler, callback) { 357 | this.register("NotificationInterface", "newUser", handler, () => { 358 | this.call("NotificationRegistryInterface", "registerNewUserHandler", { 359 | endPointId: this.webSocket.endPointId 360 | }, () => { 361 | if (callback != null) { 362 | callback(); 363 | } 364 | }); 365 | }); 366 | } 367 | 368 | unregisterNewUserHandler(handler, callback) { 369 | this.unregister(handler); 370 | this.call("NotificationRegistryInterface", "unregisterNewUserHandler", { 371 | endPointId: this.webSocket.endPointId 372 | }, () => { 373 | if (callback != null) { 374 | callback(); 375 | } 376 | }); 377 | } 378 | 379 | unregisterChangeProgressProjectHandler(poid, newHandler, closedHandler, callback) { 380 | this.unregister(newHandler); 381 | this.unregister(closedHandler); 382 | this.call("NotificationRegistryInterface", "unregisterChangeProgressOnProject", { 383 | poid: poid, 384 | endPointId: this.webSocket.endPointId 385 | }, callback); 386 | } 387 | 388 | registerChangeProgressProjectHandler(poid, newHandler, closedHandler, callback) { 389 | this.register("NotificationInterface", "newProgressOnProjectTopic", newHandler, () => { 390 | this.register("NotificationInterface", "closedProgressOnProjectTopic", closedHandler, () => { 391 | this.call("NotificationRegistryInterface", "registerChangeProgressOnProject", { 392 | poid: poid, 393 | endPointId: this.webSocket.endPointId 394 | }, () => { 395 | if (callback != null) { 396 | callback(); 397 | } 398 | }); 399 | }); 400 | }); 401 | } 402 | 403 | unregisterChangeProgressServerHandler(newHandler, closedHandler, callback) { 404 | this.unregister(newHandler); 405 | this.unregister(closedHandler); 406 | if (this.webSocket.endPointId != null) { 407 | this.call("NotificationRegistryInterface", "unregisterChangeProgressOnServer", { 408 | endPointId: this.webSocket.endPointId 409 | }, callback); 410 | } 411 | } 412 | 413 | registerChangeProgressServerHandler(newHandler, closedHandler, callback) { 414 | this.register("NotificationInterface", "newProgressOnServerTopic", newHandler, () => { 415 | this.register("NotificationInterface", "closedProgressOnServerTopic", closedHandler, () => { 416 | this.call("NotificationRegistryInterface", "registerChangeProgressOnServer", { 417 | endPointId: this.webSocket.endPointId 418 | }, () => { 419 | if (callback != null) { 420 | callback(); 421 | } 422 | }); 423 | }); 424 | }); 425 | } 426 | 427 | unregisterChangeProgressRevisionHandler(roid, newHandler, closedHandler, callback) { 428 | this.unregister(newHandler); 429 | this.unregister(closedHandler); 430 | this.call("NotificationRegistryInterface", "unregisterChangeProgressOnProject", { 431 | roid: roid, 432 | endPointId: this.webSocket.endPointId 433 | }, callback); 434 | } 435 | 436 | registerChangeProgressRevisionHandler(poid, roid, newHandler, closedHandler, callback) { 437 | this.register("NotificationInterface", "newProgressOnRevisionTopic", newHandler, () => { 438 | this.register("NotificationInterface", "closedProgressOnRevisionTopic", closedHandler, () => { 439 | this.call("NotificationRegistryInterface", "registerChangeProgressOnRevision", { 440 | poid: poid, 441 | roid: roid, 442 | endPointId: this.webSocket.endPointId 443 | }, () => { 444 | if (callback != null) { 445 | callback(); 446 | } 447 | }); 448 | }); 449 | }); 450 | } 451 | 452 | registerNewProjectHandler(handler, callback) { 453 | this.register("NotificationInterface", "newProject", handler, () => { 454 | this.call("NotificationRegistryInterface", "registerNewProjectHandler", { 455 | endPointId: this.webSocket.endPointId 456 | }, () => { 457 | if (callback != null) { 458 | callback(); 459 | } 460 | }); 461 | }); 462 | } 463 | 464 | unregisterNewProjectHandler(handler, callback) { 465 | this.unregister(handler); 466 | if (this.webSocket.endPointId != null) { 467 | this.call("NotificationRegistryInterface", "unregisterNewProjectHandler", { 468 | endPointId: this.webSocket.endPointId 469 | }, () => { 470 | if (callback != null) { 471 | callback(); 472 | } 473 | }, () => { 474 | // Discard 475 | }); 476 | } 477 | } 478 | 479 | unregisterNewRevisionOnSpecificProjectHandler(poid, handler, callback) { 480 | this.unregister(handler); 481 | this.call("NotificationRegistryInterface", "unregisterNewRevisionOnSpecificProjectHandler", { 482 | endPointId: this.webSocket.endPointId, 483 | poid: poid 484 | }, () => { 485 | if (callback != null) { 486 | callback(); 487 | } 488 | }, () => { 489 | // Discard 490 | }); 491 | } 492 | 493 | unregisterNewExtendedDataOnRevisionHandler(roid, handler, callback) { 494 | this.unregister(handler); 495 | this.call("NotificationRegistryInterface", "unregisterNewExtendedDataOnRevisionHandler", { 496 | endPointId: this.webSocket.endPointId, 497 | roid: roid 498 | }, () => { 499 | if (callback != null) { 500 | callback(); 501 | } 502 | }); 503 | } 504 | 505 | registerProgressHandler(topicId, handler, callback) { 506 | this.register("NotificationInterface", "progress", handler, () => { 507 | this.call("NotificationRegistryInterface", "registerProgressHandler", { 508 | topicId: topicId, 509 | endPointId: this.webSocket.endPointId 510 | }, () => { 511 | if (callback != null) { 512 | callback(); 513 | } else { 514 | this.call("NotificationRegistryInterface", "getProgress", { 515 | topicId: topicId 516 | }, (state) => { 517 | handler(topicId, state); 518 | }); 519 | } 520 | }); 521 | }); 522 | } 523 | 524 | unregisterProgressHandler(topicId, handler, callback) { 525 | this.unregister(handler); 526 | this.call("NotificationRegistryInterface", "unregisterProgressHandler", { 527 | topicId: topicId, 528 | endPointId: this.webSocket.endPointId 529 | }, () => {}).done(callback); 530 | } 531 | 532 | unregister(listener) { 533 | for (let i in this.listeners) { 534 | for (let j in this.listeners[i]) { 535 | const list = this.listeners[i][j]; 536 | for (let k = 0; k < list.length; k++) { 537 | if (list[k] === listener) { 538 | list.splice(k, 1); 539 | return; 540 | } 541 | } 542 | } 543 | } 544 | } 545 | 546 | createRequest(interfaceName, method, data) { 547 | let object = {}; 548 | object["interface"] = interfaceName; 549 | object.method = method; 550 | for (var key in data) { 551 | if (data[key] instanceof Set) { 552 | // Convert ES6 Set to an array 553 | data[key] = Array.from(data[key]); 554 | } 555 | } 556 | object.parameters = data; 557 | 558 | return object; 559 | } 560 | 561 | getJson(address, data, success, error) { 562 | const xhr = new XMLHttpRequest(); 563 | xhr.open("POST", address); 564 | xhr.onerror = () => { 565 | if (error != null) { 566 | error("Unknown network error"); 567 | } 568 | }; 569 | xhr.setRequestHeader("Content-Type", "application/json; charset=UTF-8"); 570 | xhr.onload = (jqXHR, textStatus, errorThrown) => { 571 | if (xhr.status === 200) { 572 | let data = ""; 573 | try { 574 | data = JSON.parse(xhr.responseText); 575 | } catch (e) { 576 | if (e instanceof SyntaxError) { 577 | if (error != null) { 578 | error(e); 579 | } else { 580 | this.notifier.setError(e); 581 | console.error(e); 582 | } 583 | } else { 584 | console.error(e); 585 | } 586 | } 587 | success(data); 588 | } else { 589 | if (error != null) { 590 | error(jqXHR, textStatus, errorThrown); 591 | } else { 592 | this.notifier.setError(textStatus); 593 | console.error(jqXHR, textStatus, errorThrown); 594 | } 595 | } 596 | }; 597 | xhr.send(JSON.stringify(data)); 598 | } 599 | 600 | multiCall(requests, callback, errorCallback, showBusy, showDone, showError, connectWebSocket) { 601 | if (!this.webSocket.connected && this.token != null && connectWebSocket) { 602 | this.webSocket.connect().then(() => { 603 | this.multiCall(requests, callback, errorCallback, showBusy, showDone, showError); 604 | }); 605 | return; 606 | } 607 | const promise = new BimServerApiPromise(); 608 | let request = null; 609 | if (requests.length == 1) { 610 | request = requests[0]; 611 | if (this.interfaceMapping[request[0]] == null) { 612 | this.log("Interface " + request[0] + " not found"); 613 | } 614 | request = { 615 | request: this.createRequest(this.interfaceMapping[request[0]], request[1], request[2]) 616 | }; 617 | } else if (requests.length > 1) { 618 | let requestObjects = []; 619 | requests.forEach((request) => { 620 | if (this.interfaceMapping[request[0]] == null) { 621 | this.log("Interface " + request[0] + " not found"); 622 | } 623 | requestObjects.push(this.createRequest(this.interfaceMapping[request[0]], request[1], request[2])); 624 | }); 625 | request = { 626 | requests: requestObjects 627 | }; 628 | } else if (requests.length === 0) { 629 | promise.fire(); 630 | callback(); 631 | } 632 | 633 | // this.notifier.clear(); 634 | 635 | if (this.token != null) { 636 | request.token = this.token; 637 | } 638 | 639 | let key = requests[0][1]; 640 | requests.forEach((item, index) => { 641 | if (index > 0) { 642 | key += "_" + item; 643 | } 644 | }); 645 | 646 | let showedBusy = false; 647 | if (showBusy) { 648 | if (this.lastBusyTimeOut != null) { 649 | clearTimeout(this.lastBusyTimeOut); 650 | this.lastBusyTimeOut = null; 651 | } 652 | if (typeof window !== 'undefined' && window.setTimeout != null) { 653 | this.lastBusyTimeOut = window.setTimeout(() => { 654 | this.notifier.setInfo(this.translate(key + "_BUSY"), -1); 655 | showedBusy = true; 656 | }, 200); 657 | } 658 | } 659 | 660 | // this.notifier.resetStatusQuick(); 661 | 662 | this.log("request", request); 663 | 664 | this.getJson(this.address, request, (data) => { 665 | this.log("response", data); 666 | let errorsToReport = []; 667 | if (requests.length == 1) { 668 | if (showBusy) { 669 | if (this.lastBusyTimeOut != null) { 670 | clearTimeout(this.lastBusyTimeOut); 671 | } 672 | } 673 | if (data.response.exception != null) { 674 | if (showError) { 675 | if (this.lastTimeOut != null) { 676 | clearTimeout(this.lastTimeOut); 677 | } 678 | this.notifier.setError(data.response.exception.message); 679 | } else { 680 | if (showedBusy) { 681 | this.notifier.resetStatus(); 682 | } 683 | errorsToReport.push(data.response.exception); 684 | } 685 | } else { 686 | if (showDone) { 687 | this.notifier.setSuccess(this.translate(key + "_DONE"), 5000); 688 | } else { 689 | if (showedBusy) { 690 | this.notifier.resetStatus(); 691 | } 692 | } 693 | } 694 | } else if (requests.length > 1) { 695 | data.responses.forEach((response) => { 696 | if (response.exception != null) { 697 | if (errorCallback == null) { 698 | this.notifier.setError(response.exception.message); 699 | } else { 700 | errorsToReport.push(response.exception); 701 | } 702 | } 703 | }); 704 | } 705 | if (errorsToReport.length > 0 && errorCallback) { 706 | if (requests.length == 1) { 707 | errorCallback(errorsToReport[0]); // with one request (and one error) -> call with an object 708 | } else { 709 | errorCallback(errorsToReport); // multiple requests, sends an array of errors 710 | } 711 | } else { 712 | if (requests.length == 1) { 713 | callback(data.response); 714 | } else if (requests.length > 1) { 715 | callback(data.responses); 716 | } 717 | } 718 | promise.fire(); 719 | }, 720 | (jqXHR, textStatus, errorThrown) => { 721 | if (textStatus == "abort") { 722 | // ignore 723 | } else { 724 | this.log(errorThrown); 725 | this.log(textStatus); 726 | this.log(jqXHR); 727 | if (this.lastTimeOut != null) { 728 | clearTimeout(this.lastTimeOut); 729 | } 730 | this.notifier.setError(this.translate("ERROR_REMOTE_METHOD_CALL")); 731 | } 732 | if (errorCallback != null) { 733 | const result = {}; 734 | result.error = textStatus; 735 | result.ok = false; 736 | errorCallback(result); 737 | } 738 | promise.fire(); 739 | }); 740 | return promise; 741 | } 742 | 743 | getModel(poid, roid, schema, deep, callback, name) { 744 | const model = new Model(this, poid, roid, schema); 745 | if (name != null) { 746 | model.name = name; 747 | } 748 | model.load(deep, callback); 749 | return model; 750 | } 751 | 752 | createModel(poid, callback) { 753 | const model = new Model(this, poid); 754 | model.init(callback); 755 | return model; 756 | } 757 | 758 | callWithNoIndication(interfaceName, methodName, data, callback, errorCallback) { 759 | return this.call(interfaceName, methodName, data, callback, errorCallback, false, false, false); 760 | } 761 | 762 | callWithFullIndication(interfaceName, methodName, data, callback) { 763 | return this.call(interfaceName, methodName, data, callback, null, true, true, true); 764 | } 765 | 766 | callWithUserErrorIndication(action, data, callback) { 767 | return this.call(null, null, data, callback, null, false, false, true); 768 | } 769 | 770 | callWithUserErrorAndDoneIndication(action, data, callback) { 771 | return this.call(null, null, data, callback, null, false, true, true); 772 | } 773 | 774 | isA(schema, typeSubject, typeName) { 775 | let isa = false; 776 | if (typeSubject == typeName) { 777 | return true; 778 | } 779 | 780 | let subject = this.schemas[schema][typeSubject]; 781 | 782 | // TODO duplicate code (also twice in model.js) 783 | if (typeSubject === "GeometryInfo" || typeSubject === "GeometryData" || typeSubject === "Buffer") { 784 | subject = this.schemas.geometry[typeSubject]; 785 | } 786 | 787 | if (subject == null) { 788 | console.log(typeSubject, "not found"); 789 | } 790 | subject.superclasses.some((superclass) => { 791 | if (superclass == typeName) { 792 | isa = true; 793 | return true; 794 | } 795 | if (this.isA(schema, superclass, typeName)) { 796 | isa = true; 797 | return true; 798 | } 799 | return false; 800 | }); 801 | return isa; 802 | } 803 | 804 | initiateCheckin(project, deserializerOid, callback, errorCallback) { 805 | this.callWithNoIndication("ServiceInterface", "initiateCheckin", { 806 | deserializerOid: deserializerOid, 807 | poid: project.oid 808 | }, (topicId) => { 809 | if (callback != null) { 810 | callback(topicId); 811 | } 812 | }, (error) => { 813 | errorCallback(error); 814 | }); 815 | } 816 | 817 | checkin(topicId, project, comment, file, deserializerOid, progressListener, success, error) { 818 | const xhr = new XMLHttpRequest(); 819 | 820 | xhr.upload.addEventListener("progress", 821 | (e) => { 822 | if (e.lengthComputable) { 823 | const percentage = Math.round((e.loaded * 100) / e.total); 824 | progressListener(percentage); 825 | } 826 | }, false); 827 | 828 | xhr.addEventListener("load", (event) => { 829 | const result = JSON.parse(xhr.response); 830 | 831 | if (result.exception == null) { 832 | if (success != null) { 833 | success(result.checkinid); 834 | } 835 | } else { 836 | if (error == null) { 837 | console.error(result.exception); 838 | } else { 839 | error(result.exception); 840 | } 841 | } 842 | }, false); 843 | 844 | xhr.open("POST", this.baseUrl + "/upload"); 845 | 846 | const formData = new FormData(); 847 | 848 | formData.append("token", this.token); 849 | formData.append("deserializerOid", deserializerOid); 850 | formData.append("comment", comment); 851 | formData.append("poid", project.oid); 852 | formData.append("topicId", topicId); 853 | formData.append("file", file); 854 | 855 | xhr.send(formData); 856 | } 857 | 858 | addExtendedData(roid, title, schema, data, success, error) { 859 | const reader = new FileReader(); 860 | const xhr = new XMLHttpRequest(); 861 | 862 | xhr.addEventListener("load", (e) => { 863 | const result = JSON.parse(xhr.response); 864 | 865 | if (result.exception == null) { 866 | this.call("ServiceInterface", "addExtendedDataToRevision", { 867 | roid: roid, 868 | extendedData: { 869 | __type: "SExtendedData", 870 | title: title, 871 | schemaId: schema.oid, 872 | fileId: result.fileId 873 | } 874 | }, () => { 875 | success(result.checkinid); 876 | }); 877 | } else { 878 | error(result.exception); 879 | } 880 | }, false); 881 | xhr.open("POST", this.baseUrl + "/upload"); 882 | if (typeof data == "File") { 883 | var file = data; 884 | reader.onload = () => { 885 | const formData = new FormData(); 886 | formData.append("action", "file"); 887 | formData.append("token", this.token); 888 | 889 | const blob = new Blob([file], { 890 | type: schema.contentType 891 | }); 892 | 893 | formData.append("file", blob, file.name); 894 | xhr.send(formData); 895 | }; 896 | reader.readAsBinaryString(file); 897 | } else { 898 | // Assuming data is a Blob 899 | const formData = new FormData(); 900 | formData.append("action", "file"); 901 | formData.append("token", this.token); 902 | formData.append("file", data, data.name); 903 | xhr.send(formData); 904 | } 905 | } 906 | 907 | setToken(token, callback, errorCallback) { 908 | this.token = token; 909 | this.call("AuthInterface", "getLoggedInUser", {}, (data) => { 910 | this.user = data; 911 | this.webSocket.connect(callback); 912 | }, () => { 913 | this.token = null; 914 | if (errorCallback != null) { 915 | errorCallback(); 916 | } 917 | }, true, false, true, false); 918 | } 919 | 920 | callWithWebsocket(interfaceName, methodName, data) { 921 | var promise = new Promise((resolve, reject) => { 922 | var id = this.idCounter++; 923 | this.websocketCalls.set(id, (response) => { 924 | resolve(response.response.result); 925 | }); 926 | var request = { 927 | id: id, 928 | request: { 929 | interface: interfaceName, 930 | method: methodName, 931 | parameters: data 932 | } 933 | }; 934 | if (this.token != null) { 935 | request.token = this.token; 936 | } 937 | this.webSocket.send(request); 938 | }); 939 | return promise; 940 | } 941 | 942 | /** 943 | * Call a single method, this method delegates to the multiCall method 944 | * @param {string} interfaceName - Interface name, e.g. "ServiceInterface" 945 | * @param {string} methodName - Methodname, e.g. "addProject" 946 | * @param {Object} data - Object with a field per arument 947 | * @param {Function} callback - Function to callback, first argument in callback will be the returned object 948 | * @param {Function} errorCallback - Function to callback on error 949 | * @param {boolean} showBusy - Whether to show busy indication 950 | * @param {boolean} showDone - Whether to show done indication 951 | * @param {boolean} showError - Whether to show errors 952 | * 953 | */ 954 | call(interfaceName, methodName, data, callback, errorCallback, showBusy = true, showDone = false, showError = true, connectWebSocket = true) { 955 | return this.multiCall([ 956 | [ 957 | interfaceName, 958 | methodName, 959 | data 960 | ] 961 | ], (data) => { 962 | if (data.exception == null) { 963 | if (callback != null) { 964 | callback(data.result); 965 | } 966 | } else { 967 | if (errorCallback != null) { 968 | errorCallback(data.exception); 969 | } 970 | } 971 | }, errorCallback, showBusy, showDone, showError, connectWebSocket); 972 | } 973 | } 974 | -------------------------------------------------------------------------------- /geometry.js: -------------------------------------------------------------------------------- 1 | export const geometry = { 2 | "classes" : { 3 | "GeometryInfo" : { 4 | "domain" : "geometry", 5 | "superclasses" : [ ], 6 | "fields" : { 7 | "bounds" : { 8 | "type" : "Bounds", 9 | "reference" : true, 10 | "many" : false, 11 | "inverse" : false 12 | }, 13 | "boundsUntransformed" : { 14 | "type" : "Bounds", 15 | "reference" : true, 16 | "many" : false, 17 | "inverse" : false 18 | }, 19 | "startVertex" : { 20 | "type" : "int", 21 | "reference" : false, 22 | "many" : false, 23 | "inverse" : false 24 | }, 25 | "startIndex" : { 26 | "type" : "int", 27 | "reference" : false, 28 | "many" : false, 29 | "inverse" : false 30 | }, 31 | "primitiveCount" : { 32 | "type" : "int", 33 | "reference" : false, 34 | "many" : false, 35 | "inverse" : false 36 | }, 37 | "transformation" : { 38 | "type" : "bytearray", 39 | "reference" : false, 40 | "many" : false, 41 | "inverse" : false 42 | }, 43 | "data" : { 44 | "type" : "GeometryData", 45 | "reference" : true, 46 | "many" : false, 47 | "inverse" : false 48 | }, 49 | "area" : { 50 | "type" : "double", 51 | "reference" : false, 52 | "many" : false, 53 | "inverse" : false 54 | }, 55 | "volume" : { 56 | "type" : "double", 57 | "reference" : false, 58 | "many" : false, 59 | "inverse" : false 60 | }, 61 | "hasTransparency" : { 62 | "type" : "boolean", 63 | "reference" : false, 64 | "many" : false, 65 | "inverse" : false 66 | }, 67 | "ifcProductOid" : { 68 | "type" : "long", 69 | "reference" : false, 70 | "many" : false, 71 | "inverse" : false 72 | }, 73 | "ifcProductUuid" : { 74 | "type" : "bytearray", 75 | "reference" : false, 76 | "many" : false, 77 | "inverse" : false 78 | }, 79 | "ifcProductRid" : { 80 | "type" : "int", 81 | "reference" : false, 82 | "many" : false, 83 | "inverse" : false 84 | }, 85 | "density" : { 86 | "type" : "EFloat", 87 | "reference" : false, 88 | "many" : false, 89 | "inverse" : false 90 | }, 91 | "boundsMm" : { 92 | "type" : "Bounds", 93 | "reference" : true, 94 | "many" : false, 95 | "inverse" : false 96 | }, 97 | "boundsUntransformedMm" : { 98 | "type" : "Bounds", 99 | "reference" : true, 100 | "many" : false, 101 | "inverse" : false 102 | }, 103 | "additionalData" : { 104 | "type" : "string", 105 | "reference" : false, 106 | "many" : false, 107 | "inverse" : false 108 | }, 109 | "nrColors" : { 110 | "type" : "int", 111 | "reference" : false, 112 | "many" : false, 113 | "inverse" : false 114 | }, 115 | "nrVertices" : { 116 | "type" : "int", 117 | "reference" : false, 118 | "many" : false, 119 | "inverse" : false 120 | }, 121 | "ifcProductPid" : { 122 | "type" : "int", 123 | "reference" : false, 124 | "many" : false, 125 | "inverse" : false 126 | } 127 | } 128 | }, 129 | "Vector3f" : { 130 | "domain" : "geometry", 131 | "superclasses" : [ ], 132 | "fields" : { 133 | "x" : { 134 | "type" : "double", 135 | "reference" : false, 136 | "many" : false, 137 | "inverse" : false 138 | }, 139 | "y" : { 140 | "type" : "double", 141 | "reference" : false, 142 | "many" : false, 143 | "inverse" : false 144 | }, 145 | "z" : { 146 | "type" : "double", 147 | "reference" : false, 148 | "many" : false, 149 | "inverse" : false 150 | } 151 | } 152 | }, 153 | "Bounds" : { 154 | "domain" : "geometry", 155 | "superclasses" : [ ], 156 | "fields" : { 157 | "min" : { 158 | "type" : "Vector3f", 159 | "reference" : true, 160 | "many" : false, 161 | "inverse" : false 162 | }, 163 | "max" : { 164 | "type" : "Vector3f", 165 | "reference" : true, 166 | "many" : false, 167 | "inverse" : false 168 | } 169 | } 170 | }, 171 | "Buffer" : { 172 | "domain" : "geometry", 173 | "superclasses" : [ ], 174 | "fields" : { 175 | "data" : { 176 | "type" : "bytearray", 177 | "reference" : false, 178 | "many" : false, 179 | "inverse" : false 180 | } 181 | } 182 | }, 183 | "GeometryData" : { 184 | "domain" : "geometry", 185 | "superclasses" : [ ], 186 | "fields" : { 187 | "nrIndices" : { 188 | "type" : "int", 189 | "reference" : false, 190 | "many" : false, 191 | "inverse" : false 192 | }, 193 | "nrVertices" : { 194 | "type" : "int", 195 | "reference" : false, 196 | "many" : false, 197 | "inverse" : false 198 | }, 199 | "nrNormals" : { 200 | "type" : "int", 201 | "reference" : false, 202 | "many" : false, 203 | "inverse" : false 204 | }, 205 | "nrColors" : { 206 | "type" : "int", 207 | "reference" : false, 208 | "many" : false, 209 | "inverse" : false 210 | }, 211 | "indices" : { 212 | "type" : "Buffer", 213 | "reference" : true, 214 | "many" : false, 215 | "inverse" : false 216 | }, 217 | "vertices" : { 218 | "type" : "Buffer", 219 | "reference" : true, 220 | "many" : false, 221 | "inverse" : false 222 | }, 223 | "verticesQuantized" : { 224 | "type" : "Buffer", 225 | "reference" : true, 226 | "many" : false, 227 | "inverse" : false 228 | }, 229 | "normals" : { 230 | "type" : "Buffer", 231 | "reference" : true, 232 | "many" : false, 233 | "inverse" : false 234 | }, 235 | "normalsQuantized" : { 236 | "type" : "Buffer", 237 | "reference" : true, 238 | "many" : false, 239 | "inverse" : false 240 | }, 241 | "colorsQuantized" : { 242 | "type" : "Buffer", 243 | "reference" : true, 244 | "many" : false, 245 | "inverse" : false 246 | }, 247 | "color" : { 248 | "type" : "Vector4f", 249 | "reference" : true, 250 | "many" : false, 251 | "inverse" : false 252 | }, 253 | "hasTransparency" : { 254 | "type" : "boolean", 255 | "reference" : false, 256 | "many" : false, 257 | "inverse" : false 258 | }, 259 | "reused" : { 260 | "type" : "int", 261 | "reference" : false, 262 | "many" : false, 263 | "inverse" : false 264 | }, 265 | "type" : { 266 | "type" : "short", 267 | "reference" : false, 268 | "many" : false, 269 | "inverse" : false 270 | }, 271 | "mostUsedColor" : { 272 | "type" : "Vector4f", 273 | "reference" : true, 274 | "many" : false, 275 | "inverse" : false 276 | }, 277 | "boundsMm" : { 278 | "type" : "Bounds", 279 | "reference" : true, 280 | "many" : false, 281 | "inverse" : false 282 | }, 283 | "saveableTriangles" : { 284 | "type" : "int", 285 | "reference" : false, 286 | "many" : false, 287 | "inverse" : false 288 | }, 289 | "colorPack" : { 290 | "type" : "ColorPack", 291 | "reference" : true, 292 | "many" : false, 293 | "inverse" : false 294 | }, 295 | "lineIndices" : { 296 | "type" : "Buffer", 297 | "reference" : true, 298 | "many" : false, 299 | "inverse" : false 300 | }, 301 | "nrLineIndices" : { 302 | "type" : "int", 303 | "reference" : false, 304 | "many" : false, 305 | "inverse" : false 306 | } 307 | } 308 | }, 309 | "Vector4f" : { 310 | "domain" : "geometry", 311 | "superclasses" : [ ], 312 | "fields" : { 313 | "x" : { 314 | "type" : "EFloat", 315 | "reference" : false, 316 | "many" : false, 317 | "inverse" : false 318 | }, 319 | "y" : { 320 | "type" : "EFloat", 321 | "reference" : false, 322 | "many" : false, 323 | "inverse" : false 324 | }, 325 | "z" : { 326 | "type" : "EFloat", 327 | "reference" : false, 328 | "many" : false, 329 | "inverse" : false 330 | }, 331 | "w" : { 332 | "type" : "EFloat", 333 | "reference" : false, 334 | "many" : false, 335 | "inverse" : false 336 | } 337 | } 338 | }, 339 | "ColorPack" : { 340 | "domain" : "geometry", 341 | "superclasses" : [ ], 342 | "fields" : { 343 | "data" : { 344 | "type" : "bytearray", 345 | "reference" : false, 346 | "many" : false, 347 | "inverse" : false 348 | } 349 | } 350 | } 351 | } 352 | }; -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |

BIMserver JavaScript API

6 | 7 |

8 | To make it easier to interact with the OpenSource BIMserver from a web environment (like a browser or NodeJS) you can use this library. 9 | This library uses the ECMAScript6 Modules concept. Not all browsers support this yet. 10 |

11 | 12 |

Caching

13 |

14 | The "?_v=%VERSION%" additions are there for efficient caching purposes. Any server system serving these files can tell the client to cache these files indefinitely. 15 |

16 | 17 |

Links

18 | 27 | 28 | To include this library in your project, copy this to your
<head>
29 |
30 | <script type="module" src="bimserverapipromise.js?_v=%VERSION%"></script>
31 | <script type="module" src="bimserverapiwebsocket.js?_v=%VERSION%"></script>
32 | <script type="module" src="bimserverclient.js?_v=%VERSION%"></script>
33 | <script type="module" src="ifc2x3tc1.js?_v=%VERSION%"></script>
34 | <script type="module" src="ifc4.js?_v=%VERSION%"></script>
35 | <script type="module" src="model.js?_v=%VERSION%"></script>
36 | <script type="module" src="translations_en.js?_v=%VERSION%"></script>
37 | 
38 | 39 | Combined minified version (only available on a released version): 40 | 41 | bimserverapi.js?_v=%VERSION% 42 | 43 |

Static import example

44 |
45 | // Import the library, all dependencies will be handled by the module system
46 | import BimServerClient from './bimserverclient.js';
47 | 
48 | // Create a new API, the path given should point to the base path of a BIMserver. This can also be a full URL like "http://bimserveraddress"
49 | var api = new BimServerClient("../..");
50 | api.init((client, version) => {
51 | 	console.log(version.version);
52 | });
53 | 
54 | 55 |

Dynamic import example

56 |
57 | 	// When the location on the API is not known in advance, you can use dynamic loading, in most browsers you'll need to use a "dev" version for this to work (Chrome 64 for example).
58 | 	var address = "http://addressofapi";
59 | 	Promise.all([
60 | 		address + "/bimserverclient.js",
61 | 		address + "/bimserverapipromise.js"
62 | 	].map(x => import(x)))
63 | 	.then(([BimServerClient, BimServerApiPromise]) => {
64 | 		var api = new BimServerClient.BimServerClient("../..");
65 | 		api.init((client, version) => {
66 | 			document.getElementById("version").innerHTML = JSON.stringify(version.version, 0, 2);
67 | 		});
68 | 	});
69 | 
70 | 71 |

(Live) Version returned from local BIMserver

72 |
73 | 
74 | 75 | 82 | 83 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | Version 3, 29 June 2007 3 | 4 | Copyright (C) 2007 Free Software Foundation, Inc. 5 | Everyone is permitted to copy and distribute verbatim copies 6 | of this license document, but changing it is not allowed. 7 | 8 | 9 | This version of the GNU Lesser General Public License incorporates 10 | the terms and conditions of version 3 of the GNU General Public 11 | License, supplemented by the additional permissions listed below. 12 | 13 | 0. Additional Definitions. 14 | 15 | As used herein, "this License" refers to version 3 of the GNU Lesser 16 | General Public License, and the "GNU GPL" refers to version 3 of the GNU 17 | General Public License. 18 | 19 | "The Library" refers to a covered work governed by this License, 20 | other than an Application or a Combined Work as defined below. 21 | 22 | An "Application" is any work that makes use of an interface provided 23 | by the Library, but which is not otherwise based on the Library. 24 | Defining a subclass of a class defined by the Library is deemed a mode 25 | of using an interface provided by the Library. 26 | 27 | A "Combined Work" is a work produced by combining or linking an 28 | Application with the Library. The particular version of the Library 29 | with which the Combined Work was made is also called the "Linked 30 | Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the 33 | Corresponding Source for the Combined Work, excluding any source code 34 | for portions of the Combined Work that, considered in isolation, are 35 | based on the Application, and not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the 38 | object code and/or source code for the Application, including any data 39 | and utility programs needed for reproducing the Combined Work from the 40 | Application, but excluding the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License 45 | without being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a 50 | facility refers to a function or data to be supplied by an Application 51 | that uses the facility (other than as an argument passed when the 52 | facility is invoked), then you may convey a copy of the modified 53 | version: 54 | 55 | a) under this License, provided that you make a good faith effort to 56 | ensure that, in the event an Application does not supply the 57 | function or data, the facility still operates, and performs 58 | whatever part of its purpose remains meaningful, or 59 | 60 | b) under the GNU GPL, with none of the additional permissions of 61 | this License applicable to that copy. 62 | 63 | 3. Object Code Incorporating Material from Library Header Files. 64 | 65 | The object code form of an Application may incorporate material from 66 | a header file that is part of the Library. You may convey such object 67 | code under terms of your choice, provided that, if the incorporated 68 | material is not limited to numerical parameters, data structure 69 | layouts and accessors, or small macros, inline functions and templates 70 | (ten or fewer lines in length), you do both of the following: 71 | 72 | a) Give prominent notice with each copy of the object code that the 73 | Library is used in it and that the Library and its use are 74 | covered by this License. 75 | 76 | b) Accompany the object code with a copy of the GNU GPL and this license 77 | document. 78 | 79 | 4. Combined Works. 80 | 81 | You may convey a Combined Work under terms of your choice that, 82 | taken together, effectively do not restrict modification of the 83 | portions of the Library contained in the Combined Work and reverse 84 | engineering for debugging such modifications, if you also do each of 85 | the following: 86 | 87 | a) Give prominent notice with each copy of the Combined Work that 88 | the Library is used in it and that the Library and its use are 89 | covered by this License. 90 | 91 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 92 | document. 93 | 94 | c) For a Combined Work that displays copyright notices during 95 | execution, include the copyright notice for the Library among 96 | these notices, as well as a reference directing the user to the 97 | copies of the GNU GPL and this license document. 98 | 99 | d) Do one of the following: 100 | 101 | 0) Convey the Minimal Corresponding Source under the terms of this 102 | License, and the Corresponding Application Code in a form 103 | suitable for, and under terms that permit, the user to 104 | recombine or relink the Application with a modified version of 105 | the Linked Version to produce a modified Combined Work, in the 106 | manner specified by section 6 of the GNU GPL for conveying 107 | Corresponding Source. 108 | 109 | 1) Use a suitable shared library mechanism for linking with the 110 | Library. A suitable mechanism is one that (a) uses at run time 111 | a copy of the Library already present on the user's computer 112 | system, and (b) will operate properly with a modified version 113 | of the Library that is interface-compatible with the Linked 114 | Version. 115 | 116 | e) Provide Installation Information, but only if you would otherwise 117 | be required to provide such information under section 6 of the 118 | GNU GPL, and only to the extent that such information is 119 | necessary to install and execute a modified version of the 120 | Combined Work produced by recombining or relinking the 121 | Application with a modified version of the Linked Version. (If 122 | you use option 4d0, the Installation Information must accompany 123 | the Minimal Corresponding Source and Corresponding Application 124 | Code. If you use option 4d1, you must provide the Installation 125 | Information in the manner specified by section 6 of the GNU GPL 126 | for conveying Corresponding Source.) 127 | 128 | 5. Combined Libraries. 129 | 130 | You may place library facilities that are a work based on the 131 | Library side by side in a single library together with other library 132 | facilities that are not Applications and are not covered by this 133 | License, and convey such a combined library under terms of your 134 | choice, if you do both of the following: 135 | 136 | a) Accompany the combined library with a copy of the same work based 137 | on the Library, uncombined with any other library facilities, 138 | conveyed under the terms of this License. 139 | 140 | b) Give prominent notice with the combined library that part of it 141 | is a work based on the Library, and explaining where to find the 142 | accompanying uncombined form of the same work. 143 | 144 | 6. Revised Versions of the GNU Lesser General Public License. 145 | 146 | The Free Software Foundation may publish revised and/or new versions 147 | of the GNU Lesser General Public License from time to time. Such new 148 | versions will be similar in spirit to the present version, but may 149 | differ in detail to address new problems or concerns. 150 | 151 | Each version is given a distinguishing version number. If the 152 | Library as you received it specifies that a certain numbered version 153 | of the GNU Lesser General Public License "or any later version" 154 | applies to it, you have the option of following the terms and 155 | conditions either of that published version or of any later version 156 | published by the Free Software Foundation. If the Library as you 157 | received it does not specify a version number of the GNU Lesser 158 | General Public License, you may choose any version of the GNU Lesser 159 | General Public License ever published by the Free Software Foundation. 160 | 161 | If the Library as you received it specifies that a proxy can decide 162 | whether future versions of the GNU Lesser General Public License shall 163 | apply, that proxy's public statement of acceptance of any version is 164 | permanent authorization for you to choose that version for the 165 | Library. -------------------------------------------------------------------------------- /model.js: -------------------------------------------------------------------------------- 1 | import {BimServerApiPromise} from "./bimserverapipromise.js"; 2 | 3 | export class Model { 4 | constructor(bimServerApi, poid, roid, schema) { 5 | this.schema = schema; 6 | this.bimServerApi = bimServerApi; 7 | this.poid = poid; 8 | this.roid = roid; 9 | this.waiters = []; 10 | 11 | this.objects = {}; 12 | this.objectsByGuid = {}; 13 | this.objectsByName = {}; 14 | 15 | this.oidsFetching = {}; 16 | this.guidsFetching = {}; 17 | this.namesFetching = {}; 18 | 19 | // Those are only fully loaded types (all of them), should not be stored here if loaded partially 20 | this.loadedTypes = []; 21 | this.loadedDeep = false; 22 | this.changedObjectOids = {}; 23 | this.loading = false; 24 | this.logging = true; 25 | 26 | this.changes = 0; 27 | this.changeListeners = []; 28 | } 29 | 30 | init(callback) { 31 | callback(); 32 | } 33 | 34 | load(deep, modelLoadCallback) { 35 | const othis = this; 36 | 37 | if (deep) { 38 | this.loading = true; 39 | this.bimServerApi.getJsonStreamingSerializer(function (serializer) { 40 | othis.bimServerApi.call("ServiceInterface", "download", { 41 | roids: [othis.roid], 42 | serializerOid: serializer.oid, 43 | sync: false 44 | }, function (topicId) { 45 | const url = othis.bimServerApi.generateRevisionDownloadUrl({ 46 | topicId: topicId, 47 | serializerOid: serializer.oid 48 | }); 49 | othis.bimServerApi.getJson(url, null, function (data) { 50 | data.objects.forEach(function (object) { 51 | othis.objects[object._i] = othis.createWrapper(object, object._t); 52 | }); 53 | othis.loading = false; 54 | othis.loadedDeep = true; 55 | othis.waiters.forEach(function (waiter) { 56 | waiter(); 57 | }); 58 | othis.waiters = []; 59 | othis.bimServerApi.call("ServiceInterface", "cleanupLongAction", { 60 | topicId: topicId 61 | }, function () { 62 | if (modelLoadCallback != null) { 63 | modelLoadCallback(othis); 64 | } 65 | }); 66 | }, function (error) { 67 | console.log(error); 68 | }); 69 | }); 70 | }); 71 | } else { 72 | if (modelLoadCallback != null) { 73 | modelLoadCallback(othis); 74 | } 75 | } 76 | } 77 | 78 | // Start a transaction, make sure to wait for the callback to be called, only after that the transaction will be active 79 | startTransaction(callback) { 80 | this.bimServerApi.call("LowLevelInterface", "startTransaction", { 81 | poid: this.poid 82 | }, (tid) => { 83 | this.tid = tid; 84 | callback(tid); 85 | }); 86 | } 87 | 88 | // Checks whether a transaction is running, if not, it throws an exception, otherwise it return the tid 89 | checkTransaction() { 90 | if (this.tid != null) { 91 | return this.tid; 92 | } 93 | throw new Error("No transaction is running, call startTransaction first"); 94 | } 95 | 96 | create(className, object, callback) { 97 | const tid = this.checkTransaction(); 98 | object._t = className; 99 | const wrapper = this.createWrapper({}, className); 100 | this.bimServerApi.call("LowLevelInterface", "createObject", { 101 | tid: tid, 102 | className: className 103 | }, (oid) => { 104 | wrapper._i = oid; 105 | this.objects[object._i] = wrapper; 106 | object._s = 1; 107 | if (callback != null) { 108 | callback(object); 109 | } 110 | }); 111 | return object; 112 | } 113 | 114 | reset() { 115 | 116 | } 117 | 118 | commit(comment, callback) { 119 | const tid = this.checkTransaction(); 120 | this.bimServerApi.call("LowLevelInterface", "commitTransaction", { 121 | tid: tid, 122 | comment: comment 123 | }, function (roid) { 124 | if (callback != null) { 125 | callback(roid); 126 | } 127 | }); 128 | } 129 | 130 | abort(callback) { 131 | const tid = this.checkTransaction(); 132 | this.bimServerApi.call("LowLevelInterface", "abortTransaction", { 133 | tid: tid 134 | }, function () { 135 | if (callback != null) { 136 | callback(); 137 | } 138 | }); 139 | } 140 | 141 | addChangeListener(changeListener) { 142 | this.changeListeners.push(changeListener); 143 | } 144 | 145 | incrementChanges() { 146 | this.changes++; 147 | this.changeListeners.forEach((changeListener) => { 148 | changeListener(this.changes); 149 | }); 150 | } 151 | 152 | extendClass(wrapperClass, typeName) { 153 | let realType = this.bimServerApi.schemas[this.schema][typeName]; 154 | if (typeName === "GeometryInfo" || typeName === "GeometryData" || typeName === "Buffer") { 155 | realType = this.bimServerApi.schemas.geometry[typeName]; 156 | } 157 | realType.superclasses.forEach((typeName) => { 158 | this.extendClass(wrapperClass, typeName); 159 | }); 160 | 161 | const othis = this; 162 | 163 | for (let fieldName in realType.fields) { 164 | const field = realType.fields[fieldName]; 165 | field.name = fieldName; 166 | wrapperClass.fields.push(field); 167 | (function (field, fieldName) { 168 | if (field.reference) { 169 | wrapperClass["set" + fieldName.firstUpper() + "Wrapped"] = function (typeName, value) { 170 | const object = this.object; 171 | object[fieldName] = { 172 | _t: typeName, 173 | value: value 174 | }; 175 | const tid = othis.checkTransaction(); 176 | const type = othis.bimServerApi.schemas[othis.schema][typeName]; 177 | const wrappedValueType = type.fields.wrappedValue; 178 | if (wrappedValueType.type === "string") { 179 | othis.bimServerApi.call("LowLevelInterface", "setWrappedStringAttribute", { 180 | tid: tid, 181 | oid: object._i, 182 | attributeName: fieldName, 183 | type: typeName, 184 | value: value 185 | }, function () { 186 | if (object.changedFields == null) { 187 | object.changedFields = {}; 188 | } 189 | object.changedFields[fieldName] = true; 190 | othis.changedObjectOids[object.oid] = true; 191 | othis.incrementChanges(); 192 | }); 193 | } 194 | }; 195 | wrapperClass["set" + fieldName.firstUpper()] = function (value) { 196 | const tid = othis.checkTransaction(); 197 | const object = this.object; 198 | object[fieldName] = value; 199 | if (value == null) { 200 | othis.bimServerApi.call("LowLevelInterface", "unsetReference", { 201 | tid: tid, 202 | oid: object._i, 203 | referenceName: fieldName, 204 | }, function () { 205 | if (object.changedFields == null) { 206 | object.changedFields = {}; 207 | } 208 | object.changedFields[fieldName] = true; 209 | othis.changedObjectOids[object.oid] = true; 210 | }); 211 | } else { 212 | othis.bimServerApi.call("LowLevelInterface", "setReference", { 213 | tid: tid, 214 | oid: object._i, 215 | referenceName: fieldName, 216 | referenceOid: value._i 217 | }, function () { 218 | if (object.changedFields == null) { 219 | object.changedFields = {}; 220 | } 221 | object.changedFields[fieldName] = true; 222 | othis.changedObjectOids[object.oid] = true; 223 | }); 224 | } 225 | }; 226 | wrapperClass["add" + fieldName.firstUpper()] = function (value, callback) { 227 | const object = this.object; 228 | const tid = othis.checkTransaction(); 229 | if (object[fieldName] == null) { 230 | object[fieldName] = []; 231 | } 232 | object[fieldName].push(value); 233 | othis.bimServerApi.call("LowLevelInterface", "addReference", { 234 | tid: tid, 235 | oid: object._i, 236 | referenceName: fieldName, 237 | referenceOid: value._i 238 | }, function () { 239 | if (object.changedFields == null) { 240 | object.changedFields = {}; 241 | } 242 | object.changedFields[fieldName] = true; 243 | othis.changedObjectOids[object.oid] = true; 244 | if (callback != null) { 245 | callback(); 246 | } 247 | }); 248 | }; 249 | wrapperClass["remove" + fieldName.firstUpper()] = function (value, callback) { 250 | const object = this.object; 251 | const tid = othis.checkTransaction(); 252 | const list = object[fieldName]; 253 | const index = list.indexOf(value); 254 | list.splice(index, 1); 255 | 256 | othis.bimServerApi.call("LowLevelInterface", "removeReference", { 257 | tid: tid, 258 | oid: object._i, 259 | referenceName: fieldName, 260 | index: index 261 | }, function () { 262 | if (object.changedFields == null) { 263 | object.changedFields = {}; 264 | } 265 | object.changedFields[fieldName] = true; 266 | othis.changedObjectOids[object.oid] = true; 267 | if (callback != null) { 268 | callback(); 269 | } 270 | }); 271 | }; 272 | wrapperClass["get" + fieldName.firstUpper()] = function (callback) { 273 | const object = this.object; 274 | const model = this.model; 275 | const promise = new BimServerApiPromise(); 276 | if (object[fieldName] != null) { 277 | if (field.many) { 278 | object[fieldName].forEach(function (item, index) { 279 | callback(item, index); 280 | }); 281 | } else { 282 | callback(object[fieldName]); 283 | } 284 | promise.fire(); 285 | return promise; 286 | } 287 | const embValue = object["_e" + fieldName]; 288 | if (embValue != null) { 289 | if (callback != null) { 290 | callback(embValue); 291 | } 292 | promise.fire(); 293 | return promise; 294 | } 295 | const value = object["_r" + fieldName]; 296 | if (field.many) { 297 | if (object[fieldName] == null) { 298 | object[fieldName] = []; 299 | } 300 | if (value != null) { 301 | model.get(value, function (v) { 302 | object[fieldName].push(v); 303 | callback(v, object[fieldName].length - 1); 304 | }).done(function () { 305 | promise.fire(); 306 | }); 307 | } else { 308 | promise.fire(); 309 | } 310 | } else { 311 | if (value != null) { 312 | const ref = othis.objects[value._i]; 313 | if (value._i == -1) { 314 | callback(null); 315 | promise.fire(); 316 | } else if (ref == null || ref.object._s == 0) { 317 | model.get(value._i, function (v) { 318 | object[fieldName] = v; 319 | callback(v); 320 | }).done(function () { 321 | promise.fire(); 322 | }); 323 | } else { 324 | object[fieldName] = ref; 325 | callback(ref); 326 | promise.fire(); 327 | } 328 | } else { 329 | callback(null); 330 | promise.fire(); 331 | } 332 | } 333 | return promise; 334 | }; 335 | } else { 336 | wrapperClass["get" + fieldName.firstUpper()] = function (callback) { 337 | const object = this.object; 338 | if (field.many) { 339 | if (object[fieldName] == null) { 340 | object[fieldName] = []; 341 | } 342 | // object[fieldName].push = function () {}; 343 | } 344 | if (callback != null) { 345 | callback(object[fieldName]); 346 | } 347 | return object[fieldName]; 348 | }; 349 | wrapperClass["set" + fieldName.firstUpper()] = function (value) { 350 | const object = this.object; 351 | object[fieldName] = value; 352 | const tid = othis.checkTransaction(); 353 | if (field.many) { 354 | othis.bimServerApi.call("LowLevelInterface", "setDoubleAttributes", { 355 | tid: tid, 356 | oid: object._i, 357 | attributeName: fieldName, 358 | values: value 359 | }, function () {}); 360 | } else { 361 | if (value == null) { 362 | othis.bimServerApi.call("LowLevelInterface", "unsetAttribute", { 363 | tid: tid, 364 | oid: object._i, 365 | attributeName: fieldName 366 | }, function () {}); 367 | } else if (field.type === "string") { 368 | othis.bimServerApi.call("LowLevelInterface", "setStringAttribute", { 369 | tid: tid, 370 | oid: object._i, 371 | attributeName: fieldName, 372 | value: value 373 | }, function () {}); 374 | } else if (field.type === "double") { 375 | othis.bimServerApi.call("LowLevelInterface", "setDoubleAttribute", { 376 | tid: tid, 377 | oid: object._i, 378 | attributeName: fieldName, 379 | value: value 380 | }, function () {}); 381 | } else if (field.type === "boolean") { 382 | othis.bimServerApi.call("LowLevelInterface", "setBooleanAttribute", { 383 | tid: tid, 384 | oid: object._i, 385 | attributeName: fieldName, 386 | value: value 387 | }, function () {}); 388 | } else if (field.type === "int") { 389 | othis.bimServerApi.call("LowLevelInterface", "setIntegerAttribute", { 390 | tid: tid, 391 | oid: object._i, 392 | attributeName: fieldName, 393 | value: value 394 | }, function () {}); 395 | } else if (field.type === "enum") { 396 | othis.bimServerApi.call("LowLevelInterface", "setEnumAttribute", { 397 | tid: tid, 398 | oid: object._i, 399 | attributeName: fieldName, 400 | value: value 401 | }, function () {}); 402 | } else { 403 | othis.bimServerApi.log("Unimplemented type " + typeof value); 404 | } 405 | object[fieldName] = value; 406 | } 407 | if (object.changedFields == null) { 408 | object.changedFields = {}; 409 | } 410 | object.changedFields[fieldName] = true; 411 | othis.changedObjectOids[object.oid] = true; 412 | }; 413 | } 414 | })(field, fieldName); 415 | } 416 | } 417 | 418 | dumpByType() { 419 | const mapLoaded = {}; 420 | const mapNotLoaded = {}; 421 | for (let oid in this.objects) { 422 | const object = this.objects[oid]; 423 | const type = object.getType(); 424 | const counter = mapLoaded[type]; 425 | if (object.object._s == 1) { 426 | if (counter == null) { 427 | mapLoaded[type] = 1; 428 | } else { 429 | mapLoaded[type] = counter + 1; 430 | } 431 | } 432 | if (object.object._s == 0) { 433 | const counter = mapNotLoaded[type]; 434 | if (counter == null) { 435 | mapNotLoaded[type] = 1; 436 | } else { 437 | mapNotLoaded[type] = counter + 1; 438 | } 439 | } 440 | } 441 | console.log("LOADED"); 442 | for (let type in mapLoaded) { 443 | console.log(type, mapLoaded[type]); 444 | } 445 | console.log("NOT_LOADED"); 446 | for (let type in mapNotLoaded) { 447 | console.log(type, mapNotLoaded[type]); 448 | } 449 | } 450 | 451 | getClass(typeName) { 452 | const othis = this; 453 | 454 | if (this.bimServerApi.classes[typeName] == null) { 455 | let realType = this.bimServerApi.schemas[this.schema][typeName]; 456 | if (realType == null) { 457 | if (typeName === "GeometryInfo" || typeName === "GeometryData" || typeName === "Buffer") { 458 | realType = this.bimServerApi.schemas.geometry[typeName]; 459 | } 460 | if (realType == null) { 461 | throw "Type " + typeName + " not found in schema " + this.schema; 462 | } 463 | } 464 | 465 | const wrapperClass = { 466 | fields: [] 467 | }; 468 | 469 | wrapperClass.isA = function (typeName) { 470 | return othis.bimServerApi.isA(othis.schema, this.object._t, typeName); 471 | }; 472 | wrapperClass.getType = function () { 473 | return this.object._t; 474 | }; 475 | wrapperClass.remove = function (removeCallback) { 476 | const tid = othis.checkTransaction(); 477 | othis.bimServerApi.call("LowLevelInterface", "removeObject", { 478 | tid: tid, 479 | oid: this.object._i 480 | }, function () { 481 | if (removeCallback != null) { 482 | removeCallback(); 483 | } 484 | delete othis.objects[this.object._i]; 485 | }); 486 | }; 487 | 488 | othis.extendClass(wrapperClass, typeName); 489 | 490 | othis.bimServerApi.classes[typeName] = wrapperClass; 491 | } 492 | return othis.bimServerApi.classes[typeName]; 493 | } 494 | 495 | createWrapper(object, typeName) { 496 | if (this.objects[object._i] != null) { 497 | console.log("Warning!", object); 498 | } 499 | if (typeName == null) { 500 | console.warn("typeName = null", object); 501 | } 502 | object.oid = object._i; 503 | const cl = this.getClass(typeName); 504 | if (cl == null) { 505 | console.error("No class found for " + typeName); 506 | } 507 | const wrapper = Object.create(cl); 508 | // transient variables 509 | wrapper.trans = { 510 | mode: 2 511 | }; 512 | wrapper.oid = object.oid; 513 | wrapper.model = this; 514 | wrapper.object = object; 515 | return wrapper; 516 | } 517 | 518 | size(callback) { 519 | this.bimServerApi.call("ServiceInterface", "getRevision", { 520 | roid: this.roid 521 | }, function (revision) { 522 | callback(revision.size); 523 | }); 524 | } 525 | 526 | count(type, includeAllSubTypes, callback) { 527 | // TODO use includeAllSubTypes 528 | this.bimServerApi.call("LowLevelInterface", "count", { 529 | roid: this.roid, 530 | className: type 531 | }, function (size) { 532 | callback(size); 533 | }); 534 | } 535 | 536 | getByX(methodName, keyname, fetchingMap, targetMap, query, getValueMethod, list, callback) { 537 | const promise = new BimServerApiPromise(); 538 | if (typeof list == "string" || typeof list == "number") { 539 | list = [list]; 540 | } 541 | let len = list.length; 542 | // Iterating in reverse order because we remove items from this array 543 | while (len--) { 544 | const item = list[len]; 545 | if (targetMap[item] != null) { 546 | // Already loaded? Remove from list and call callback 547 | const existingObject = targetMap[item].object; 548 | if (existingObject._s == 1) { 549 | const index = list.indexOf(item); 550 | list.splice(index, 1); 551 | callback(targetMap[item]); 552 | } 553 | } else if (fetchingMap[item] != null) { 554 | // Already loading? Add the callback to the list and remove from fetching list 555 | fetchingMap[item].push(callback); 556 | const index = list.indexOf(item); 557 | list.splice(index, 1); 558 | } 559 | } 560 | 561 | const othis = this; 562 | // Any left? 563 | if (list.length > 0) { 564 | list.forEach(function (item) { 565 | fetchingMap[item] = []; 566 | }); 567 | othis.bimServerApi.getJsonStreamingSerializer(function (serializer) { 568 | const request = { 569 | roids: [othis.roid], 570 | query: JSON.stringify(query), 571 | serializerOid: serializer.oid, 572 | sync: false 573 | }; 574 | othis.bimServerApi.call("ServiceInterface", "download", request, function (topicId) { 575 | const url = othis.bimServerApi.generateRevisionDownloadUrl({ 576 | topicId: topicId, 577 | serializerOid: serializer.oid 578 | }); 579 | othis.bimServerApi.getJson(url, null, function (data) { 580 | if (data.objects.length > 0) { 581 | let done = 0; 582 | data.objects.forEach(function (object) { 583 | let wrapper = null; 584 | if (othis.objects[object._i] != null) { 585 | wrapper = othis.objects[object._i]; 586 | if (wrapper.object._s != 1) { 587 | wrapper.object = object; 588 | } 589 | } else { 590 | wrapper = othis.createWrapper(object, object._t); 591 | } 592 | const item = getValueMethod(object); 593 | // Checking the value again, because sometimes serializers send more objects... 594 | if (list.indexOf(item) != -1) { 595 | targetMap[item] = wrapper; 596 | if (fetchingMap[item] != null) { 597 | fetchingMap[item].forEach(function (cb) { 598 | cb(wrapper); 599 | }); 600 | delete fetchingMap[item]; 601 | } 602 | callback(wrapper); 603 | } 604 | done++; 605 | if (done == data.objects.length) { 606 | othis.bimServerApi.call("ServiceInterface", "cleanupLongAction", { 607 | topicId: topicId 608 | }, function () { 609 | promise.fire(); 610 | }); 611 | } 612 | }); 613 | } else { 614 | othis.bimServerApi.log("Object with " + keyname + " " + list + " not found"); 615 | callback(null); 616 | promise.fire(); 617 | } 618 | }, function (error) { 619 | console.log(error); 620 | }); 621 | }); 622 | }); 623 | } else { 624 | promise.fire(); 625 | } 626 | return promise; 627 | } 628 | 629 | getByGuids(guids, callback) { 630 | const query = { 631 | guids: guids 632 | }; 633 | return this.getByX("getByGuid", "guid", this.guidsFetching, this.objectsByGuid, query, function (object) { 634 | return object.GlobalId; 635 | }, guids, callback); 636 | } 637 | 638 | get(oids, callback) { 639 | if (typeof oids == "number") { 640 | oids = [oids]; 641 | } else if (typeof oids == "string") { 642 | oids = [parseInt(oids)]; 643 | } else if (Array.isArray(oids)) { 644 | const newOids = []; 645 | oids.forEach(function (oid) { 646 | if (typeof oid == "object") { 647 | newOids.push(oid._i); 648 | } else { 649 | newOids.push(oid); 650 | } 651 | }); 652 | oids = newOids; 653 | } 654 | const query = { 655 | oids: oids 656 | }; 657 | return this.getByX("get", "OID", this.oidsFetching, this.objects, query, function (object) { 658 | return object._i; 659 | }, oids, callback); 660 | } 661 | 662 | getByName(names, callback) { 663 | const query = { 664 | names: names 665 | }; 666 | return this.getByX("getByName", "name", this.namesFetching, this.objectsByName, query, function (object) { 667 | return object.getName == null ? null : object.getName(); 668 | }, names, callback); 669 | } 670 | 671 | query(query, callback, errorCallback) { 672 | const promise = new BimServerApiPromise(); 673 | const fullTypesLoading = {}; 674 | if (query.queries != null) { 675 | query.queries.forEach((subQuery) => { 676 | if (subQuery.type != null) { 677 | if (typeof subQuery.type === "object") { 678 | fullTypesLoading[subQuery.type.name] = true; 679 | this.loadedTypes[subQuery.type.name] = {}; 680 | if (subQuery.type.includeAllSubTypes) { 681 | const schema = this.bimServerApi.schemas[this.schema]; 682 | this.bimServerApi.getAllSubTypes(schema, subQuery.type.name, (subTypeName) => { 683 | fullTypesLoading[subTypeName] = true; 684 | this.loadedTypes[subTypeName] = {}; 685 | }); 686 | } 687 | } else { 688 | fullTypesLoading[subQuery.type] = true; 689 | this.loadedTypes[subQuery.type] = {}; 690 | if (subQuery.includeAllSubTypes) { 691 | const schema = this.bimServerApi.schemas[this.schema]; 692 | this.bimServerApi.getAllSubTypes(schema, subQuery.type, (subTypeName) => { 693 | fullTypesLoading[subTypeName] = true; 694 | this.loadedTypes[subTypeName] = {}; 695 | }); 696 | } 697 | } 698 | } 699 | }); 700 | } 701 | this.bimServerApi.getJsonStreamingSerializer((serializer) => { 702 | this.bimServerApi.callWithFullIndication("ServiceInterface", "download", { 703 | roids: [this.roid], 704 | query: JSON.stringify(query), 705 | serializerOid: serializer.oid, 706 | sync: false 707 | }, (topicId) => { 708 | let handled = false; 709 | this.bimServerApi.registerProgressHandler(topicId, (topicId, state) => { 710 | if (state.title == "Done preparing" && !handled) { 711 | handled = true; 712 | const url = this.bimServerApi.generateRevisionDownloadUrl({ 713 | topicId: topicId, 714 | serializerOid: serializer.oid 715 | }); 716 | this.bimServerApi.notifier.setInfo(this.bimServerApi.translate("GETTING_MODEL_DATA"), -1); 717 | this.bimServerApi.getJson(url, null, (data) => { 718 | //console.log("query", data.objects.length); 719 | data.objects.forEach((object) => { 720 | let wrapper = this.objects[object._i]; 721 | if (wrapper == null) { 722 | wrapper = this.createWrapper(object, object._t); 723 | this.objects[object._i] = wrapper; 724 | if (fullTypesLoading[object._t] != null) { 725 | this.loadedTypes[object._t][wrapper.oid] = wrapper; 726 | } 727 | } else { 728 | if (object._s == 1) { 729 | wrapper.object = object; 730 | } 731 | } 732 | // if (othis.loadedTypes[wrapper.getType()] == null) { 733 | // othis.loadedTypes[wrapper.getType()] = {}; 734 | // } 735 | // othis.loadedTypes[wrapper.getType()][object._i] = wrapper; 736 | if (object._s == 1 && callback != null) { 737 | callback(wrapper); 738 | } 739 | }); 740 | // othis.dumpByType(); 741 | this.bimServerApi.call("ServiceInterface", "cleanupLongAction", { 742 | topicId: topicId 743 | }, () => { 744 | promise.fire(); 745 | this.bimServerApi.notifier.setSuccess(this.bimServerApi.translate("MODEL_DATA_DONE")); 746 | }); 747 | }); 748 | } else if (state.state == "AS_ERROR") { 749 | if (errorCallback != null) { 750 | errorCallback(state.title); 751 | } else { 752 | console.error(state.title); 753 | } 754 | } 755 | }); 756 | }); 757 | }); 758 | return promise; 759 | } 760 | 761 | getAllOfType(type, includeAllSubTypes, callback) { 762 | const promise = new BimServerApiPromise(); 763 | if (this.loadedDeep) { 764 | for (let oid in this.objects) { 765 | const object = this.objects[oid]; 766 | if (object._t == type) { 767 | callback(object); 768 | } 769 | } 770 | promise.fire(); 771 | } else { 772 | const types = []; 773 | types.push(type); 774 | if (includeAllSubTypes) { 775 | this.bimServerApi.getAllSubTypes(this.bimServerApi.schemas[this.schema], type, function (subType) { 776 | types.push(subType); 777 | }); 778 | } 779 | 780 | const query = { 781 | queries: [] 782 | }; 783 | 784 | types.forEach((t) => { 785 | if (this.loadedTypes[t] && Object.getOwnPropertyNames(this.loadedTypes[t]).length !== 0) { 786 | for (let oid in this.loadedTypes[t]) { 787 | callback(this.loadedTypes[t][oid]); 788 | } 789 | } else { 790 | query.queries.push({ 791 | type: t 792 | }); 793 | } 794 | }); 795 | 796 | if (query.queries.length > 0) { 797 | this.bimServerApi.getJsonStreamingSerializer((serializer) => { 798 | this.bimServerApi.call("ServiceInterface", "download", { 799 | roids: [this.roid], 800 | query: JSON.stringify(query), 801 | serializerOid: serializer.oid, 802 | sync: false 803 | }, (topicId) => { 804 | const url = this.bimServerApi.generateRevisionDownloadUrl({ 805 | topicId: topicId, 806 | serializerOid: serializer.oid 807 | }); 808 | this.bimServerApi.getJson(url, null, (data) => { 809 | data.objects.forEach((object) => { 810 | if (!this.loadedTypes[object._t]) { 811 | this.loadedTypes[object._t] = {} 812 | } 813 | 814 | if (this.objects[object._i]) { 815 | // Hmm we are doing a query on type, but some objects have already loaded, let's use those instead 816 | const wrapper = this.objects[object._i]; 817 | if (wrapper.object._s !== 1) { 818 | // Replace the value with something that's LOADED 819 | wrapper.object = object; 820 | } 821 | if (wrapper.isA(object._t)) { 822 | this.loadedTypes[object._t][object._i] = wrapper; 823 | callback(wrapper); 824 | } 825 | } else { 826 | const wrapper = this.createWrapper(object, object._t); 827 | this.objects[object._i] = wrapper; 828 | if (object._s == 1) { 829 | this.loadedTypes[object._t][object._i] = wrapper; 830 | callback(wrapper); 831 | } 832 | } 833 | }); 834 | this.bimServerApi.call("ServiceInterface", "cleanupLongAction", { 835 | topicId: topicId 836 | }, () => { 837 | promise.fire(); 838 | }); 839 | }, (error) => { 840 | console.log(error); 841 | }); 842 | }); 843 | }); 844 | } else { 845 | promise.fire(); 846 | } 847 | } 848 | return promise; 849 | } 850 | } 851 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bimserverapi", 3 | "groupId": "org.opensourcebim", 4 | "artifactId": "javascriptapi", 5 | "organization": "OpenSource BIM", 6 | "version": "0.0.167", 7 | "description": "A JavaScript API for the OpenSource BIMserver", 8 | "author": { 9 | "name": "Ruben de Laat", 10 | "email": "ruben@logic-labs.nl" 11 | }, 12 | "scripts": { 13 | "build": "rollup -c" 14 | }, 15 | "files": [ 16 | "*.js" 17 | ], 18 | "main": "build/bimserverapi.js", 19 | "module": "bimserverclient.js", 20 | "repository": { 21 | "type": "git", 22 | "url": "https://github.com/opensourceBIM/BIMserver-JavaScript-API" 23 | }, 24 | "bugs": { 25 | "url": "https://github.com/opensourceBIM/BIMserver-JavaScript-API/issues" 26 | }, 27 | "keywords": [], 28 | "browser": { 29 | "xhr2": false, 30 | "websocket": false 31 | }, 32 | "dependencies": { 33 | "websocket": "*", 34 | "xhr2": "*" 35 | }, 36 | "devDependencies": { 37 | "babel-core": "^6.26.0", 38 | "babel-plugin-external-helpers": "^6.22.0", 39 | "babel-preset-env": "^1.6.1", 40 | "grunt": "*", 41 | "grunt-contrib-concat": "*", 42 | "grunt-contrib-jshint": "*", 43 | "grunt-contrib-nodeunit": "*", 44 | "grunt-contrib-uglify": "*", 45 | "grunt-zip": "*", 46 | "rollup": "^0.50.0", 47 | "rollup-plugin-babel": "^3.0.2", 48 | "rollup-plugin-commonjs": "^8.2.6", 49 | "rollup-plugin-node-resolve": "^3.0.0", 50 | "rollup-plugin-uglify": "^2.0.1" 51 | }, 52 | "preferGlobal": true, 53 | "private": false, 54 | "analyze": true, 55 | "license": "AGPL-3.0", 56 | "readmeFilename": "README.md", 57 | "tonicExampleFilename": "tonicExample.js" 58 | } 59 | -------------------------------------------------------------------------------- /plugin/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/opensourceBIM/BIMserver-JavaScript-API/74b42a77f264a36952e8b7ffe41af99dd0c0c4ca/plugin/icon.png -------------------------------------------------------------------------------- /plugin/plugin.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | bimserverjavascriptapi 5 | BIMserver JavaScript API 6 | JavaScript API for BIMserver 7 | 8 | -------------------------------------------------------------------------------- /plugin/version.properties: -------------------------------------------------------------------------------- 1 | build.date=${timestamp} -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | org.opensourcebim 5 | bimserverapi 6 | 0.0.177-SNAPSHOT 7 | BIMServer JavaScript API 8 | JavaScript API for BIMserver 9 | https://github.com/opensourceBIM/BIMserver-JavaScript-API 10 | 11 | OpenSource BIM 12 | opensourcebim.org 13 | 14 | 15 | 16 | GNU LGPLv3 17 | https://choosealicense.com/licenses/lgpl-3.0/ 18 | repo 19 | 20 | 21 | 22 | 23 | Ruben de Laat 24 | ruben@logic-labs.nl 25 | 26 | 27 | 28 | scm:git:https://github.com/opensourceBIM/BIMserver-JavaScript-API.git 29 | scm:git:https://github.com/opensourceBIM/BIMserver-JavaScript-API.git 30 | bimserverapi-0.0.167 31 | https://github.com/opensourceBIM/BIMserver-JavaScript-API.git 32 | 33 | 34 | GitHub 35 | https://github.com/opensourceBIM/BIMserver-JavaScript-API/issues 36 | 37 | 38 | 39 | ossrh 40 | https://oss.sonatype.org/service/local/staging/deploy/maven2/ 41 | 42 | 43 | ossrh 44 | https://oss.sonatype.org/content/repositories/snapshots 45 | 46 | 47 | 48 | yyyy-MM-dd'T'HH:mm:ssZ 49 | UTF-8 50 | ${maven.build.timestamp} 51 | 52 | 53 | 54 | org.opensourcebim 55 | pluginbase 56 | 1.5.177 57 | 58 | 59 | 60 | 61 | 62 | . 63 | . 64 | 65 | index.html 66 | *.js 67 | 68 | 69 | 70 | ../../output 71 | 72 | 73 | ../../build 74 | 75 | 76 | plugin 77 | true 78 | plugin 79 | 80 | 81 | 82 | 83 | org.codehaus.mojo 84 | build-helper-maven-plugin 85 | 86 | 87 | attach-plugin 88 | package 89 | 90 | attach-artifact 91 | 92 | 93 | 94 | 95 | plugin/plugin.xml 96 | xml 97 | plugin 98 | 99 | 100 | plugin/icon.png 101 | png 102 | icon 103 | 104 | 105 | ${project.build.outputDirectory}/plugin/version.properties 106 | properties 107 | version 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | org.sonatype.plugins 116 | nexus-staging-maven-plugin 117 | 1.6.3 118 | true 119 | 120 | ossrh 121 | https://oss.sonatype.org/ 122 | true 123 | 60 124 | 125 | 126 | 127 | maven-release-plugin 128 | 2.5.3 129 | 130 | github.com 131 | false 132 | release 133 | deploy 134 | 135 | 136 | 137 | 138 | 139 | 140 | release 141 | 142 | 143 | 144 | maven-gpg-plugin 145 | 1.5 146 | 147 | 148 | sign-artifacts 149 | verify 150 | 151 | sign 152 | 153 | 154 | 155 | --pinentry-mode 156 | loopback 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | import resolve from 'rollup-plugin-node-resolve'; 2 | import commonjs from 'rollup-plugin-commonjs'; 3 | import babel from 'rollup-plugin-babel'; 4 | import uglify from 'rollup-plugin-uglify'; 5 | 6 | export default { 7 | input: 'bimserverclient.js', 8 | output: { 9 | format: 'umd', 10 | name: 'bimserverapi', 11 | file: 'build/bimserverapi.umd.js', 12 | sourceMap: true 13 | }, 14 | plugins: [ 15 | resolve({ 16 | jsnext: true, 17 | main: true, 18 | preferBuiltins: true, 19 | browser: true 20 | }), 21 | commonjs({ 22 | sourceMap: false 23 | }), 24 | babel({ 25 | exclude: ['node_modules/**'] 26 | }) 27 | ] 28 | }; -------------------------------------------------------------------------------- /tonicExample.js: -------------------------------------------------------------------------------- 1 | var bimserverapi = require("bimserverapi"); 2 | 3 | bimserverapi.login("admin@bimserver.org", "admin"); -------------------------------------------------------------------------------- /translations_en.js: -------------------------------------------------------------------------------- 1 | export const translations = { 2 | GETDATAOBJECTSBYTYPE_BUSY: "Loading objects", 3 | REQUESTPASSWORDCHANGE_BUSY: "Busy sending password reset e-mail", 4 | REQUESTPASSWORDCHANGE_DONE: "A password reset e-mail has been sent", 5 | SETSERVERSETTINGS_DONE: "Server settings successfully updated", 6 | ENABLEPLUGIN_DONE: "Plugin successfully enabled", 7 | DISABLEPLUGIN_DONE: "Plugin successfully disabled", 8 | SETDEFAULTWEBMODULE_DONE: "Default webmodule changed", 9 | SETDEFAULTQUERYENGINE_DONE: "Default Query Engine successfully changed", 10 | SETDEFAULTMODELMERGER_DONE: "Default Model Merger successfully changed", 11 | SETDEFAULTSERIALIZER_DONE: "Default Serializer successfully changed", 12 | SETDEFAULTOBJECTIDM_DONE: "Default ObjectIDM successfully changed", 13 | SETDEFAULTRENDERENGINE_DONE: "Default Render Engine successfully changed", 14 | SETDEFAULTMODELCOMPARE_DONE: "Default Model Compare successfully changed", 15 | LOGIN_BUSY: "Trying to login", 16 | CHANGEUSERTYPE_DONE: "Type of user successfully changed", 17 | ADDUSER_DONE: "User successfully added, you should receive a validation email shortly", 18 | UPDATEINTERNALSERVICE_DONE: "Internal service successfully updated", 19 | UPDATEMODELCOMPARE_DONE: "Model compare plugin successfully updated", 20 | UPDATEMODELMERGER_DONE: "Model merger successfully updated", 21 | UPDATEQUERYENGINE_DONE: "Query engine plugin successfully updated", 22 | UPDATEOBJECTIDM_DONE: "ObjectIDM succesfully updated", 23 | UPDATEDESERIALIZER_DONE: "Serializer succesfully updated", 24 | ADDUSERTOPROJECT_DONE: "User successfully added to project", 25 | REMOVEUSERFROMPROJECT_DONE: "User successfully removed from project", 26 | UNDELETEPROJECT_DONE: "Project successfully undeleted", 27 | DELETEPROJECT_DONE: "Project successfully deleted", 28 | ADDPROJECT_DONE: "Project successfully added", 29 | VALIDATEACCOUNT_DONE: "Account successfully validated, you can now login", 30 | ADDPROJECTASSUBPROJECT_DONE: "Sub project added successfully", 31 | DOWNLOADBYJSONQUERY_BUSY: "Downloading BIM", 32 | CHECKINFROMURL_DONE: "Done checking in from URL", 33 | GETLOGGEDINUSER_BUSY: "Getting user details", 34 | SETPLUGINSETTINGS_DONE: "Plugin settings successfully saved", 35 | GETSERVERINFO_BUSY: "Getting server info", 36 | GETVERSION_BUSY: "Getting server version", 37 | GETPROJECTBYPOID_BUSY: "Getting project details", 38 | GETALLRELATEDPROJECTS_BUSY: "Getting related project's details", 39 | GETSERIALIZERBYPLUGINCLASSNAME_BUSY: "Getting serializer info", 40 | CLEANUPLONGACTION_BUSY: "Cleaning up", 41 | GETREVISIONSUMMARY_BUSY: "Getting revision summary", 42 | DOWNLOADBYOIDS_BUSY: "Downloading model data", 43 | REGISTERPROGRESSHANDLER_BUSY: "Registering for updates on progress", 44 | GETALLREVISIONSOFPROJECT_BUSY: "Getting all revisions of project", 45 | GETPLUGINDESCRIPTOR_BUSY: "Getting plugin information", 46 | GETUSERSETTINGS_BUSY: "Getting user settings", 47 | GETALLQUERYENGINES_BUSY: "Getting query engines", 48 | REGISTERNEWPROJECTHANDLER_BUSY: "Registering for updates on new projects", 49 | ADDUSER_BUSY: "Adding user...", 50 | GETAVAILABLEPLUGINBUNDLES_BUSY: "Loading available plugins, this can take a while...", 51 | GETAVAILABLEPLUGINBUNDLES_DONE: "Done loading available plugins", 52 | GETINSTALLEDPLUGINBUNDLES_BUSY: "Loading installed plugins, this can take a while...", 53 | GETINSTALLEDPLUGINBUNDLES_DONE: "Done loading installed plugins", 54 | INSTALLPLUGINBUNDLE_BUSY: "Installing plugin...", 55 | INSTALLPLUGINBUNDLE_DONE: "Plugin successfully installed", 56 | GETPLUGININFORMATION_BUSY: "Getting plugin information, this can take a while for large plugins...", 57 | GETPLUGININFORMATION_DONE: "Plugin information successfully retrieved", 58 | DOWNLOAD_BUSY: "Downloading model data...", 59 | DOWNLOAD_DONE: "Model data downloaded", 60 | LOGIN_DONE: "Login successful", 61 | LOGOUT_DONE: "Logout successful", 62 | UPDATEPROJECT_DONE: "Project successfully updated", 63 | TRIGGERREVISIONSERVICE_BUSY: "Triggering service...", 64 | TRIGGERREVISIONSERVICE_DONE: "Service triggered successfully", 65 | INSTALLPLUGINBUNDLEFROMFILE_DONE: "Plugin bundle successfully installed from file", 66 | CHECKINFROMURL_BUSY: "Checking in from URL...", 67 | ERROR_REMOTE_METHOD_CALL: "Remote error (server probably down or not reachable)", 68 | GETTING_MODEL_DATA: "Getting model data...", 69 | MODEL_DATA_DONE: "Model data successfully downloaded...", 70 | INITIATECHECKIN_BUSY: "Initiating checkin...", 71 | GETTILECOUNTS_BUSY: "Counting tiles...", 72 | LISTBOUNDINGBOXES_BUSY: "Loading bounding boxes...", 73 | GETALLPROJECTS_BUSY: "Loading all projects...", 74 | GETSUGGESTEDDESERIALIZERFOREXTENSION_BUSY: "Loading deserializer...", 75 | GETDENSITYTHRESHOLD_BUSY: "Loading density information...", 76 | LISTPLUGINSINBUNDLE_DONE: "Plugins in bundle loaded successfully", 77 | CHECKINFROMURLSYNC_BUSY: "Checking in from URL...", 78 | SETPLUGINSYSTEMSETTINGS_DONE: "Plugin system settings successfully updated", 79 | INSTALLPLUGINBUNDLEFROMFILE_BUSY: "Installing plugin bundle from file..." 80 | }; --------------------------------------------------------------------------------