/* ***** BEGIN LICENSE BLOCK ***** * Distributed under the BSD license: * * Copyright (c) 2010, Ajax.org B.V. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Ajax.org B.V. nor the * names of its contributors may be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * ***** END LICENSE BLOCK ***** */ define(function(require, exports, module) { "use strict"; var oop = require("../lib/oop"); var EventEmitter = require("../lib/event_emitter").EventEmitter; var config = require("../config"); var WorkerClient = function(topLevelNamespaces, mod, classname) { this.changeListener = this.changeListener.bind(this); this.onMessage = this.onMessage.bind(this); this.onError = this.onError.bind(this); var workerUrl; if (config.get("packaged")) { workerUrl = config.moduleUrl(mod, "worker"); } else { var normalizePath = this.$normalizePath; if (typeof require.supports !== "undefined" && require.supports.indexOf("ucjs2-pinf-0") >= 0) { // We are running in the sourcemint loader. workerUrl = require.nameToUrl("ace/worker/worker_sourcemint"); } else { // We are running in RequireJS. // nameToUrl is renamed to toUrl in requirejs 2 if (require.nameToUrl && !require.toUrl) require.toUrl = require.nameToUrl; workerUrl = normalizePath(require.toUrl("ace/worker/worker", null, "_")); } var tlns = {}; topLevelNamespaces.forEach(function(ns) { tlns[ns] = normalizePath(require.toUrl(ns, null, "_").replace(/.js(\?.*)?$/, "")); }); } this.$worker = new Worker(workerUrl); this.$worker.postMessage({ init : true, tlns: tlns, module: mod, classname: classname }); this.callbackId = 1; this.callbacks = {}; this.$worker.onerror = this.onError; this.$worker.onmessage = this.onMessage; }; (function(){ oop.implement(this, EventEmitter); this.onError = function(e) { window.console && console.log && console.log(e); throw e; }; this.onMessage = function(e) { var msg = e.data; switch(msg.type) { case "log": window.console && console.log && console.log.apply(console, msg.data); break; case "event": this._emit(msg.name, {data: msg.data}); break; case "call": var callback = this.callbacks[msg.id]; if (callback) { callback(msg.data); delete this.callbacks[msg.id]; } break; } }; this.$normalizePath = function(path) { if (!location.host) // needed for file:// protocol return path; path = path.replace(/^[a-z]+:\/\/[^\/]+/, ""); // Remove domain name and rebuild it path = location.protocol + "//" + location.host // paths starting with a slash are relative to the root (host) + (path.charAt(0) == "/" ? "" : location.pathname.replace(/\/[^\/]*$/, "")) + "/" + path.replace(/^[\/]+/, ""); return path; }; this.terminate = function() { this._emit("terminate", {}); this.$worker.terminate(); this.$worker = null; this.$doc.removeEventListener("change", this.changeListener); this.$doc = null; }; this.send = function(cmd, args) { this.$worker.postMessage({command: cmd, args: args}); }; this.call = function(cmd, args, callback) { if (callback) { var id = this.callbackId++; this.callbacks[id] = callback; args.push(id); } this.send(cmd, args); }; this.emit = function(event, data) { try { // firefox refuses to clone objects which have function properties // TODO: cleanup event this.$worker.postMessage({event: event, data: {data: data.data}}); } catch(ex) {} }; this.attachToDocument = function(doc) { if(this.$doc) this.terminate(); this.$doc = doc; this.call("setValue", [doc.getValue()]); doc.on("change", this.changeListener); }; this.changeListener = function(e) { e.range = { start: e.data.range.start, end: e.data.range.end }; this.emit("change", e); }; }).call(WorkerClient.prototype); var UIWorkerClient = function(topLevelNamespaces, mod, classname) { this.changeListener = this.changeListener.bind(this); this.callbackId = 1; this.callbacks = {}; this.messageBuffer = []; var main = null; var sender = Object.create(EventEmitter); var _self = this; this.$worker = {} this.$worker.postMessage = function(e) { _self.messageBuffer.push(e); main && setTimeout(processNext); }; var processNext = function() { var msg = _self.messageBuffer.shift(); if (msg.command) main[msg.command].apply(main, msg.args); else if (msg.event) sender._emit(msg.event, msg.data); }; sender.postMessage = function(msg) { _self.onMessage({data: msg}); }; sender.callback = function(data, callbackId) { this.postMessage({type: "call", id: callbackId, data: data}); }; sender.emit = function(name, data) { this.postMessage({type: "event", name: name, data: data}); }; require([mod], function(Main) { main = new Main[classname](sender); while (_self.messageBuffer.length) processNext(); }); }; UIWorkerClient.prototype = WorkerClient.prototype; exports.UIWorkerClient = UIWorkerClient; exports.WorkerClient = WorkerClient; });