diff --git a/README.md b/README.md index 060fb42..be632b1 100755 --- a/README.md +++ b/README.md @@ -7,7 +7,11 @@ - [Mozilla Firefox](https://www.mozilla.org/ru/firefox/new/) (v43+). Начиная с версии 52, с [расширением](https://www.cryptopro.ru/sites/default/files/products/cades/extensions/cryptopro_extension_for_cades_browser_plug_in-1.1.1-an+fx-windows.xpi). - [Internet Explorer](http://windows.microsoft.com/ru-ru/internet-explorer/download-ie) (v8+). -Полифилл для Promise необходимо подключать самостоятельно. +Список необходимых полифиллов (подключаются самостоятельно): +- Promise +- EventTarget.addEventListener +- Array.prototype.forEach +- Array.prototype.map ## Установка и настройка ```bash diff --git a/example/index.html b/example/index.html index 37c3149..0f03004 100755 --- a/example/index.html +++ b/example/index.html @@ -3,7 +3,6 @@ Пример использования CryptoPro - @@ -16,7 +15,20 @@ publicPath: '../dist/' }; + + + + + + + + + + + + + diff --git a/example/polyfills/addEventListener.js b/example/polyfills/addEventListener.js new file mode 100644 index 0000000..c38f96d --- /dev/null +++ b/example/polyfills/addEventListener.js @@ -0,0 +1,32 @@ +!window.addEventListener && (function (WindowPrototype, DocumentPrototype, ElementPrototype, addEventListener, removeEventListener, dispatchEvent, registry) { + WindowPrototype[addEventListener] = DocumentPrototype[addEventListener] = ElementPrototype[addEventListener] = function (type, listener) { + var target = this; + + registry.unshift([target, type, listener, function (event) { + event.currentTarget = target; + event.preventDefault = function () { + event.returnValue = false + }; + event.stopPropagation = function () { + event.cancelBubble = true + }; + event.target = event.srcElement || target; + + listener.call(target, event); + }]); + + this.attachEvent("on" + type, registry[0][3]); + }; + + WindowPrototype[removeEventListener] = DocumentPrototype[removeEventListener] = ElementPrototype[removeEventListener] = function (type, listener) { + for (var index = 0, register; register = registry[index]; ++index) { + if (register[0] == this && register[1] == type && register[2] == listener) { + return this.detachEvent("on" + type, registry.splice(index, 1)[0][3]); + } + } + }; + + WindowPrototype[dispatchEvent] = DocumentPrototype[dispatchEvent] = ElementPrototype[dispatchEvent] = function (eventObject) { + return this.fireEvent("on" + eventObject.type, eventObject); + }; +})(Window.prototype, HTMLDocument.prototype, Element.prototype, "addEventListener", "removeEventListener", "dispatchEvent", []); \ No newline at end of file diff --git a/example/polyfills/atob-btoa.js b/example/polyfills/atob-btoa.js new file mode 100644 index 0000000..43d9b10 --- /dev/null +++ b/example/polyfills/atob-btoa.js @@ -0,0 +1,76 @@ +(function (root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define([], function () { + factory(root); + }); + } else factory(root); +// node.js has always supported base64 conversions, while browsers that support +// web workers support base64 too, but you may never know. +})(typeof exports !== "undefined" ? exports : this, function (root) { + if (root.atob) { + // Some browsers' implementation of atob doesn't support whitespaces + // in the encoded string (notably, IE). This wraps the native atob + // in a function that strips the whitespaces. + // The original function can be retrieved in atob.original + try { + root.atob(" "); + } catch (e) { + root.atob = (function (atob) { + var func = function (string) { + return atob(String(string).replace(/[\t\n\f\r ]+/g, "")); + }; + func.original = atob; + return func; + })(root.atob); + } + return; + } + + // base64 character set, plus padding character (=) + var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", + // Regular expression to check formal correctness of base64 encoded strings + b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/; + + root.btoa = function (string) { + string = String(string); + var bitmap, a, b, c, + result = "", i = 0, + rest = string.length % 3; // To determine the final padding + + for (; i < string.length;) { + if ((a = string.charCodeAt(i++)) > 255 + || (b = string.charCodeAt(i++)) > 255 + || (c = string.charCodeAt(i++)) > 255) + throw new TypeError("Failed to execute 'btoa' on 'Window': The string to be encoded contains characters outside of the Latin1 range."); + + bitmap = (a << 16) | (b << 8) | c; + result += b64.charAt(bitmap >> 18 & 63) + b64.charAt(bitmap >> 12 & 63) + + b64.charAt(bitmap >> 6 & 63) + b64.charAt(bitmap & 63); + } + + // If there's need of padding, replace the last 'A's with equal signs + return rest ? result.slice(0, rest - 3) + "===".substring(rest) : result; + }; + + root.atob = function (string) { + // atob can work with strings with whitespaces, even inside the encoded part, + // but only \t, \n, \f, \r and ' ', which can be stripped. + string = String(string).replace(/[\t\n\f\r ]+/g, ""); + if (!b64re.test(string)) + throw new TypeError("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded."); + + // Adding the padding if missing, for semplicity + string += "==".slice(2 - (string.length & 3)); + var bitmap, result = "", r1, r2, i = 0; + for (; i < string.length;) { + bitmap = b64.indexOf(string.charAt(i++)) << 18 | b64.indexOf(string.charAt(i++)) << 12 + | (r1 = b64.indexOf(string.charAt(i++))) << 6 | (r2 = b64.indexOf(string.charAt(i++))); + + result += r1 === 64 ? String.fromCharCode(bitmap >> 16 & 255) + : r2 === 64 ? String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255) + : String.fromCharCode(bitmap >> 16 & 255, bitmap >> 8 & 255, bitmap & 255); + } + return result; + }; +}); \ No newline at end of file diff --git a/example/polyfills/forEach.js b/example/polyfills/forEach.js new file mode 100644 index 0000000..ff2481c --- /dev/null +++ b/example/polyfills/forEach.js @@ -0,0 +1,59 @@ +// Шаги алгоритма ECMA-262, 5-е издание, 15.4.4.18 +// Ссылка (en): http://es5.github.io/#x15.4.4.18 +// Ссылка (ru): http://es5.javascript.ru/x15.4.html#x15.4.4.18 +if (!Array.prototype.forEach) { + + Array.prototype.forEach = function (callback, thisArg) { + + var T, k; + + if (this == null) { + throw new TypeError(' this is null or not defined'); + } + + // 1. Положим O равным результату вызова ToObject passing the |this| value as the argument. + var O = Object(this); + + // 2. Положим lenValue равным результату вызова внутреннего метода Get объекта O с аргументом "length". + // 3. Положим len равным ToUint32(lenValue). + var len = O.length >>> 0; + + // 4. Если IsCallable(callback) равен false, выкинем исключение TypeError. + // Смотрите: http://es5.github.com/#x9.11 + if (typeof callback !== 'function') { + throw new TypeError(callback + ' is not a function'); + } + + // 5. Если thisArg присутствует, положим T равным thisArg; иначе положим T равным undefined. + if (arguments.length > 1) { + T = thisArg; + } + + // 6. Положим k равным 0 + k = 0; + + // 7. Пока k < len, будем повторять + while (k < len) { + + var kValue; + + // a. Положим Pk равным ToString(k). + // Это неявное преобразование для левостороннего операнда в операторе in + // b. Положим kPresent равным результату вызова внутреннего метода HasProperty объекта O с аргументом Pk. + // Этот шаг может быть объединён с шагом c + // c. Если kPresent равен true, то + if (k in O) { + + // i. Положим kValue равным результату вызова внутреннего метода Get объекта O с аргументом Pk. + kValue = O[k]; + + // ii. Вызовем внутренний метод Call функции callback с объектом T в качестве значения this и + // списком аргументов, содержащим kValue, k и O. + callback.call(T, kValue, k, O); + } + // d. Увеличим k на 1. + k++; + } + // 8. Вернём undefined. + }; +} \ No newline at end of file diff --git a/example/polyfills/map.js b/example/polyfills/map.js new file mode 100644 index 0000000..5ea7c9a --- /dev/null +++ b/example/polyfills/map.js @@ -0,0 +1,91 @@ +// Шаги алгоритма ECMA-262, 5-е издание, 15.4.4.19 +// Ссылка (en): http://es5.github.com/#x15.4.4.19 +// Ссылка (ru): http://es5.javascript.ru/x15.4.html#x15.4.4.19 +if (!Array.prototype.map) { + + Array.prototype.map = function(callback, thisArg) { + + var T, A, k; + + if (this == null) { + throw new TypeError(' this is null or not defined'); + } + + // 1. Положим O равным результату вызова ToObject с передачей ему + // значения |this| в качестве аргумента. + var O = Object(this); + + // 2. Положим lenValue равным результату вызова внутреннего метода Get + // объекта O с аргументом "length". + // 3. Положим len равным ToUint32(lenValue). + var len = O.length >>> 0; + + // 4. Если вызов IsCallable(callback) равен false, выкидываем исключение TypeError. + // Смотрите (en): http://es5.github.com/#x9.11 + // Смотрите (ru): http://es5.javascript.ru/x9.html#x9.11 + if (typeof callback !== 'function') { + throw new TypeError(callback + ' is not a function'); + } + + // 5. Если thisArg присутствует, положим T равным thisArg; иначе положим T равным undefined. + if (arguments.length > 1) { + T = thisArg; + } + + // 6. Положим A равным новому масиву, как если бы он был создан выражением new Array(len), + // где Array является стандартным встроенным конструктором с этим именем, + // а len является значением len. + A = new Array(len); + + // 7. Положим k равным 0 + k = 0; + + // 8. Пока k < len, будем повторять + while (k < len) { + + var kValue, mappedValue; + + // a. Положим Pk равным ToString(k). + // Это неявное преобразование для левостороннего операнда в операторе in + // b. Положим kPresent равным результату вызова внутреннего метода HasProperty + // объекта O с аргументом Pk. + // Этот шаг может быть объединён с шагом c + // c. Если kPresent равен true, то + if (k in O) { + + // i. Положим kValue равным результату вызова внутреннего метода Get + // объекта O с аргументом Pk. + kValue = O[k]; + + // ii. Положим mappedValue равным результату вызова внутреннего метода Call + // функции callback со значением T в качестве значения this и списком + // аргументов, содержащим kValue, k и O. + mappedValue = callback.call(T, kValue, k, O); + + // iii. Вызовем внутренний метод DefineOwnProperty объекта A с аргументами + // Pk, Описатель Свойства + // { Value: mappedValue, + // Writable: true, + // Enumerable: true, + // Configurable: true } + // и false. + + // В браузерах, поддерживающих Object.defineProperty, используем следующий код: + // Object.defineProperty(A, k, { + // value: mappedValue, + // writable: true, + // enumerable: true, + // configurable: true + // }); + + // Для лучшей поддержки браузерами, используем следующий код: + A[k] = mappedValue; + } + // d. Увеличим k на 1. + k++; + } + + // 9. Вернём A. + return A; + }; +} \ No newline at end of file diff --git a/example/polyfills/promise.js b/example/polyfills/promise.js new file mode 100644 index 0000000..56786ec --- /dev/null +++ b/example/polyfills/promise.js @@ -0,0 +1,236 @@ +(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); \ No newline at end of file