mirror of
https://github.com/discourse/discourse.git
synced 2024-11-25 09:42:07 +08:00
FEATURE: Create IE Support Plugin (#8520)
This core plugin, which could be split off in the future, allows us to load IE specific code on demand. Co-authored-by: jjaffeux <j.jaffeux@gmail.com>
This commit is contained in:
parent
5431ae0a17
commit
4abe4454dd
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -53,6 +53,7 @@ bootsnap-compile-cache/
|
|||
!/plugins/discourse-narrative-bot
|
||||
!/plugins/discourse-presence
|
||||
!/plugins/discourse-local-dates
|
||||
!/plugins/discourse-internet-explorer
|
||||
/plugins/*/auto_generated/
|
||||
|
||||
/spec/fixtures/plugins/my_plugin/auto_generated
|
||||
|
|
|
@ -1,185 +1,9 @@
|
|||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
|
||||
if (!Object.entries) {
|
||||
Object.entries = function(obj) {
|
||||
var ownProps = Object.keys(obj),
|
||||
i = ownProps.length,
|
||||
resArray = new Array(i); // preallocate the Array
|
||||
while (i--) resArray[i] = [ownProps[i], obj[ownProps[i]]];
|
||||
|
||||
return resArray;
|
||||
};
|
||||
}
|
||||
|
||||
// adapted from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
|
||||
// missing in ie only
|
||||
if (!Object.values) {
|
||||
Object.values = function(obj) {
|
||||
var ownProps = Object.keys(obj),
|
||||
i = ownProps.length,
|
||||
resArray = new Array(i); // preallocate the Array
|
||||
while (i--) resArray[i] = obj[ownProps[i]];
|
||||
|
||||
return resArray;
|
||||
};
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
|
||||
if (typeof Object.assign !== "function") {
|
||||
// Must be writable: true, enumerable: false, configurable: true
|
||||
Object.defineProperty(Object, "assign", {
|
||||
value: function assign(target) {
|
||||
// .length of function is 2
|
||||
"use strict";
|
||||
if (target == null) {
|
||||
// TypeError if undefined or null
|
||||
throw new TypeError("Cannot convert undefined or null to object");
|
||||
}
|
||||
|
||||
var to = Object(target);
|
||||
|
||||
for (var index = 1; index < arguments.length; index++) {
|
||||
var nextSource = arguments[index];
|
||||
|
||||
if (nextSource != null) {
|
||||
// Skip over if undefined or null
|
||||
for (var nextKey in nextSource) {
|
||||
// Avoid bugs when hasOwnProperty is shadowed
|
||||
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
||||
to[nextKey] = nextSource[nextKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
},
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes#Polyfill
|
||||
/* eslint-disable */
|
||||
if (!Array.prototype.includes) {
|
||||
Object.defineProperty(Array.prototype, "includes", {
|
||||
value: function(searchElement, fromIndex) {
|
||||
if (this == null) {
|
||||
throw new TypeError('"this" is null or not defined');
|
||||
}
|
||||
|
||||
// 1. Let O be ? ToObject(this value).
|
||||
var o = Object(this);
|
||||
|
||||
// 2. Let len be ? ToLength(? Get(O, "length")).
|
||||
var len = o.length >>> 0;
|
||||
|
||||
// 3. If len is 0, return false.
|
||||
if (len === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Let n be ? ToInteger(fromIndex).
|
||||
// (If fromIndex is undefined, this step produces the value 0.)
|
||||
var n = fromIndex | 0;
|
||||
|
||||
// 5. If n ≥ 0, then
|
||||
// a. Let k be n.
|
||||
// 6. Else n < 0,
|
||||
// a. Let k be len + n.
|
||||
// b. If k < 0, let k be 0.
|
||||
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
|
||||
|
||||
function sameValueZero(x, y) {
|
||||
return (
|
||||
x === y ||
|
||||
(typeof x === "number" &&
|
||||
typeof y === "number" &&
|
||||
isNaN(x) &&
|
||||
isNaN(y))
|
||||
);
|
||||
}
|
||||
|
||||
// 7. Repeat, while k < len
|
||||
while (k < len) {
|
||||
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
|
||||
// b. If SameValueZero(searchElement, elementK) is true, return true.
|
||||
if (sameValueZero(o[k], searchElement)) {
|
||||
return true;
|
||||
}
|
||||
// c. Increase k by 1.
|
||||
k++;
|
||||
}
|
||||
|
||||
// 8. Return false
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes#Polyfill
|
||||
if (!String.prototype.includes) {
|
||||
Object.defineProperty(String.prototype, "includes", {
|
||||
value: function(search, start) {
|
||||
if (typeof start !== "number") {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
if (start + search.length > this.length) {
|
||||
return false;
|
||||
} else {
|
||||
return this.indexOf(search, start) !== -1;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// https://tc39.github.io/ecma262/#sec-array.prototype.find
|
||||
if (!Array.prototype.find) {
|
||||
Object.defineProperty(Array.prototype, "find", {
|
||||
value: function(predicate) {
|
||||
// 1. Let O be ? ToObject(this value).
|
||||
if (this == null) {
|
||||
throw new TypeError('"this" is null or not defined');
|
||||
}
|
||||
|
||||
var o = Object(this);
|
||||
|
||||
// 2. Let len be ? ToLength(? Get(O, "length")).
|
||||
var len = o.length >>> 0;
|
||||
|
||||
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
|
||||
if (typeof predicate !== "function") {
|
||||
throw new TypeError("predicate must be a function");
|
||||
}
|
||||
|
||||
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
||||
var thisArg = arguments[1];
|
||||
|
||||
// 5. Let k be 0.
|
||||
var k = 0;
|
||||
|
||||
// 6. Repeat, while k < len
|
||||
while (k < len) {
|
||||
// a. Let Pk be ! ToString(k).
|
||||
// b. Let kValue be ? Get(O, Pk).
|
||||
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
|
||||
// d. If testResult is true, return kValue.
|
||||
var kValue = o[k];
|
||||
if (predicate.call(thisArg, kValue, k, o)) {
|
||||
return kValue;
|
||||
}
|
||||
// e. Increase k by 1.
|
||||
k++;
|
||||
}
|
||||
|
||||
// 7. Return undefined.
|
||||
return undefined;
|
||||
},
|
||||
configurable: true,
|
||||
writable: true
|
||||
});
|
||||
}
|
||||
// Any IE only polyfill should be moved in discourse-internet-explorer plugin
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags#Polyfill
|
||||
// IE and EDGE
|
||||
if (RegExp.prototype.flags === undefined) {
|
||||
Object.defineProperty(RegExp.prototype, "flags", {
|
||||
configurable: true,
|
||||
|
@ -189,181 +13,4 @@ if (RegExp.prototype.flags === undefined) {
|
|||
});
|
||||
}
|
||||
|
||||
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
|
||||
if (!String.prototype.padStart) {
|
||||
String.prototype.padStart = function padStart(targetLength, padString) {
|
||||
targetLength = targetLength >> 0; //truncate if number, or convert non-number to 0;
|
||||
padString = String(typeof padString !== "undefined" ? padString : " ");
|
||||
if (this.length >= targetLength) {
|
||||
return String(this);
|
||||
} else {
|
||||
targetLength = targetLength - this.length;
|
||||
if (targetLength > padString.length) {
|
||||
padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
|
||||
}
|
||||
return padString.slice(0, targetLength) + String(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
|
||||
if (!String.prototype.padEnd) {
|
||||
String.prototype.padEnd = function padEnd(targetLength, padString) {
|
||||
targetLength = targetLength >> 0; //floor if number or convert non-number to 0;
|
||||
padString = String(typeof padString !== "undefined" ? padString : " ");
|
||||
if (this.length > targetLength) {
|
||||
return String(this);
|
||||
} else {
|
||||
targetLength = targetLength - this.length;
|
||||
if (targetLength > padString.length) {
|
||||
padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
|
||||
}
|
||||
return String(this) + padString.slice(0, targetLength);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
|
||||
// Production steps of ECMA-262, Edition 6, 22.1.2.1
|
||||
if (!Array.from) {
|
||||
Array.from = (function() {
|
||||
var toStr = Object.prototype.toString;
|
||||
var isCallable = function(fn) {
|
||||
return typeof fn === "function" || toStr.call(fn) === "[object Function]";
|
||||
};
|
||||
var toInteger = function(value) {
|
||||
var number = Number(value);
|
||||
if (isNaN(number)) {
|
||||
return 0;
|
||||
}
|
||||
if (number === 0 || !isFinite(number)) {
|
||||
return number;
|
||||
}
|
||||
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
|
||||
};
|
||||
var maxSafeInteger = Math.pow(2, 53) - 1;
|
||||
var toLength = function(value) {
|
||||
var len = toInteger(value);
|
||||
return Math.min(Math.max(len, 0), maxSafeInteger);
|
||||
};
|
||||
|
||||
// The length property of the from method is 1.
|
||||
return function from(arrayLike /*, mapFn, thisArg */) {
|
||||
// 1. Let C be the this value.
|
||||
var C = this;
|
||||
|
||||
// 2. Let items be ToObject(arrayLike).
|
||||
var items = Object(arrayLike);
|
||||
|
||||
// 3. ReturnIfAbrupt(items).
|
||||
if (arrayLike == null) {
|
||||
throw new TypeError(
|
||||
"Array.from requires an array-like object - not null or undefined"
|
||||
);
|
||||
}
|
||||
|
||||
// 4. If mapfn is undefined, then let mapping be false.
|
||||
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
|
||||
var T;
|
||||
if (typeof mapFn !== "undefined") {
|
||||
// 5. else
|
||||
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
|
||||
if (!isCallable(mapFn)) {
|
||||
throw new TypeError(
|
||||
"Array.from: when provided, the second argument must be a function"
|
||||
);
|
||||
}
|
||||
|
||||
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
||||
if (arguments.length > 2) {
|
||||
T = arguments[2];
|
||||
}
|
||||
}
|
||||
|
||||
// 10. Let lenValue be Get(items, "length").
|
||||
// 11. Let len be ToLength(lenValue).
|
||||
var len = toLength(items.length);
|
||||
|
||||
// 13. If IsConstructor(C) is true, then
|
||||
// 13. a. Let A be the result of calling the [[Construct]] internal method
|
||||
// of C with an argument list containing the single item len.
|
||||
// 14. a. Else, Let A be ArrayCreate(len).
|
||||
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
|
||||
|
||||
// 16. Let k be 0.
|
||||
var k = 0;
|
||||
// 17. Repeat, while k < len… (also steps a - h)
|
||||
var kValue;
|
||||
while (k < len) {
|
||||
kValue = items[k];
|
||||
if (mapFn) {
|
||||
A[k] =
|
||||
typeof T === "undefined"
|
||||
? mapFn(kValue, k)
|
||||
: mapFn.call(T, kValue, k);
|
||||
} else {
|
||||
A[k] = kValue;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
// 18. Let putStatus be Put(A, "length", len, true).
|
||||
A.length = len;
|
||||
// 20. Return A.
|
||||
return A;
|
||||
};
|
||||
})();
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat#Polyfill
|
||||
if (!String.prototype.repeat) {
|
||||
String.prototype.repeat = function(count) {
|
||||
"use strict";
|
||||
if (this == null)
|
||||
throw new TypeError("can't convert " + this + " to object");
|
||||
|
||||
var str = "" + this;
|
||||
// To convert string to integer.
|
||||
count = +count;
|
||||
// Check NaN
|
||||
if (count != count) count = 0;
|
||||
|
||||
if (count < 0) throw new RangeError("repeat count must be non-negative");
|
||||
|
||||
if (count == Infinity)
|
||||
throw new RangeError("repeat count must be less than infinity");
|
||||
|
||||
count = Math.floor(count);
|
||||
if (str.length == 0 || count == 0) return "";
|
||||
|
||||
// Ensuring count is a 31-bit integer allows us to heavily optimize the
|
||||
// main part. But anyway, most current (August 2014) browsers can't handle
|
||||
// strings 1 << 28 chars or longer, so:
|
||||
if (str.length * count >= 1 << 28)
|
||||
throw new RangeError(
|
||||
"repeat count must not overflow maximum string size"
|
||||
);
|
||||
|
||||
var maxCount = str.length * count;
|
||||
count = Math.floor(Math.log(count) / Math.log(2));
|
||||
while (count) {
|
||||
str += str;
|
||||
count--;
|
||||
}
|
||||
str += str.substring(0, maxCount - str.length);
|
||||
return str;
|
||||
};
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/fr/docs/Web/API/NodeList/forEach
|
||||
if (window.NodeList && !NodeList.prototype.forEach) {
|
||||
NodeList.prototype.forEach = function(callback, thisArg) {
|
||||
thisArg = thisArg || window;
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
callback.call(thisArg, this[i], i, this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/* eslint-enable */
|
||||
|
|
|
@ -76,46 +76,6 @@
|
|||
}
|
||||
}
|
||||
|
||||
//IE11 Support
|
||||
@media screen and (max-width: 767px) {
|
||||
table.staff-logs tr {
|
||||
display: -ms-grid;
|
||||
-ms-grid-columns: 1fr 1fr 1fr 0.5fr;
|
||||
-ms-grid-rows: auto auto;
|
||||
td {
|
||||
display: -ms-grid;
|
||||
&.staff-users {
|
||||
-ms-grid-row: 1;
|
||||
-ms-grid-column: 1;
|
||||
-ms-grid-column-span: 2;
|
||||
}
|
||||
&.created-at {
|
||||
-ms-grid-row: 1;
|
||||
-ms-grid-column: 4;
|
||||
}
|
||||
&.action {
|
||||
-ms-grid-row: 2;
|
||||
-ms-grid-column: 1;
|
||||
}
|
||||
&.subject {
|
||||
-ms-grid-row: 2;
|
||||
-ms-grid-column: 2;
|
||||
-ms-grid-column-span: 3;
|
||||
}
|
||||
&.details {
|
||||
-ms-grid-row: 3;
|
||||
-ms-grid-column: 1;
|
||||
-ms-grid-column-span: 3;
|
||||
}
|
||||
&.context {
|
||||
-ms-grid-row: 4;
|
||||
-ms-grid-column: 1;
|
||||
-ms-grid-column-span: 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@include breakpoint(mobile-extra-large) {
|
||||
table.staff-logs tr {
|
||||
grid-template-columns: 1fr 1fr 0.5fr;
|
||||
|
|
|
@ -84,7 +84,7 @@ class Plugin::Instance
|
|||
|
||||
def register_anonymous_cache_key(key, &block)
|
||||
key_method = "key_#{key}"
|
||||
add_to_class(Middleware::AnonymousCache, key_method, &block)
|
||||
add_to_class(Middleware::AnonymousCache::Helper, key_method, &block)
|
||||
Middleware::AnonymousCache.cache_key_segments[key] = key_method
|
||||
Middleware::AnonymousCache.compile_key_builder
|
||||
end
|
||||
|
|
|
@ -72,7 +72,8 @@ class Plugin::Metadata
|
|||
"discourse-rss-polling",
|
||||
"docker_manager",
|
||||
"lazy-yt",
|
||||
"poll"
|
||||
"poll",
|
||||
"discourse-internet-explorer"
|
||||
])
|
||||
|
||||
FIELDS ||= [:name, :about, :version, :authors, :url, :required_version]
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
@media screen and (max-width: 767px) {
|
||||
table.staff-logs tr {
|
||||
display: -ms-grid;
|
||||
-ms-grid-columns: 1fr 1fr 1fr 0.5fr;
|
||||
-ms-grid-rows: auto auto;
|
||||
td {
|
||||
display: -ms-grid;
|
||||
&.staff-users {
|
||||
-ms-grid-row: 1;
|
||||
-ms-grid-column: 1;
|
||||
-ms-grid-column-span: 2;
|
||||
}
|
||||
&.created-at {
|
||||
-ms-grid-row: 1;
|
||||
-ms-grid-column: 4;
|
||||
}
|
||||
&.action {
|
||||
-ms-grid-row: 2;
|
||||
-ms-grid-column: 1;
|
||||
}
|
||||
&.subject {
|
||||
-ms-grid-row: 2;
|
||||
-ms-grid-column: 2;
|
||||
-ms-grid-column-span: 3;
|
||||
}
|
||||
&.details {
|
||||
-ms-grid-row: 3;
|
||||
-ms-grid-column: 1;
|
||||
-ms-grid-column-span: 3;
|
||||
}
|
||||
&.context {
|
||||
-ms-grid-row: 4;
|
||||
-ms-grid-column: 1;
|
||||
-ms-grid-column-span: 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
en:
|
||||
site_settings:
|
||||
discourse_internet_explorer_enabled: "Internet Explorer support"
|
3
plugins/discourse-internet-explorer/config/settings.yml
Normal file
3
plugins/discourse-internet-explorer/config/settings.yml
Normal file
|
@ -0,0 +1,3 @@
|
|||
plugins:
|
||||
discourse_internet_explorer_enabled:
|
||||
default: true
|
57
plugins/discourse-internet-explorer/plugin.rb
Normal file
57
plugins/discourse-internet-explorer/plugin.rb
Normal file
|
@ -0,0 +1,57 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
# name: discourse-internet-explorer
|
||||
# about: Attempts to provide backward support for internt explorer
|
||||
# version: 1.0
|
||||
# authors: Joffrey Jaffeux, David Taylor, Daniel Waterworth, Robin Ward
|
||||
# url: https://github.com/discourse/discourse/tree/master/plugins/discourse-internet-explorer
|
||||
|
||||
enabled_site_setting :discourse_internet_explorer_enabled
|
||||
hide_plugin if self.respond_to?(:hide_plugin)
|
||||
|
||||
register_asset 'stylesheets/ie.scss'
|
||||
|
||||
after_initialize do
|
||||
|
||||
# Conditionally load the stylesheet. There is unfortunately no easy way to do this via
|
||||
# Plugin API.
|
||||
reloadable_patch do |plugin|
|
||||
ApplicationHelper.module_eval do
|
||||
alias_method :previous_discourse_stylesheet_link_tag, :discourse_stylesheet_link_tag
|
||||
def discourse_stylesheet_link_tag(name, opts = {})
|
||||
|
||||
if name == 'discourse-internet-explorer'
|
||||
return unless SiteSetting.discourse_internet_explorer_enabled?
|
||||
return unless request.env['HTTP_USER_AGENT'] =~ /MSIE|Trident/
|
||||
end
|
||||
|
||||
previous_discourse_stylesheet_link_tag(name, opts)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
register_anonymous_cache_key(:ie) do
|
||||
unless defined?(@is_ie)
|
||||
session = @env[self.class::RACK_SESSION]
|
||||
# don't initialize params until later
|
||||
# otherwise you get a broken params on the request
|
||||
params = {}
|
||||
|
||||
@is_ie = BrowserDetection.browser(@env[self.class::USER_AGENT]) == :ie
|
||||
end
|
||||
|
||||
@is_ie
|
||||
end
|
||||
|
||||
# not using patch on preload_script as js is fine and we need this file
|
||||
# to be loaded before other files
|
||||
register_html_builder('server:before-script-load') do |controller|
|
||||
if BrowserDetection.browser(controller.request.env['HTTP_USER_AGENT']) == :ie
|
||||
path = controller.helpers.script_asset_path('/plugins/discourse-internet-explorer/js/ie')
|
||||
|
||||
<<~JAVASCRIPT
|
||||
<script src="#{path}"></script>
|
||||
JAVASCRIPT
|
||||
end
|
||||
end
|
||||
end
|
347
plugins/discourse-internet-explorer/public/js/ie.js
Normal file
347
plugins/discourse-internet-explorer/public/js/ie.js
Normal file
|
@ -0,0 +1,347 @@
|
|||
/* eslint-disable */
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries
|
||||
if (!Object.values) {
|
||||
Object.values = function(obj) {
|
||||
var ownProps = Object.keys(obj),
|
||||
i = ownProps.length,
|
||||
resArray = new Array(i); // preallocate the Array
|
||||
while (i--) resArray[i] = obj[ownProps[i]];
|
||||
|
||||
return resArray;
|
||||
};
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/fr/docs/Web/API/NodeList/forEach
|
||||
if (window.NodeList && !NodeList.prototype.forEach) {
|
||||
NodeList.prototype.forEach = function(callback, thisArg) {
|
||||
thisArg = thisArg || window;
|
||||
for (var i = 0; i < this.length; i++) {
|
||||
callback.call(thisArg, this[i], i, this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes#Polyfill
|
||||
if (!Array.prototype.includes) {
|
||||
Object.defineProperty(Array.prototype, "includes", {
|
||||
value: function(searchElement, fromIndex) {
|
||||
if (this == null) {
|
||||
throw new TypeError('"this" is null or not defined');
|
||||
}
|
||||
|
||||
// 1. Let O be ? ToObject(this value).
|
||||
var o = Object(this);
|
||||
|
||||
// 2. Let len be ? ToLength(? Get(O, "length")).
|
||||
var len = o.length >>> 0;
|
||||
|
||||
// 3. If len is 0, return false.
|
||||
if (len === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 4. Let n be ? ToInteger(fromIndex).
|
||||
// (If fromIndex is undefined, this step produces the value 0.)
|
||||
var n = fromIndex | 0;
|
||||
|
||||
// 5. If n ≥ 0, then
|
||||
// a. Let k be n.
|
||||
// 6. Else n < 0,
|
||||
// a. Let k be len + n.
|
||||
// b. If k < 0, let k be 0.
|
||||
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
|
||||
|
||||
function sameValueZero(x, y) {
|
||||
return (
|
||||
x === y ||
|
||||
(typeof x === "number" &&
|
||||
typeof y === "number" &&
|
||||
isNaN(x) &&
|
||||
isNaN(y))
|
||||
);
|
||||
}
|
||||
|
||||
// 7. Repeat, while k < len
|
||||
while (k < len) {
|
||||
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
|
||||
// b. If SameValueZero(searchElement, elementK) is true, return true.
|
||||
if (sameValueZero(o[k], searchElement)) {
|
||||
return true;
|
||||
}
|
||||
// c. Increase k by 1.
|
||||
k++;
|
||||
}
|
||||
|
||||
// 8. Return false
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes#Polyfill
|
||||
if (!String.prototype.includes) {
|
||||
Object.defineProperty(String.prototype, "includes", {
|
||||
value: function(search, start) {
|
||||
if (typeof start !== "number") {
|
||||
start = 0;
|
||||
}
|
||||
|
||||
if (start + search.length > this.length) {
|
||||
return false;
|
||||
} else {
|
||||
return this.indexOf(search, start) !== -1;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
|
||||
if (!Array.prototype.find) {
|
||||
Object.defineProperty(Array.prototype, "find", {
|
||||
value: function(predicate) {
|
||||
// 1. Let O be ? ToObject(this value).
|
||||
if (this == null) {
|
||||
throw new TypeError('"this" is null or not defined');
|
||||
}
|
||||
|
||||
var o = Object(this);
|
||||
|
||||
// 2. Let len be ? ToLength(? Get(O, "length")).
|
||||
var len = o.length >>> 0;
|
||||
|
||||
// 3. If IsCallable(predicate) is false, throw a TypeError exception.
|
||||
if (typeof predicate !== "function") {
|
||||
throw new TypeError("predicate must be a function");
|
||||
}
|
||||
|
||||
// 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
||||
var thisArg = arguments[1];
|
||||
|
||||
// 5. Let k be 0.
|
||||
var k = 0;
|
||||
|
||||
// 6. Repeat, while k < len
|
||||
while (k < len) {
|
||||
// a. Let Pk be ! ToString(k).
|
||||
// b. Let kValue be ? Get(O, Pk).
|
||||
// c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
|
||||
// d. If testResult is true, return kValue.
|
||||
var kValue = o[k];
|
||||
if (predicate.call(thisArg, kValue, k, o)) {
|
||||
return kValue;
|
||||
}
|
||||
// e. Increase k by 1.
|
||||
k++;
|
||||
}
|
||||
|
||||
// 7. Return undefined.
|
||||
return undefined;
|
||||
},
|
||||
configurable: true,
|
||||
writable: true
|
||||
});
|
||||
}
|
||||
|
||||
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
|
||||
if (!String.prototype.padStart) {
|
||||
String.prototype.padStart = function padStart(targetLength, padString) {
|
||||
targetLength = targetLength >> 0; //truncate if number, or convert non-number to 0;
|
||||
padString = String(typeof padString !== "undefined" ? padString : " ");
|
||||
if (this.length >= targetLength) {
|
||||
return String(this);
|
||||
} else {
|
||||
targetLength = targetLength - this.length;
|
||||
if (targetLength > padString.length) {
|
||||
padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
|
||||
}
|
||||
return padString.slice(0, targetLength) + String(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// https://github.com/uxitten/polyfill/blob/master/string.polyfill.js
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
|
||||
if (!String.prototype.padEnd) {
|
||||
String.prototype.padEnd = function padEnd(targetLength, padString) {
|
||||
targetLength = targetLength >> 0; //floor if number or convert non-number to 0;
|
||||
padString = String(typeof padString !== "undefined" ? padString : " ");
|
||||
if (this.length > targetLength) {
|
||||
return String(this);
|
||||
} else {
|
||||
targetLength = targetLength - this.length;
|
||||
if (targetLength > padString.length) {
|
||||
padString += padString.repeat(targetLength / padString.length); //append to original to ensure we are longer than needed
|
||||
}
|
||||
return String(this) + padString.slice(0, targetLength);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
|
||||
// Production steps of ECMA-262, Edition 6, 22.1.2.1
|
||||
if (!Array.from) {
|
||||
Array.from = (function() {
|
||||
var toStr = Object.prototype.toString;
|
||||
var isCallable = function(fn) {
|
||||
return typeof fn === "function" || toStr.call(fn) === "[object Function]";
|
||||
};
|
||||
var toInteger = function(value) {
|
||||
var number = Number(value);
|
||||
if (isNaN(number)) {
|
||||
return 0;
|
||||
}
|
||||
if (number === 0 || !isFinite(number)) {
|
||||
return number;
|
||||
}
|
||||
return (number > 0 ? 1 : -1) * Math.floor(Math.abs(number));
|
||||
};
|
||||
var maxSafeInteger = Math.pow(2, 53) - 1;
|
||||
var toLength = function(value) {
|
||||
var len = toInteger(value);
|
||||
return Math.min(Math.max(len, 0), maxSafeInteger);
|
||||
};
|
||||
|
||||
// The length property of the from method is 1.
|
||||
return function from(arrayLike /*, mapFn, thisArg */) {
|
||||
// 1. Let C be the this value.
|
||||
var C = this;
|
||||
|
||||
// 2. Let items be ToObject(arrayLike).
|
||||
var items = Object(arrayLike);
|
||||
|
||||
// 3. ReturnIfAbrupt(items).
|
||||
if (arrayLike == null) {
|
||||
throw new TypeError(
|
||||
"Array.from requires an array-like object - not null or undefined"
|
||||
);
|
||||
}
|
||||
|
||||
// 4. If mapfn is undefined, then let mapping be false.
|
||||
var mapFn = arguments.length > 1 ? arguments[1] : void undefined;
|
||||
var T;
|
||||
if (typeof mapFn !== "undefined") {
|
||||
// 5. else
|
||||
// 5. a If IsCallable(mapfn) is false, throw a TypeError exception.
|
||||
if (!isCallable(mapFn)) {
|
||||
throw new TypeError(
|
||||
"Array.from: when provided, the second argument must be a function"
|
||||
);
|
||||
}
|
||||
|
||||
// 5. b. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
||||
if (arguments.length > 2) {
|
||||
T = arguments[2];
|
||||
}
|
||||
}
|
||||
|
||||
// 10. Let lenValue be Get(items, "length").
|
||||
// 11. Let len be ToLength(lenValue).
|
||||
var len = toLength(items.length);
|
||||
|
||||
// 13. If IsConstructor(C) is true, then
|
||||
// 13. a. Let A be the result of calling the [[Construct]] internal method
|
||||
// of C with an argument list containing the single item len.
|
||||
// 14. a. Else, Let A be ArrayCreate(len).
|
||||
var A = isCallable(C) ? Object(new C(len)) : new Array(len);
|
||||
|
||||
// 16. Let k be 0.
|
||||
var k = 0;
|
||||
// 17. Repeat, while k < len… (also steps a - h)
|
||||
var kValue;
|
||||
while (k < len) {
|
||||
kValue = items[k];
|
||||
if (mapFn) {
|
||||
A[k] =
|
||||
typeof T === "undefined"
|
||||
? mapFn(kValue, k)
|
||||
: mapFn.call(T, kValue, k);
|
||||
} else {
|
||||
A[k] = kValue;
|
||||
}
|
||||
k += 1;
|
||||
}
|
||||
// 18. Let putStatus be Put(A, "length", len, true).
|
||||
A.length = len;
|
||||
// 20. Return A.
|
||||
return A;
|
||||
};
|
||||
})();
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign
|
||||
if (typeof Object.assign !== "function") {
|
||||
// Must be writable: true, enumerable: false, configurable: true
|
||||
Object.defineProperty(Object, "assign", {
|
||||
value: function assign(target) {
|
||||
// .length of function is 2
|
||||
"use strict";
|
||||
if (target == null) {
|
||||
// TypeError if undefined or null
|
||||
throw new TypeError("Cannot convert undefined or null to object");
|
||||
}
|
||||
|
||||
var to = Object(target);
|
||||
|
||||
for (var index = 1; index < arguments.length; index++) {
|
||||
var nextSource = arguments[index];
|
||||
|
||||
if (nextSource != null) {
|
||||
// Skip over if undefined or null
|
||||
for (var nextKey in nextSource) {
|
||||
// Avoid bugs when hasOwnProperty is shadowed
|
||||
if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
|
||||
to[nextKey] = nextSource[nextKey];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return to;
|
||||
},
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat#Polyfill
|
||||
if (!String.prototype.repeat) {
|
||||
String.prototype.repeat = function(count) {
|
||||
"use strict";
|
||||
if (this == null)
|
||||
throw new TypeError("can't convert " + this + " to object");
|
||||
|
||||
var str = "" + this;
|
||||
// To convert string to integer.
|
||||
count = +count;
|
||||
// Check NaN
|
||||
if (count != count) count = 0;
|
||||
|
||||
if (count < 0) throw new RangeError("repeat count must be non-negative");
|
||||
|
||||
if (count == Infinity)
|
||||
throw new RangeError("repeat count must be less than infinity");
|
||||
|
||||
count = Math.floor(count);
|
||||
if (str.length == 0 || count == 0) return "";
|
||||
|
||||
// Ensuring count is a 31-bit integer allows us to heavily optimize the
|
||||
// main part. But anyway, most current (August 2014) browsers can't handle
|
||||
// strings 1 << 28 chars or longer, so:
|
||||
if (str.length * count >= 1 << 28)
|
||||
throw new RangeError(
|
||||
"repeat count must not overflow maximum string size"
|
||||
);
|
||||
|
||||
var maxCount = str.length * count;
|
||||
count = Math.floor(Math.log(count) / Math.log(2));
|
||||
while (count) {
|
||||
str += str;
|
||||
count--;
|
||||
}
|
||||
str += str.substring(0, maxCount - str.length);
|
||||
return str;
|
||||
};
|
||||
}
|
||||
|
||||
/* eslint-enable */
|
|
@ -0,0 +1,26 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require "rails_helper"
|
||||
|
||||
describe Middleware::AnonymousCache::Helper do
|
||||
def env(opts = {})
|
||||
{
|
||||
"HTTP_HOST" => "http://test.com",
|
||||
"REQUEST_URI" => "/path?bla=1",
|
||||
"REQUEST_METHOD" => "GET",
|
||||
"rack.input" => ""
|
||||
}.merge(opts)
|
||||
end
|
||||
|
||||
def new_helper(opts = {})
|
||||
Middleware::AnonymousCache::Helper.new(env(opts))
|
||||
end
|
||||
|
||||
it "includes ie in cache key" do
|
||||
helper = new_helper
|
||||
expect(helper.cache_key).to include("ie=false")
|
||||
|
||||
helper = new_helper("HTTP_USER_AGENT" => "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko")
|
||||
expect(helper.cache_key).to include("ie=true")
|
||||
end
|
||||
end
|
|
@ -0,0 +1,37 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'Bootstrapping the Discourse App' do
|
||||
let(:ie_agent) { "Mozilla/5.0 (Windows NT 6.3; Trident/7.0; rv:11.0) like Gecko" }
|
||||
|
||||
context "when disabled" do
|
||||
before do
|
||||
SiteSetting.discourse_internet_explorer_enabled = false
|
||||
end
|
||||
|
||||
it "does not include the IE stylesheet or Javascript" do
|
||||
get "/categories", headers: { "HTTP_USER_AGENT" => ie_agent }
|
||||
expect(response.body).not_to match(/discourse-internet-explorer\/js\/ie.js/)
|
||||
expect(response.body).not_to match(/stylesheets\/discourse-internet-explorer/)
|
||||
end
|
||||
end
|
||||
|
||||
context "when enabled" do
|
||||
before do
|
||||
SiteSetting.discourse_internet_explorer_enabled = true
|
||||
end
|
||||
|
||||
it "includes the IE js and css" do
|
||||
get "/categories", headers: { "HTTP_USER_AGENT" => ie_agent }
|
||||
expect(response.body).to match(/discourse-internet-explorer\/js\/ie.js/)
|
||||
expect(response.body).to match(/stylesheets\/discourse-internet-explorer/)
|
||||
end
|
||||
|
||||
it "doesn't include IE stuff for non-IE browsers" do
|
||||
get "/categories", headers: { "HTTP_USER_AGENT" => "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.70 Safari/537.36" }
|
||||
expect(response.body).not_to match(/discourse-internet-explorer\/js\/ie.js/)
|
||||
expect(response.body).not_to match(/stylesheets\/discourse-internet-explorer/)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user