[go: nahoru, domu]

Skip to content

Commit

Permalink
Vendor in an IE11 polyfill for Promises
Browse files Browse the repository at this point in the history
This commit introduces a polyfill to add support for Promises in IE11.
This means IE11 can be tested without first running
`utils/as_require.js`.
  • Loading branch information
DirectXMan12 committed Mar 21, 2017
1 parent adfc9d3 commit 152c399
Show file tree
Hide file tree
Showing 7 changed files with 295 additions and 21 deletions.
2 changes: 2 additions & 0 deletions LICENSE.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ licenses (all MPL 2.0 compatible):

vendor/browser-es-module-loader: MIT

vendor/promise.js : MIT

Any other files not mentioned above are typically marked with
a copyright/license header at the top of the file. The default noVNC
license is MPL-2.0.
Expand Down
41 changes: 25 additions & 16 deletions utils/use_require.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,24 +14,29 @@ program
.parse(process.argv);

// the various important paths
var main_path = path.resolve(__dirname, '..');
var core_path = path.resolve(__dirname, '..', 'core');
var app_path = path.resolve(__dirname, '..', 'app');
var vendor_path = path.resolve(__dirname, '..', 'vendor');
var out_dir_base = path.resolve(__dirname, '..', 'build');
var lib_dir_base = path.resolve(__dirname, '..', 'lib');
const paths = {
main: path.resolve(__dirname, '..'),
core: path.resolve(__dirname, '..', 'core'),
app: path.resolve(__dirname, '..', 'app'),
vendor: path.resolve(__dirname, '..', 'vendor'),
out_dir_base: path.resolve(__dirname, '..', 'build'),
lib_dir_base: path.resolve(__dirname, '..', 'lib'),
};

const no_copy_files = new Set([
// skip these -- they don't belong in the processed application
path.join(vendor_path, 'sinon.js'),
path.join(vendor_path, 'browser-es-module-loader'),
path.join(paths.vendor, 'sinon.js'),
path.join(paths.vendor, 'browser-es-module-loader'),
path.join(paths.vendor, 'promise.js'),
]);

const no_transform_files = new Set([
// don't transform this -- we want it imported as-is to properly catch loading errors
path.join(app_path, 'error-handler.js'),
path.join(paths.app, 'error-handler.js'),
]);

no_copy_files.forEach((file) => no_transform_files.add(file));

// walkDir *recursively* walks directories trees,
// calling the callback for all normal files found.
var walkDir = function (base_path, cb, filter) {
Expand All @@ -55,7 +60,7 @@ var walkDir = function (base_path, cb, filter) {
var transform_html = function (new_script) {
// write out the modified vnc.html file that works with the bundle
var src_html_path = path.resolve(__dirname, '..', 'vnc.html');
var out_html_path = path.resolve(out_dir_base, 'vnc.html');
var out_html_path = path.resolve(paths.out_dir_base, 'vnc.html');
fs.readFile(src_html_path, (err, contents_raw) => {
if (err) { throw err; }

Expand Down Expand Up @@ -92,10 +97,10 @@ var make_lib_files = function (import_format, source_maps, with_app_dir) {

var in_path;
if (with_app_dir) {
var out_path_base = out_dir_base;
in_path = main_path;
var out_path_base = paths.out_dir_base;
in_path = paths.main;
} else {
var out_path_base = lib_dir_base;
var out_path_base = paths.lib_dir_base;
}

fse.ensureDirSync(out_path_base);
Expand Down Expand Up @@ -144,11 +149,15 @@ var make_lib_files = function (import_format, source_maps, with_app_dir) {
});
};

walkDir(core_path, handleDir.bind(null, true, in_path || core_path), (filename, stats) => !no_copy_files.has(filename));
walkDir(vendor_path, handleDir.bind(null, true, in_path || main_path), (filename, stats) => !no_copy_files.has(filename));
if (with_app_dir && helper && helper.noCopyOverride) {
helper.noCopyOverride(paths, no_copy_files);
}

walkDir(paths.core, handleDir.bind(null, true, in_path || paths.core), (filename, stats) => !no_copy_files.has(filename));
walkDir(paths.vendor, handleDir.bind(null, true, in_path || paths.main), (filename, stats) => !no_copy_files.has(filename));

if (with_app_dir) {
walkDir(app_path, handleDir.bind(null, false, in_path || app_path), (filename, stats) => !no_copy_files.has(filename));
walkDir(paths.app, handleDir.bind(null, false, in_path || paths.app), (filename, stats) => !no_copy_files.has(filename));

const out_app_path = path.join(out_path_base, 'app.js');
if (helper && helper.appWriter) {
Expand Down
8 changes: 7 additions & 1 deletion utils/use_require_helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ module.exports = {
console.log(`Please place RequireJS in ${path.join(base_out_path, 'require.js')}`);
return `<script src="require.js" data-main="${path.relative(base_out_path, out_path)}"></script>`;
},
noCopyOverride: () => {},
},
'commonjs': {
optionsOverride: (opts) => {
Expand All @@ -23,12 +24,17 @@ module.exports = {
b.bundle().pipe(fs.createWriteStream(out_path));
return `<script src="${path.relative(base_out_path, out_path)}"></script>`;
},
noCopyOverride: () => {},
},
'systemjs': {
appWriter: (base_out_path, out_path) => {
fs.writeFile(out_path, 'SystemJS.import("./app/ui.js");', (err) => { if (err) throw err; });
console.log(`Please place SystemJS in ${path.join(base_out_path, 'system-production.js')}`);
return `<script src="system-production.js"></script>\n<script src="${path.relative(base_out_path, out_path)}"></script>`;
return `<script src="vendor/promise.js"></script>
<script src="system-production.js"></script>\n<script src="${path.relative(base_out_path, out_path)}"></script>`;
},
noCopyOverride: (paths, no_copy_files) => {
no_copy_files.delete(path.join(paths.vendor, 'promise.js'));
},
},
'umd': {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1343,9 +1343,9 @@ WorkerPool.prototype = {
},

_stop: function () {
for (let wrkr of this._workers) {
this._workers.forEach(function(wrkr) {
wrkr.terminate();
}
});
}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,9 @@ WorkerPool.prototype = {
},

_stop: function () {
for (let wrkr of this._workers) {
this._workers.forEach(function(wrkr) {
wrkr.terminate();
}
});
}
};

Expand Down
255 changes: 255 additions & 0 deletions vendor/promise.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,255 @@
/* Copyright (c) 2014 Taylor Hakes
* Copyright (c) 2014 Forbes Lindesay
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

(function (root) {

// Store setTimeout reference so promise-polyfill will be unaffected by
// other code modifying setTimeout (like sinon.useFakeTimers())
var setTimeoutFunc = setTimeout;

function noop() {}

// Polyfill for Function.prototype.bind
function bind(fn, thisArg) {
return function () {
fn.apply(thisArg, arguments);
};
}

function Promise(fn) {
if (typeof this !== 'object') throw new TypeError('Promises must be constructed via new');
if (typeof fn !== 'function') throw new TypeError('not a function');
this._state = 0;
this._handled = false;
this._value = undefined;
this._deferreds = [];

doResolve(fn, this);
}

function handle(self, deferred) {
while (self._state === 3) {
self = self._value;
}
if (self._state === 0) {
self._deferreds.push(deferred);
return;
}
self._handled = true;
Promise._immediateFn(function () {
var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
if (cb === null) {
(self._state === 1 ? resolve : reject)(deferred.promise, self._value);
return;
}
var ret;
try {
ret = cb(self._value);
} catch (e) {
reject(deferred.promise, e);
return;
}
resolve(deferred.promise, ret);
});
}

function resolve(self, newValue) {
try {
// Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.');
if (newValue && (typeof newValue === 'object' || typeof newValue === 'function')) {
var then = newValue.then;
if (newValue instanceof Promise) {
self._state = 3;
self._value = newValue;
finale(self);
return;
} else if (typeof then === 'function') {
doResolve(bind(then, newValue), self);
return;
}
}
self._state = 1;
self._value = newValue;
finale(self);
} catch (e) {
reject(self, e);
}
}

function reject(self, newValue) {
self._state = 2;
self._value = newValue;
finale(self);
}

function finale(self) {
if (self._state === 2 && self._deferreds.length === 0) {
Promise._immediateFn(function() {
if (!self._handled) {
Promise._unhandledRejectionFn(self._value);
}
});
}

for (var i = 0, len = self._deferreds.length; i < len; i++) {
handle(self, self._deferreds[i]);
}
self._deferreds = null;
}

function Handler(onFulfilled, onRejected, promise) {
this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
this.onRejected = typeof onRejected === 'function' ? onRejected : null;
this.promise = promise;
}

/**
* Take a potentially misbehaving resolver function and make sure
* onFulfilled and onRejected are only called once.
*
* Makes no guarantees about asynchrony.
*/
function doResolve(fn, self) {
var done = false;
try {
fn(function (value) {
if (done) return;
done = true;
resolve(self, value);
}, function (reason) {
if (done) return;
done = true;
reject(self, reason);
});
} catch (ex) {
if (done) return;
done = true;
reject(self, ex);
}
}

Promise.prototype['catch'] = function (onRejected) {
return this.then(null, onRejected);
};

Promise.prototype.then = function (onFulfilled, onRejected) {
var prom = new (this.constructor)(noop);

handle(this, new Handler(onFulfilled, onRejected, prom));
return prom;
};

Promise.all = function (arr) {
var args = Array.prototype.slice.call(arr);

return new Promise(function (resolve, reject) {
if (args.length === 0) return resolve([]);
var remaining = args.length;

function res(i, val) {
try {
if (val && (typeof val === 'object' || typeof val === 'function')) {
var then = val.then;
if (typeof then === 'function') {
then.call(val, function (val) {
res(i, val);
}, reject);
return;
}
}
args[i] = val;
if (--remaining === 0) {
resolve(args);
}
} catch (ex) {
reject(ex);
}
}

for (var i = 0; i < args.length; i++) {
res(i, args[i]);
}
});
};

Promise.resolve = function (value) {
if (value && typeof value === 'object' && value.constructor === Promise) {
return value;
}

return new Promise(function (resolve) {
resolve(value);
});
};

Promise.reject = function (value) {
return new Promise(function (resolve, reject) {
reject(value);
});
};

Promise.race = function (values) {
return new Promise(function (resolve, reject) {
for (var i = 0, len = values.length; i < len; i++) {
values[i].then(resolve, reject);
}
});
};

// Use polyfill for setImmediate for performance gains
Promise._immediateFn = (typeof setImmediate === 'function' && function (fn) { setImmediate(fn); }) ||
function (fn) {
setTimeoutFunc(fn, 0);
};

Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
if (typeof console !== 'undefined' && console) {
console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
}
};

/**
* Set the immediate function to execute callbacks
* @param fn {function} Function to execute
* @deprecated
*/
Promise._setImmediateFn = function _setImmediateFn(fn) {
Promise._immediateFn = fn;
};

/**
* Change the function to execute on unhandled rejection
* @param {function} fn Function to execute on unhandled rejection
* @deprecated
*/
Promise._setUnhandledRejectionFn = function _setUnhandledRejectionFn(fn) {
Promise._unhandledRejectionFn = fn;
};

if (typeof module !== 'undefined' && module.exports) {
module.exports = Promise;
} else if (!root.Promise) {
root.Promise = Promise;
}

})(this);
Loading

0 comments on commit 152c399

Please sign in to comment.