remove canvas's i18n clone in RCE

refs FOO-2801
flag = none

the code has since been ported to use format-message and the only
straggler was the natcompare module, which really only needed the active
locale, which is now exposed to it by the entrypoint

~ test plan ~

the file browser in the RCE continues to work

Change-Id: Iaf6fc588fe86117f9cc63701f70992987be547ba
Reviewed-on: https://gerrit.instructure.com/c/canvas-lms/+/293551
Tested-by: Service Cloud Jenkins <svc.cloudjenkins@instructure.com>
Reviewed-by: Jacob DeWar <jacob.dewar@instructure.com>
QA-Review: Jacob DeWar <jacob.dewar@instructure.com>
Product-Review: Ahmad Amireh <ahmad@instructure.com>
This commit is contained in:
Ahmad Amireh 2022-06-08 12:52:30 -06:00
parent 14668a3c2d
commit 0301bb58aa
19 changed files with 10 additions and 3416 deletions

View File

@ -16,7 +16,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
const JsProcessor = require('i18nliner/dist/lib/processors/js_processor')['default'];
const JsProcessor = require('@instructure/i18nliner/dist/lib/processors/js_processor')['default'];
const ScopedESMExtractor = require('../js/scoped_esm_extractor');
const dedent = require('dedent')

View File

@ -118,7 +118,6 @@
"format-message": "^6",
"format-message-generate-id": "^6",
"i18n-js": "^3",
"i18nliner": "~0.2.0",
"isomorphic-fetch": "2.2.1",
"jquery": "https://github.com/instructure/jquery.git#1.7.2-with-AMD-and-CommonJS",
"js-beautify": "1",

View File

@ -1,59 +0,0 @@
/*
* Copyright (C) 2011 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import $ from 'jquery'
// for backwards compat, this might be defined already but we expose
// it as a module here
if (!('INST' in window)) window.INST = {}
// ============================================================================================
// = Try to figure out what browser they are using and set INST.broswer.theirbrowser to true =
// = and add a css class to the body for that browser =
// ============================================================================================
INST.browser = {}
// Test for WebKit.
if (window.devicePixelRatio) {
INST.browser.webkit = true
// from: http://www.byond.com/members/?command=view_post&post=53727
INST.browser[
escape(navigator.javaEnabled.toString()) ==
'function%20javaEnabled%28%29%20%7B%20%5Bnative%20code%5D%20%7D'
? 'chrome'
: 'safari'
] = true
}
// this is just using jquery's browser sniffing result of if its firefox, it
// should probably use feature detection
INST.browser.ff = $.browser.mozilla
INST.browser.touch = 'ontouchstart' in document
INST.browser['no-touch'] = !INST.browser.touch
// now we have some degree of knowing which of the common browsers it is,
// on dom ready, give the body those classes
// so for example, if you were on IE6 the body would have the classes "ie" AND "ie6"
const classesToAdd = $.map(INST.browser, (v, k) => (v === true ? k : undefined)).join(' ')
$(() => {
$('body').addClass(classesToAdd)
})
export default INST

View File

@ -21,8 +21,6 @@ import {render, waitFor, fireEvent, within, screen} from '@testing-library/react
import FileBrowser from '../FileBrowser'
import {apiSource} from './filesHelpers'
jest.mock('../natcompare', () => ({strings: a => a}))
const defaultProps = overrides => ({
allowedUpload: true,
selectFile: jest.fn(),

View File

@ -1,54 +0,0 @@
// Copyright (C) 2017 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import axios from 'axios'
const stringIds = {Accept: 'application/json+canvas-string-ids'}
export function getRootFolder(contextType, contextId) {
return axios.get(`/api/v1/${contextType}/${contextId}/folders/root`, stringIds)
}
function createFormData(data) {
const formData = new FormData()
Object.keys(data).forEach(key => formData.append(key, data[key]))
return formData
}
function onFileUploadInfoReceived(file, uploadInfo, onSuccess, onFailure) {
const formData = createFormData({...uploadInfo.upload_params, file})
const config = {'Content-Type': 'multipart/form-data', ...stringIds}
axios
.post(uploadInfo.upload_url, formData, config)
.then(response => onSuccess(response.data))
.catch(response => onFailure(response))
}
export function uploadFile(file, folderId, onSuccess, onFailure) {
axios
.post(
`/api/v1/folders/${folderId}/files`,
{
name: file.name,
size: file.size,
parent_folder_id: folderId,
on_duplicate: 'rename'
},
stringIds
)
.then(response => onFileUploadInfoReceived(file, response.data, onSuccess, onFailure))
.catch(response => onFailure(response))
}

View File

@ -1,905 +0,0 @@
/**
* @version: 1.0 Alpha-1
* @author: Coolite Inc. http://www.coolite.com/
* @date: 2008-04-13
* @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved.
* @license: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/.
* @website: http://www.datejs.com/
*/
import './en-US'
;(function() {
const _originalDateDotPrototypeDotToString = Date.prototype.toString
const $D = Date,
$P = $D.prototype,
$C = $D.CultureInfo,
p = function(s, l) {
if (!l) {
l = 2
}
return ('000' + s).slice(l * -1)
}
/**
* Resets the time of this Date object to 12:00 AM (00:00), which is the start of the day.
* @param {Boolean} .clone() this date instance before clearing Time
* @return {Date} this
*/
$P.clearTime = function() {
this.setHours(0)
this.setMinutes(0)
this.setSeconds(0)
this.setMilliseconds(0)
return this
}
/**
* Resets the time of this Date object to the current time ('now').
* @return {Date} this
*/
$P.setTimeToNow = function() {
const n = new Date()
this.setHours(n.getHours())
this.setMinutes(n.getMinutes())
this.setSeconds(n.getSeconds())
this.setMilliseconds(n.getMilliseconds())
return this
}
/**
* Gets a date that is set to the current date. The time is set to the start of the day (00:00 or 12:00 AM).
* @return {Date} The current date.
*/
$D.today = function() {
return new Date().clearTime()
}
/**
* Compares the first date to the second date and returns an number indication of their relative values.
* @param {Date} First Date object to compare [Required].
* @param {Date} Second Date object to compare to [Required].
* @return {Number} -1 = date1 is lessthan date2. 0 = values are equal. 1 = date1 is greaterthan date2.
*/
$D.compare = function(date1, date2) {
if (isNaN(date1) || isNaN(date2)) {
throw new Error(date1 + ' - ' + date2)
} else if (date1 instanceof Date && date2 instanceof Date) {
return date1 < date2 ? -1 : date1 > date2 ? 1 : 0
} else {
throw new TypeError(date1 + ' - ' + date2)
}
}
/**
* Compares the first Date object to the second Date object and returns true if they are equal.
* @param {Date} First Date object to compare [Required]
* @param {Date} Second Date object to compare to [Required]
* @return {Boolean} true if dates are equal. false if they are not equal.
*/
$D.equals = function(date1, date2) {
return date1.compareTo(date2) === 0
}
/**
* Gets the day number (0-6) if given a CultureInfo specific string which is a valid dayName, abbreviatedDayName or shortestDayName (two char).
* @param {String} The name of the day (eg. "Monday, "Mon", "tuesday", "tue", "We", "we").
* @return {Number} The day number
*/
$D.getDayNumberFromName = function(name) {
const n = $C.dayNames,
m = $C.abbreviatedDayNames,
o = $C.shortestDayNames,
s = name.toLowerCase()
for (let i = 0; i < n.length; i++) {
if (n[i].toLowerCase() == s || m[i].toLowerCase() == s || o[i].toLowerCase() == s) {
return i
}
}
return -1
}
/**
* Gets the month number (0-11) if given a Culture Info specific string which is a valid monthName or abbreviatedMonthName.
* @param {String} The name of the month (eg. "February, "Feb", "october", "oct").
* @return {Number} The day number
*/
$D.getMonthNumberFromName = function(name) {
const n = $C.monthNames,
m = $C.abbreviatedMonthNames,
s = name.toLowerCase()
for (let i = 0; i < n.length; i++) {
if (n[i].toLowerCase() == s || m[i].toLowerCase() == s) {
return i
}
}
return -1
}
/**
* Determines if the current date instance is within a LeapYear.
* @param {Number} The year.
* @return {Boolean} true if date is within a LeapYear, otherwise false.
*/
$D.isLeapYear = function(year) {
return (year % 4 === 0 && year % 100 !== 0) || year % 400 === 0
}
/**
* Gets the number of days in the month, given a year and month value. Automatically corrects for LeapYear.
* @param {Number} The year.
* @param {Number} The month (0-11).
* @return {Number} The number of days in the month.
*/
$D.getDaysInMonth = function(year, month) {
return [31, $D.isLeapYear(year) ? 29 : 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
}
$D.getTimezoneAbbreviation = function(offset) {
let z = $C.timezones,
p
for (let i = 0; i < z.length; i++) {
if (z[i].offset === offset) {
return z[i].name
}
}
return null
}
$D.getTimezoneOffset = function(name) {
let z = $C.timezones,
p
for (let i = 0; i < z.length; i++) {
if (z[i].name === name.toUpperCase()) {
return z[i].offset
}
}
return null
}
/**
* Returns a new Date object that is an exact date and time copy of the original instance.
* @return {Date} A new Date instance
*/
$P.clone = function() {
return new Date(this.getTime())
}
/**
* Compares this instance to a Date object and returns an number indication of their relative values.
* @param {Date} Date object to compare [Required]
* @return {Number} -1 = this is lessthan date. 0 = values are equal. 1 = this is greaterthan date.
*/
$P.compareTo = function(date) {
return Date.compare(this, date)
}
/**
* Compares this instance to another Date object and returns true if they are equal.
* @param {Date} Date object to compare. If no date to compare, new Date() [now] is used.
* @return {Boolean} true if dates are equal. false if they are not equal.
*/
$P.equals = function(date) {
return Date.equals(this, date || new Date())
}
/**
* Determines if this instance is between a range of two dates or equal to either the start or end dates.
* @param {Date} Start of range [Required]
* @param {Date} End of range [Required]
* @return {Boolean} true is this is between or equal to the start and end dates, else false
*/
$P.between = function(start, end) {
return this.getTime() >= start.getTime() && this.getTime() <= end.getTime()
}
/**
* Determines if this date occurs after the date to compare to.
* @param {Date} Date object to compare. If no date to compare, new Date() ("now") is used.
* @return {Boolean} true if this date instance is greater than the date to compare to (or "now"), otherwise false.
*/
$P.isAfter = function(date) {
return this.compareTo(date || new Date()) === 1
}
/**
* Determines if this date occurs before the date to compare to.
* @param {Date} Date object to compare. If no date to compare, new Date() ("now") is used.
* @return {Boolean} true if this date instance is less than the date to compare to (or "now").
*/
$P.isBefore = function(date) {
return this.compareTo(date || new Date()) === -1
}
/**
* Determines if the current Date instance occurs today.
* @return {Boolean} true if this date instance is 'today', otherwise false.
*/
/**
* Determines if the current Date instance occurs on the same Date as the supplied 'date'.
* If no 'date' to compare to is provided, the current Date instance is compared to 'today'.
* @param {date} Date object to compare. If no date to compare, the current Date ("now") is used.
* @return {Boolean} true if this Date instance occurs on the same Day as the supplied 'date'.
*/
$P.isToday = $P.isSameDay = function(date) {
return this.clone()
.clearTime()
.equals((date || new Date()).clone().clearTime())
}
/**
* Adds the specified number of milliseconds to this instance.
* @param {Number} The number of milliseconds to add. The number can be positive or negative [Required]
* @return {Date} this
*/
$P.addMilliseconds = function(value) {
this.setUTCMilliseconds(this.getUTCMilliseconds() + value * 1)
return this
}
/**
* Adds the specified number of seconds to this instance.
* @param {Number} The number of seconds to add. The number can be positive or negative [Required]
* @return {Date} this
*/
$P.addSeconds = function(value) {
return this.addMilliseconds(value * 1000)
}
/**
* Adds the specified number of seconds to this instance.
* @param {Number} The number of seconds to add. The number can be positive or negative [Required]
* @return {Date} this
*/
$P.addMinutes = function(value) {
return this.addMilliseconds(value * 60000) /* 60*1000 */
}
/**
* Adds the specified number of hours to this instance.
* @param {Number} The number of hours to add. The number can be positive or negative [Required]
* @return {Date} this
*/
$P.addHours = function(value) {
return this.addMilliseconds(value * 3600000) /* 60*60*1000 */
}
/**
* Adds the specified number of days to this instance.
* @param {Number} The number of days to add. The number can be positive or negative [Required]
* @return {Date} this
*/
$P.addDays = function(value) {
this.setDate(this.getDate() + value * 1)
return this
}
/**
* Adds the specified number of weeks to this instance.
* @param {Number} The number of weeks to add. The number can be positive or negative [Required]
* @return {Date} this
*/
$P.addWeeks = function(value) {
return this.addDays(value * 7)
}
/**
* Adds the specified number of months to this instance.
* @param {Number} The number of months to add. The number can be positive or negative [Required]
* @return {Date} this
*/
$P.addMonths = function(value) {
const n = this.getDate()
this.setDate(1)
this.setMonth(this.getMonth() + value * 1)
this.setDate(Math.min(n, $D.getDaysInMonth(this.getFullYear(), this.getMonth())))
return this
}
/**
* Adds the specified number of years to this instance.
* @param {Number} The number of years to add. The number can be positive or negative [Required]
* @return {Date} this
*/
$P.addYears = function(value) {
return this.addMonths(value * 12)
}
/**
* Adds (or subtracts) to the value of the years, months, weeks, days, hours, minutes, seconds, milliseconds of the date instance using given configuration object. Positive and Negative values allowed.
* Example
<pre><code>
Date.today().add( { days: 1, months: 1 } )
new Date().add( { years: -1 } )
</code></pre>
* @param {Object} Configuration object containing attributes (months, days, etc.)
* @return {Date} this
*/
$P.add = function(config) {
if (typeof config === 'number') {
this._orient = config
return this
}
const x = config
if (x.milliseconds) {
this.addMilliseconds(x.milliseconds)
}
if (x.seconds) {
this.addSeconds(x.seconds)
}
if (x.minutes) {
this.addMinutes(x.minutes)
}
if (x.hours) {
this.addHours(x.hours)
}
if (x.weeks) {
this.addWeeks(x.weeks)
}
if (x.months) {
this.addMonths(x.months)
}
if (x.years) {
this.addYears(x.years)
}
if (x.days) {
this.addDays(x.days)
}
return this
}
let $y, $m, $d
/**
* Get the week number. Week one (1) is the week which contains the first Thursday of the year. Monday is considered the first day of the week.
* This algorithm is a JavaScript port of the work presented by Claus Tøndering at http://www.tondering.dk/claus/cal/node8.html#SECTION00880000000000000000
* .getWeek() Algorithm Copyright (c) 2008 Claus Tondering.
* The .getWeek() function does NOT convert the date to UTC. The local datetime is used. Please use .getISOWeek() to get the week of the UTC converted date.
* @return {Number} 1 to 53
*/
$P.getWeek = function() {
let a, b, c, d, e, f, g, n, s, w
$y = !$y ? this.getFullYear() : $y
$m = !$m ? this.getMonth() + 1 : $m
$d = !$d ? this.getDate() : $d
if ($m <= 2) {
a = $y - 1
b = ((a / 4) | 0) - ((a / 100) | 0) + ((a / 400) | 0)
c = (((a - 1) / 4) | 0) - (((a - 1) / 100) | 0) + (((a - 1) / 400) | 0)
s = b - c
e = 0
f = $d - 1 + 31 * ($m - 1)
} else {
a = $y
b = ((a / 4) | 0) - ((a / 100) | 0) + ((a / 400) | 0)
c = (((a - 1) / 4) | 0) - (((a - 1) / 100) | 0) + (((a - 1) / 400) | 0)
s = b - c
e = s + 1
f = $d + (153 * ($m - 3) + 2) / 5 + 58 + s
}
g = (a + b) % 7
d = (f + g - e) % 7
n = (f + 3 - d) | 0
if (n < 0) {
w = 53 - (((g - s) / 5) | 0)
} else if (n > 364 + s) {
w = 1
} else {
w = ((n / 7) | 0) + 1
}
$y = $m = $d = null
return w
}
/**
* Get the ISO 8601 week number. Week one ("01") is the week which contains the first Thursday of the year. Monday is considered the first day of the week.
* The .getISOWeek() function does convert the date to it's UTC value. Please use .getWeek() to get the week of the local date.
* @return {String} "01" to "53"
*/
$P.getISOWeek = function() {
$y = this.getUTCFullYear()
$m = this.getUTCMonth() + 1
$d = this.getUTCDate()
return p(this.getWeek())
}
/**
* Moves the date to Monday of the week set. Week one (1) is the week which contains the first Thursday of the year.
* @param {Number} A Number (1 to 53) that represents the week of the year.
* @return {Date} this
*/
$P.setWeek = function(n) {
return this.moveToDayOfWeek(1).addWeeks(n - this.getWeek())
}
// private
const validate = function(n, min, max, name) {
if (typeof n === 'undefined') {
return false
} else if (typeof n !== 'number') {
throw new TypeError(n + ' is not a Number.')
} else if (n < min || n > max) {
throw new RangeError(n + ' is not a valid value for ' + name + '.')
}
return true
}
/**
* Validates the number is within an acceptable range for milliseconds [0-999].
* @param {Number} The number to check if within range.
* @return {Boolean} true if within range, otherwise false.
*/
$D.validateMillisecond = function(value) {
return validate(value, 0, 999, 'millisecond')
}
/**
* Validates the number is within an acceptable range for seconds [0-59].
* @param {Number} The number to check if within range.
* @return {Boolean} true if within range, otherwise false.
*/
$D.validateSecond = function(value) {
return validate(value, 0, 59, 'second')
}
/**
* Validates the number is within an acceptable range for minutes [0-59].
* @param {Number} The number to check if within range.
* @return {Boolean} true if within range, otherwise false.
*/
$D.validateMinute = function(value) {
return validate(value, 0, 59, 'minute')
}
/**
* Validates the number is within an acceptable range for hours [0-23].
* @param {Number} The number to check if within range.
* @return {Boolean} true if within range, otherwise false.
*/
$D.validateHour = function(value) {
return validate(value, 0, 23, 'hour')
}
/**
* Validates the number is within an acceptable range for the days in a month [0-MaxDaysInMonth].
* @param {Number} The number to check if within range.
* @return {Boolean} true if within range, otherwise false.
*/
$D.validateDay = function(value, year, month) {
return validate(value, 1, $D.getDaysInMonth(year, month), 'day')
}
/**
* Validates the number is within an acceptable range for months [0-11].
* @param {Number} The number to check if within range.
* @return {Boolean} true if within range, otherwise false.
*/
$D.validateMonth = function(value) {
return validate(value, 0, 11, 'month')
}
/**
* Validates the number is within an acceptable range for years.
* @param {Number} The number to check if within range.
* @return {Boolean} true if within range, otherwise false.
*/
$D.validateYear = function(value) {
return validate(value, 0, 9999, 'year')
}
/**
* Set the value of year, month, day, hour, minute, second, millisecond of date instance using given configuration object.
* Example
<pre><code>
Date.today().set( { day: 20, month: 1 } )
new Date().set( { millisecond: 0 } )
</code></pre>
*
* @param {Object} Configuration object containing attributes (month, day, etc.)
* @return {Date} this
*/
$P.set = function(config) {
if ($D.validateMillisecond(config.millisecond)) {
this.setMilliseconds(config.millisecond)
}
if ($D.validateSecond(config.second)) {
this.setSeconds(config.second)
}
if ($D.validateMinute(config.minute)) {
this.setMinutes(config.minute)
}
if ($D.validateHour(config.hour)) {
this.setHours(config.hour)
}
if ($D.validateMonth(config.month)) {
this.addMonths(config.month - this.getMonth())
}
if ($D.validateYear(config.year)) {
this.addYears(config.year - this.getFullYear())
}
/* day has to go last because you can't validate the day without first knowing the month */
if ($D.validateDay(config.day, this.getFullYear(), this.getMonth())) {
this.addDays(config.day - this.getDate())
}
if (config.timezone) {
this.setTimezone(config.timezone)
}
if (config.timezoneOffset != null) {
this.setTimezoneOffset(config.timezoneOffset)
}
if (config.week && validate(config.week, 0, 53, 'week')) {
this.setWeek(config.week)
}
return this
}
/**
* Moves the date to the first day of the month.
* @return {Date} this
*/
$P.moveToFirstDayOfMonth = function() {
return this.set({day: 1})
}
/**
* Moves the date to the last day of the month.
* @return {Date} this
*/
$P.moveToLastDayOfMonth = function() {
return this.set({day: $D.getDaysInMonth(this.getFullYear(), this.getMonth())})
}
/**
* Moves the date to the next n'th occurrence of the dayOfWeek starting from the beginning of the month. The number (-1) is a magic number and will return the last occurrence of the dayOfWeek in the month.
* @param {Number} The dayOfWeek to move to
* @param {Number} The n'th occurrence to move to. Use (-1) to return the last occurrence in the month
* @return {Date} this
*/
$P.moveToNthOccurrence = function(dayOfWeek, occurrence) {
let shift = 0
if (occurrence > 0) {
shift = occurrence - 1
} else if (occurrence === -1) {
this.moveToLastDayOfMonth()
if (this.getDay() !== dayOfWeek) {
this.moveToDayOfWeek(dayOfWeek, -1)
}
return this
}
return this.moveToFirstDayOfMonth()
.addDays(-1)
.moveToDayOfWeek(dayOfWeek, +1)
.addWeeks(shift)
}
/**
* Move to the next or last dayOfWeek based on the orient value.
* @param {Number} The dayOfWeek to move to
* @param {Number} Forward (+1) or Back (-1). Defaults to +1. [Optional]
* @return {Date} this
*/
$P.moveToDayOfWeek = function(dayOfWeek, orient) {
let diff = (dayOfWeek - this.getDay() + 7 * (orient || +1)) % 7
return this.addDays(diff === 0 ? (diff += 7 * (orient || +1)) : diff)
}
/**
* Move to the next or last month based on the orient value.
* @param {Number} The month to move to. 0 = January, 11 = December
* @param {Number} Forward (+1) or Back (-1). Defaults to +1. [Optional]
* @return {Date} this
*/
$P.moveToMonth = function(month, orient) {
let diff = (month - this.getMonth() + 12 * (orient || +1)) % 12
return this.addMonths(diff === 0 ? (diff += 12 * (orient || +1)) : diff)
}
/**
* Get the Ordinal day (numeric day number) of the year, adjusted for leap year.
* @return {Number} 1 through 365 (366 in leap years)
*/
$P.getOrdinalNumber = function() {
return Math.ceil((this.clone().clearTime() - new Date(this.getFullYear(), 0, 1)) / 86400000) + 1
}
/**
* Get the time zone abbreviation of the current date.
* @return {String} The abbreviated time zone name (e.g. "EST")
*/
$P.getTimezone = function() {
return $D.getTimezoneAbbreviation(this.getUTCOffset())
}
$P.setTimezoneOffset = function(offset) {
offset = Number(offset)
offset = -(Math.floor(offset / 100) * 60 + (((offset % 100) + 60) % 60))
return this.addMinutes(offset - this.getTimezoneOffset())
}
$P.setTimezone = function(offset) {
return this.setTimezoneOffset($D.getTimezoneOffset(offset))
}
/**
* Indicates whether Daylight Saving Time is observed in the current time zone.
* @return {Boolean} true|false
*/
$P.hasDaylightSavingTime = function() {
return (
Date.today()
.set({month: 0, day: 1})
.getTimezoneOffset() !==
Date.today()
.set({month: 6, day: 1})
.getTimezoneOffset()
)
}
/**
* Indicates whether this Date instance is within the Daylight Saving Time range for the current time zone.
* @return {Boolean} true|false
*/
$P.isDaylightSavingTime = function() {
return (
Date.today()
.set({month: 0, day: 1})
.getTimezoneOffset() != this.getTimezoneOffset()
)
}
/**
* Get the offset from UTC of the current date.
* @return {String} The 4-character offset string prefixed with + or - (e.g. "-0500")
*/
$P.getUTCOffset = function() {
let offset = this.getTimezoneOffset(),
minutes = offset % 60,
hours = (offset - minutes) / 60,
n = -(hours * 100 + minutes),
r
if (n < 0) {
r = (n - 10000).toString()
return r.charAt(0) + r.substr(2)
} else {
r = (n + 10000).toString()
return '+' + r.substr(1)
}
}
/**
* Returns the number of milliseconds between this date and date.
* @param {Date} Defaults to now
* @return {Number} The diff in milliseconds
*/
$P.getElapsed = function(date) {
return (date || new Date()) - this
}
if (!$P.toISOString) {
/**
* Converts the current date instance into a string with an ISO 8601 format. The date is converted to it's UTC value.
* @return {String} ISO 8601 string of date
*/
$P.toISOString = function() {
// From http://www.json.org/json.js. Public Domain.
function f(n) {
return n < 10 ? '0' + n : n
}
return (
'"' +
this.getUTCFullYear() +
'-' +
f(this.getUTCMonth() + 1) +
'-' +
f(this.getUTCDate()) +
'T' +
f(this.getUTCHours()) +
':' +
f(this.getUTCMinutes()) +
':' +
f(this.getUTCSeconds()) +
'Z"'
)
}
}
// private
$P._toString = $P.toString
/**
* Converts the value of the current Date object to its equivalent string representation.
* Format Specifiers
<pre>
CUSTOM DATE AND TIME FORMAT STRINGS
Format Description Example
------ --------------------------------------------------------------------------- -----------------------
s The seconds of the minute between 0-59. "0" to "59"
ss The seconds of the minute with leading zero if required. "00" to "59"
m The minute of the hour between 0-59. "0" or "59"
mm The minute of the hour with leading zero if required. "00" or "59"
h The hour of the day between 1-12. "1" to "12"
hh The hour of the day with leading zero if required. "01" to "12"
H The hour of the day between 0-23. "0" to "23"
HH The hour of the day with leading zero if required. "00" to "23"
d The day of the month between 1 and 31. "1" to "31"
dd The day of the month with leading zero if required. "01" to "31"
ddd Abbreviated day name. $C.abbreviatedDayNames. "Mon" to "Sun"
dddd The full day name. $C.dayNames. "Monday" to "Sunday"
M The month of the year between 1-12. "1" to "12"
MM The month of the year with leading zero if required. "01" to "12"
MMM Abbreviated month name. $C.abbreviatedMonthNames. "Jan" to "Dec"
MMMM The full month name. $C.monthNames. "January" to "December"
yy The year as a two-digit number. "99" or "08"
yyyy The full four digit year. "1999" or "2008"
t Displays the first character of the A.M./P.M. designator. "A" or "P"
$C.amDesignator or $C.pmDesignator
tt Displays the A.M./P.M. designator. "AM" or "PM"
$C.amDesignator or $C.pmDesignator
S The ordinal suffix ("st, "nd", "rd" or "th") of the current day. "st, "nd", "rd" or "th"
|| *Format* || *Description* || *Example* ||
|| d || The CultureInfo shortDate Format Pattern || "M/d/yyyy" ||
|| D || The CultureInfo longDate Format Pattern || "dddd, MMMM dd, yyyy" ||
|| F || The CultureInfo fullDateTime Format Pattern || "dddd, MMMM dd, yyyy h:mm:ss tt" ||
|| m || The CultureInfo monthDay Format Pattern || "MMMM dd" ||
|| r || The CultureInfo rfc1123 Format Pattern || "ddd, dd MMM yyyy HH:mm:ss GMT" ||
|| s || The CultureInfo sortableDateTime Format Pattern || "yyyy-MM-ddTHH:mm:ss" ||
|| t || The CultureInfo shortTime Format Pattern || "h:mm tt" ||
|| T || The CultureInfo longTime Format Pattern || "h:mm:ss tt" ||
|| u || The CultureInfo universalSortableDateTime Format Pattern || "yyyy-MM-dd HH:mm:ssZ" ||
|| y || The CultureInfo yearMonth Format Pattern || "MMMM, yyyy" ||
STANDARD DATE AND TIME FORMAT STRINGS
Format Description Example ("en-US")
------ --------------------------------------------------------------------------- -----------------------
d The CultureInfo shortDate Format Pattern "M/d/yyyy"
D The CultureInfo longDate Format Pattern "dddd, MMMM dd, yyyy"
F The CultureInfo fullDateTime Format Pattern "dddd, MMMM dd, yyyy h:mm:ss tt"
m The CultureInfo monthDay Format Pattern "MMMM dd"
r The CultureInfo rfc1123 Format Pattern "ddd, dd MMM yyyy HH:mm:ss GMT"
s The CultureInfo sortableDateTime Format Pattern "yyyy-MM-ddTHH:mm:ss"
t The CultureInfo shortTime Format Pattern "h:mm tt"
T The CultureInfo longTime Format Pattern "h:mm:ss tt"
u The CultureInfo universalSortableDateTime Format Pattern "yyyy-MM-dd HH:mm:ssZ"
y The CultureInfo yearMonth Format Pattern "MMMM, yyyy"
</pre>
* @param {String} A format string consisting of one or more format spcifiers [Optional].
* @return {String} A string representation of the current Date object.
*/
$P.toString = function(format) {
const x = this
// Standard Date and Time Format Strings. Formats pulled from CultureInfo file and
// may vary by culture.
if (format && format.length == 1) {
const c = $C.formatPatterns
x.t = x.toString
switch (format) {
case 'd':
return x.t(c.shortDate)
case 'D':
return x.t(c.longDate)
case 'F':
return x.t(c.fullDateTime)
case 'm':
return x.t(c.monthDay)
case 'r':
return x.t(c.rfc1123)
case 's':
return x.t(c.sortableDateTime)
case 't':
return x.t(c.shortTime)
case 'T':
return x.t(c.longTime)
case 'u':
return x.t(c.universalSortableDateTime)
case 'y':
return x.t(c.yearMonth)
}
}
const ord = function(n) {
switch (n * 1) {
case 1:
case 21:
case 31:
return 'st'
case 2:
case 22:
return 'nd'
case 3:
case 23:
return 'rd'
default:
return 'th'
}
}
return format
? format.replace(/(\\)?(dd?d?d?|MM?M?M?|yy?y?y?|hh?|HH?|mm?|ss?|tt?|S)/g, function(m) {
if (m.charAt(0) === '\\') {
return m.replace('\\', '')
}
x.h = x.getHours
switch (m) {
case 'hh':
return p(x.h() < 13 ? (x.h() === 0 ? 12 : x.h()) : x.h() - 12)
case 'h':
return x.h() < 13 ? (x.h() === 0 ? 12 : x.h()) : x.h() - 12
case 'HH':
return p(x.h())
case 'H':
return x.h()
case 'mm':
return p(x.getMinutes())
case 'm':
return x.getMinutes()
case 'ss':
return p(x.getSeconds())
case 's':
return x.getSeconds()
case 'yyyy':
return p(x.getFullYear(), 4)
case 'yy':
return p(x.getFullYear())
case 'dddd':
return $C.dayNames[x.getDay()]
case 'ddd':
return $C.abbreviatedDayNames[x.getDay()]
case 'dd':
return p(x.getDate())
case 'd':
return x.getDate()
case 'MMMM':
return $C.monthNames[x.getMonth()]
case 'MMM':
return $C.abbreviatedMonthNames[x.getMonth()]
case 'MM':
return p(x.getMonth() + 1)
case 'M':
return x.getMonth() + 1
case 't':
return x.h() < 12 ? $C.amDesignator.substring(0, 1) : $C.pmDesignator.substring(0, 1)
case 'tt':
return x.h() < 12 ? $C.amDesignator : $C.pmDesignator
case 'S':
return ord(x.getDate())
default:
return m
}
})
: _originalDateDotPrototypeDotToString.apply(this, arguments)
}
})()

View File

@ -1,21 +0,0 @@
/*
* Copyright (C) 2020 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
// just a stub to require the right date.js source file to behave the same as
// the old minified version
import './parser'

View File

@ -1,85 +0,0 @@
/*
* Copyright (C) 2011 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import INST from './INST'
class SafeString {
constructor(string) {
this.string = typeof string === 'string' ? string : `${string}`
}
toString() {
return this.string
}
}
const ENTITIES = {
'&': '&amp;',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'/': '&#x2F;',
'`': '&#x60;', // for old versions of IE
'=': '&#x3D;' // in case of unquoted attributes
}
function htmlEscape(str) {
// ideally we should wrap this in a SafeString, but this is how it has
// always worked :-/
return str.replace(/[&<>"'\/`=]/g, c => ENTITIES[c])
}
// Escapes HTML tags from string, or object string props of `strOrObject`.
// returns the new string, or the object with escaped properties
export default function escape(strOrObject) {
if (typeof strOrObject === 'string') {
return htmlEscape(strOrObject)
} else if (strOrObject instanceof SafeString) {
return strOrObject
} else if (typeof strOrObject === 'number') {
return escape(strOrObject.toString())
}
for (const k in strOrObject) {
const v = strOrObject[k]
if (typeof v === 'string') {
strOrObject[k] = htmlEscape(v)
}
}
return strOrObject
}
escape.SafeString = SafeString
// tinymce plugins use this and they need it global :(
INST.htmlEscape = escape
const UNESCAPE_ENTITIES = Object.keys(ENTITIES).reduce((map, key) => {
const value = ENTITIES[key]
map[value] = key
return map
}, {})
const unescapeSource = `(?:${Object.keys(UNESCAPE_ENTITIES).join('|')})`
const UNESCAPE_REGEX = new RegExp(unescapeSource, 'g')
function unescape(str) {
return str.replace(UNESCAPE_REGEX, match => UNESCAPE_ENTITIES[match])
}
escape.unescape = unescape

View File

@ -1,52 +0,0 @@
//
// Copyright (C) 2013 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
const formatter = {
0: 'toUpperCase',
1: 'toLowerCase'
}
// see also lib/i18n/lolcalize.rb
function letThereBeLols(str) {
// don't want to mangle placeholders, wrappers, etc.
const pattern = /(\s*%h?\{[^\}]+\}\s*|\s*[\n\\`\*_\{\}\[\]\(\)\#\+\-!]+\s*|^\s+)/
const result = str.split(pattern).map(token => {
if (token.match(pattern)) return token
let s = ''
// same as for i in [0...token.length] in coffeescript
for (let i = 0, end = token.length, asc = end >= 0; asc ? i < end : i > end; asc ? i++ : i--) {
s += token[i][formatter[i % 2]]()
}
s = s.replace(/\.( |$)/, '!!?! ')
s = s.replace(/^(\w+)$/, '$1!')
if (s.length > 2) s += ' LOL!'
return s
})
return result.join('')
}
export default function i18nLolcalize(strOrObj) {
if (typeof strOrObj === 'string') return letThereBeLols(strOrObj)
const result = {}
for (const key in strOrObj) {
const value = strOrObj[key]
result[key] = letThereBeLols(value)
}
return result
}

View File

@ -1,430 +0,0 @@
/*
* Copyright (C) 2011 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import $ from 'jquery'
import i18nLolcalize from './i18nLolcalize'
import I18n from 'i18n-js'
import extend from 'i18nliner/dist/lib/extensions/i18n_js'
import htmlEscape from './htmlEscape'
// add i18nliner's runtime extensions to the global I18n object
if (!I18n.translateWithoutI18nliner) {
// can't extend or load date-js twice
import('./date').then(() => extend(I18n))
}
/*
* Overridden interpolator that localizes any interpolated numbers.
* Defaults to localizeNumber behavior (precision 9, but strips
* insignificant digits). If you want a different format, do it
* before you interpolate.
*/
const interpolate = I18n.interpolate.bind(I18n)
I18n.interpolate = function(message, origOptions) {
const options = {...origOptions}
const matches = message.match(this.PLACEHOLDER) || []
matches.forEach(placeholder => {
const name = placeholder.replace(this.PLACEHOLDER, '$1')
if (typeof options[name] === 'number') {
options[name] = this.localizeNumber(options[name])
}
})
return interpolate(message, options)
}
I18n.locale = document.documentElement.getAttribute('lang')
I18n.lookup = function(scope, options = {}) {
const translations = I18n.translations
const locales = I18n.getLocaleAndFallbacks(I18n.currentLocale())
if (typeof scope === 'object') {
scope = scope.join(this.defaultSeparator)
}
if (options.scope) {
scope = `${options.scope}${this.defaultSeparator}${scope}`
}
const scopes = scope.split(this.defaultSeparator)
let messages
for (let i = 0; !messages && i < locales.length; i++) {
messages = translations[locales[i]]
for (let j = 0; messages && j < scopes.length; j++) {
const currentScope = scopes[j]
messages = messages[currentScope]
}
}
if (!messages && options.defaultValue != null) {
messages = options.defaultValue
}
return messages
}
I18n.getLocaleAndFallbacks = function(locale) {
if (!I18n.fallbacksMap) {
I18n.fallbacksMap = I18n.computeFallbacks()
}
return I18n.fallbacksMap[locale] || [I18n.defaultLocale]
}
I18n.computeFallbacks = function() {
const map = {}
Object.keys(I18n.translations).forEach(locale => {
const locales = []
const parts = locale.split(/-/)
for (let i = parts.length; i > 0; i--) {
const candidateLocale = parts.slice(0, i).join('-')
if (candidateLocale in I18n.translations) {
locales.push(candidateLocale)
}
}
if (locales.indexOf(I18n.defaultLocale) === -1) {
locales.push(I18n.defaultLocale)
}
map[locale] = locales
})
return map
}
const _localize = I18n.localize.bind(I18n)
I18n.localize = function(scope, value) {
let result = _localize.call(this, scope, value)
if (scope.match(/^(date|time)/)) result = result.replace(/\s{2,}/, ' ')
return result
}
I18n.n = I18n.localizeNumber = (value, options = {}) => {
const format = {
...(I18n.lookup('number.format') || {}),
// use a high precision and strip zeros if no precision is provided
// 5 is as high as we want to go without causing precision issues
// when used with toFixed() and large numbers
strip_insignificant_zeros: options.strip_insignificant_zeros || options.precision == null,
precision: options.precision != null ? options.precision : 5
}
const method = options.percentage ? 'toPercentage' : 'toNumber'
if (value && value.toString().match(/e/)) {
return value.toString()
} else {
return I18n[method](value, format)
}
}
const padding = (n, pad = '00', len = 2) => {
const s = pad + n.toString()
return s.substr(s.length - len)
}
I18n.strftime = function(date, format) {
const options = this.lookup('date')
if (options) {
options.meridian = options.meridian || ['AM', 'PM']
}
const weekDay = date.getDay()
const day = date.getDate()
const year = date.getFullYear()
const month = date.getMonth() + 1
const dayOfYear =
1 + Math.round((new Date(year, month - 1, day) - new Date(year, 0, 1)) / 86400000)
const hour = date.getHours()
let hour12 = hour
const meridian = hour > 11 ? 1 : 0
const secs = date.getSeconds()
const mils = date.getMilliseconds()
const mins = date.getMinutes()
const offset = date.getTimezoneOffset()
const epochOffset = Math.floor(date.getTime() / 1000)
const absOffsetHours = Math.floor(Math.abs(offset / 60))
const absOffsetMinutes = Math.abs(offset) - absOffsetHours * 60
const timezoneoffset = `${offset > 0 ? '-' : '+'}${
absOffsetHours.toString().length < 2 ? `0${absOffsetHours}` : absOffsetHours
}${absOffsetMinutes.toString().length < 2 ? `0${absOffsetMinutes}` : absOffsetMinutes}`
if (hour12 > 12) {
hour12 -= 12
} else if (hour12 === 0) {
hour12 = 12
}
/*
not implemented:
%N // nanoseconds
%6N // microseconds
%9N // nanoseconds
%U // week number of year, starting with the first Sunday as the first day of the 01st week (00..53)
%V // week number of year according to ISO 8601 (01..53) (week starts on Monday, week 01 is the one with the first Thursday of the year)
%W // week number of year, starting with the first Monday as the first day of the 01st week (00..53)
%Z // time zone name
*/
let optionsNeeded = false
const f = format
.replace(
/%([DFrRTv])/g,
(str, p1) =>
({
D: '%m/%d/%y',
F: '%Y-%m-%d',
r: '%I:%M:%S %p',
R: '%H:%M',
T: '%H:%M:%S',
v: '%e-%b-%Y'
}[p1])
)
.replace(/%(%|\-?[a-zA-Z]|3N)/g, (str, p1) => {
// check to see if we need an options object
switch (p1) {
case 'a':
case 'A':
case 'b':
case 'B':
case 'h':
case 'p':
case 'P':
if (options == null) {
optionsNeeded = true
return ''
}
}
switch (p1) {
case 'a':
return options.abbr_day_names[weekDay]
case 'A':
return options.day_names[weekDay]
case 'b':
return options.abbr_month_names[month]
case 'B':
return options.month_names[month]
case 'd':
return padding(day)
case '-d':
return day
case 'e':
return padding(day, ' ')
case 'h':
return options.abbr_month_names[month]
case 'H':
return padding(hour)
case '-H':
return hour
case 'I':
return padding(hour12)
case '-I':
return hour12
case 'j':
return padding(dayOfYear, '00', 3)
case 'k':
return padding(hour, ' ')
case 'l':
return padding(hour12, ' ')
case 'L':
return padding(mils, '00', 3)
case 'm':
return padding(month)
case '-m':
return month
case 'M':
return padding(mins)
case '-M':
return mins
case 'n':
return '\n'
case '3N':
return padding(mils, '00', 3)
case 'p':
return options.meridian[meridian]
case 'P':
return options.meridian[meridian].toLowerCase()
case 's':
return epochOffset
case 'S':
return padding(secs)
case '-S':
return secs
case 't':
return '\t'
case 'u':
return weekDay || weekDay + 7
case 'w':
return weekDay
case 'y':
return padding(year)
case '-y':
return padding(year).replace(/^0+/, '')
case 'Y':
return year
case 'z':
return timezoneoffset
case '%':
return '%'
default:
return str
}
})
if (optionsNeeded) {
return date.toString()
}
return f
}
// like the original, except it formats count
I18n.pluralize = function(count, scope, options) {
let translation
try {
translation = this.lookup(scope, options)
} catch (error) {}
if (!translation) {
return this.missingTranslation(scope)
}
options = {precision: 0, ...options}
options.count = this.localizeNumber(count, options)
let message
switch (Math.abs(count)) {
case 0:
message =
translation.zero != null
? translation.zero
: translation.none != null
? translation.none
: translation.other != null
? translation.other
: this.missingTranslation(scope, 'zero')
break
case 1:
message = translation.one != null ? translation.one : this.missingTranslation(scope, 'one')
break
default:
message =
translation.other != null ? translation.other : this.missingTranslation(scope, 'other')
}
return this.interpolate(message, options)
}
I18n.Utils.HtmlSafeString = htmlEscape.SafeString // this is what we use elsewhere in canvas, so make i18nliner use it too
I18n.CallHelpers.keyPattern = /^\#?\w+(\.\w+)+$/ // handle our absolute keys
// when inferring the key at runtime (i.e. js/coffee or inline hbs `t`
// call), signal to normalizeKey that it shouldn't be scoped.
// TODO: make i18nliner-js set i18n_inferred_key, which will DRY things up
// slightly
const inferKey = I18n.CallHelpers.inferKey.bind(I18n.CallHelpers)
I18n.CallHelpers.inferKey = (defaultValue, translateOptions) =>
`#${inferKey(defaultValue, translateOptions)}`
I18n.CallHelpers.normalizeKey = (key, options) => {
if (key[0] === '#') {
key = key.slice(1)
delete options.scope
}
return key
}
if (window.ENV && window.ENV.lolcalize) {
I18n.CallHelpers.normalizeDefault = i18nLolcalize
}
I18n.scoped = (scope, callback) => {
const i18n_scope = new I18n.scope(scope)
if (callback) callback(i18n_scope)
return i18n_scope
}
class Scope {
constructor(scope) {
this.scope = scope
this.cache = new Map()
}
translate(...args) {
let cacheKey
try {
cacheKey = I18n.locale + JSON.stringify(args)
} catch (e) {
// if there is something in the arguments we can't stringify, just do it without cache
}
if (cacheKey) {
const cached = this.cache.get(cacheKey)
if (cached) {
return cached
} else {
const valToCache = this.translateWithoutCache(...args)
this.cache.set(cacheKey, valToCache)
return valToCache
}
} else {
return this.translateWithoutCache(...args)
}
}
translateWithoutCache() {
let args = arguments
const options = args[args.length - 1]
if (options instanceof Object) {
options.scope = this.scope
} else {
args = [...args, {scope: this.scope}]
}
return I18n.translate(...args)
}
localize(key, date) {
if (key[0] === '#') key = key.slice(1)
return I18n.localize(key, date)
}
beforeLabel(text) {
return this.t('#before_label_wrapper', '%{text}:', {text})
}
}
I18n.scope = Scope
Scope.prototype.HtmlSafeString = I18n.HtmlSafeString
Scope.prototype.lookup = I18n.lookup.bind(I18n)
Scope.prototype.toTime = I18n.toTime.bind(I18n)
Scope.prototype.toNumber = I18n.toNumber.bind(I18n)
Scope.prototype.toCurrency = I18n.toCurrency.bind(I18n)
Scope.prototype.toHumanSize = I18n.toHumanSize.bind(I18n)
Scope.prototype.toPercentage = I18n.toPercentage.bind(I18n)
Scope.prototype.localizeNumber = I18n.n.bind(I18n)
Scope.prototype.currentLocale = I18n.currentLocale.bind(I18n)
// shorthand
Scope.prototype.t = Scope.prototype.translate
Scope.prototype.l = Scope.prototype.localize
Scope.prototype.n = Scope.prototype.localizeNumber
Scope.prototype.p = Scope.prototype.pluralize
if (I18n.translations) {
$.extend(true, I18n.translations, {en: {}})
} else {
I18n.translations = {en: {}}
}
export default I18n

View File

@ -15,16 +15,18 @@
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import I18n from './i18nObj'
let activeLocale = 'en-US'
export const setLocale = locale => {
const locale_map = {zh_Hant: 'zh-Hant'}
activeLocale = locale_map[locale] || locale
}
export default {
strings(x, y) {
let locale = I18n.locale || 'en-US'
const locale_map = {zh_Hant: 'zh-Hant'}
locale = locale_map[locale] || locale
// if you change these settings, also match the settings in best_unicode_collation_key
// and Canvas::ICU.collator
return x.localeCompare(y, locale, {
return x.localeCompare(y, activeLocale, {
sensitivity: 'variant',
ignorePunctuation: false,
numeric: true

View File

@ -1,36 +0,0 @@
/*
* Copyright (C) 2016 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export default function(linkHeader) {
if (!linkHeader) {
return []
}
const retVal = {}
linkHeader
.split(',')
.map(partOfHeader => partOfHeader.split('; '))
.forEach(link => {
const myUrl = link[0].substring(1, link[0].length - 1)
let urlRel = link[1].split('=')
urlRel = urlRel[1]
urlRel = urlRel.substring(1, urlRel.length - 1)
retVal[urlRel] = myUrl
})
return retVal
}

File diff suppressed because it is too large Load Diff

View File

@ -1,75 +0,0 @@
/*
* Copyright (C) 2011 - present Instructure, Inc.
*
* This file is part of Canvas.
*
* Canvas is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, version 3 of the License.
*
* Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* You should have received a copy of the GNU Affero General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import $ from 'jquery'
// ported pluralizations from active_support/inflections.rb
// (except for cow -> kine, because nobody does that)
const skip = [
'equipment',
'information',
'rice',
'money',
'species',
'series',
'fish',
'sheep',
'jeans'
]
const patterns = [
[/person$/i, 'people'],
[/man$/i, 'men'],
[/child$/i, 'children'],
[/sex$/i, 'sexes'],
[/move$/i, 'moves'],
[/(quiz)$/i, '$1zes'],
[/^(ox)$/i, '$1en'],
[/([m|l])ouse$/i, '$1ice'],
[/(matr|vert|ind)(?:ix|ex)$/i, '$1ices'],
[/(x|ch|ss|sh)$/i, '$1es'],
[/([^aeiouy]|qu)y$/i, '$1ies'],
[/(hive)$/i, '$1s'],
[/(?:([^f])fe|([lr])f)$/i, '$1$2ves'],
[/sis$/i, 'ses'],
[/([ti])um$/i, '$1a'],
[/(buffal|tomat)o$/i, '$1oes'],
[/(bu)s$/i, '$1ses'],
[/(alias|status)$/i, '$1es'],
[/(octop|vir)us$/i, '$1i'],
[/(ax|test)is$/i, '$1es'],
[/s$/i, 's']
]
const pluralize = function(string) {
string = string || ''
if ($.inArray(string, skip) > 0) {
return string
}
for (let i = 0; i < patterns.length; i++) {
const pair = patterns[i]
if (string.match(pair[0])) {
return string.replace(pair[0], pair[1])
}
}
return string + 's'
}
pluralize.withCount = function(count, string) {
return '' + count + ' ' + (count == 1 ? string : pluralize(string))
}
export default pluralize

View File

@ -1,26 +0,0 @@
//
// Copyright (C) 2012 - present Instructure, Inc.
//
// This file is part of Canvas.
//
// Canvas is free software: you can redistribute it and/or modify it under
// the terms of the GNU Affero General Public License as published by the Free
// Software Foundation, version 3 of the License.
//
// Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
// A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
// details.
//
// You should have received a copy of the GNU Affero General Public License along
// with this program. If not, see <http://www.gnu.org/licenses/>.
import pluralize from './pluralize'
export default function splitAssetString(assetString = '', toPlural = true) {
const match = assetString.match(/(.*)_(\d+)$/)
if (match) {
const contextType = toPlural ? pluralize(match[1]) : match[1]
return [contextType, match[2]]
}
}

View File

@ -1,495 +0,0 @@
/**
* @version: 1.0 Alpha-1
* @author: Coolite Inc. http://www.coolite.com/
* @date: 2008-04-13
* @copyright: Copyright (c) 2006-2008, Coolite Inc. (http://www.coolite.com/). All rights reserved.
* @license: Licensed under The MIT License. See license.txt and http://www.datejs.com/license/.
* @website: http://www.datejs.com/
*/
/**
**************************************************************
** SugarPak - Domain Specific Language - Syntactical Sugar **
**************************************************************
*/
import './core'
;(function() {
const $D = Date,
$P = $D.prototype,
$C = $D.CultureInfo,
$N = Number.prototype
// private
$P._orient = +1
// private
$P._nth = null
// private
$P._is = false
// private
$P._same = false
// private
$P._isSecond = false
// private
$N._dateElement = 'day'
/**
* Moves the date to the next instance of a date as specified by the subsequent date element function (eg. .day(), .month()), month name function (eg. .january(), .jan()) or day name function (eg. .friday(), fri()).
* Example
<pre><code>
Date.today().next().friday();
Date.today().next().fri();
Date.today().next().march();
Date.today().next().mar();
Date.today().next().week();
</code></pre>
*
* @return {Date} date
*/
$P.next = function() {
this._orient = +1
return this
}
/**
* Creates a new Date (Date.today()) and moves the date to the next instance of the date as specified by the subsequent date element function (eg. .day(), .month()), month name function (eg. .january(), .jan()) or day name function (eg. .friday(), fri()).
* Example
<pre><code>
Date.next().friday();
Date.next().fri();
Date.next().march();
Date.next().mar();
Date.next().week();
</code></pre>
*
* @return {Date} date
*/
$D.next = function() {
return $D.today().next()
}
/**
* Moves the date to the previous instance of a date as specified by the subsequent date element function (eg. .day(), .month()), month name function (eg. .january(), .jan()) or day name function (eg. .friday(), fri()).
* Example
<pre><code>
Date.today().last().friday();
Date.today().last().fri();
Date.today().last().march();
Date.today().last().mar();
Date.today().last().week();
</code></pre>
*
* @return {Date} date
*/
$P.last = $P.prev = $P.previous = function() {
this._orient = -1
return this
}
/**
* Creates a new Date (Date.today()) and moves the date to the previous instance of the date as specified by the subsequent date element function (eg. .day(), .month()), month name function (eg. .january(), .jan()) or day name function (eg. .friday(), fri()).
* Example
<pre><code>
Date.last().friday();
Date.last().fri();
Date.previous().march();
Date.prev().mar();
Date.last().week();
</code></pre>
*
* @return {Date} date
*/
$D.last = $D.prev = $D.previous = function() {
return $D.today().last()
}
/**
* Performs a equality check when followed by either a month name, day name or .weekday() function.
* Example
<pre><code>
Date.today().is().friday(); // true|false
Date.today().is().fri();
Date.today().is().march();
Date.today().is().mar();
</code></pre>
*
* @return {Boolean} true|false
*/
$P.is = function() {
this._is = true
return this
}
/**
* Determines if two date objects occur on/in exactly the same instance of the subsequent date part function.
* The function .same() must be followed by a date part function (example: .day(), .month(), .year(), etc).
*
* An optional Date can be passed in the date part function. If now date is passed as a parameter, 'Now' is used.
*
* The following example demonstrates how to determine if two dates fall on the exact same day.
*
* Example
<pre><code>
var d1 = Date.today(); // today at 00:00
var d2 = new Date(); // exactly now.
// Do they occur on the same day?
d1.same().day(d2); // true
// Do they occur on the same hour?
d1.same().hour(d2); // false, unless d2 hour is '00' (midnight).
// What if it's the same day, but one year apart?
var nextYear = Date.today().add(1).year();
d1.same().day(nextYear); // false, because the dates must occur on the exact same day.
</code></pre>
*
* Scenario: Determine if a given date occurs during some week period 2 months from now.
*
* Example
<pre><code>
var future = Date.today().add(2).months();
return someDate.same().week(future); // true|false;
</code></pre>
*
* @return {Boolean} true|false
*/
$P.same = function() {
this._same = true
this._isSecond = false
return this
}
/**
* Determines if the current date/time occurs during Today. Must be preceded by the .is() function.
* Example
<pre><code>
someDate.is().today(); // true|false
new Date().is().today(); // true
Date.today().is().today();// true
Date.today().add(-1).day().is().today(); // false
</code></pre>
*
* @return {Boolean} true|false
*/
$P.today = function() {
return this.same().day()
}
/**
* Determines if the current date is a weekday. This function must be preceded by the .is() function.
* Example
<pre><code>
Date.today().is().weekday(); // true|false
</code></pre>
*
* @return {Boolean} true|false
*/
$P.weekday = function() {
if (this._is) {
this._is = false
return !this.is().sat() && !this.is().sun()
}
return false
}
/**
* Sets the Time of the current Date instance. A string "6:15 pm" or config object {hour:18, minute:15} are accepted.
* Example
<pre><code>
// Set time to 6:15pm with a String
Date.today().at("6:15pm");
// Set time to 6:15pm with a config object
Date.today().at({hour:18, minute:15});
</code></pre>
*
* @return {Date} date
*/
$P.at = function(time) {
return typeof time === 'string' ? $D.parse(this.toString('d') + ' ' + time) : this.set(time)
}
/**
* Creates a new Date() and adds this (Number) to the date based on the preceding date element function (eg. second|minute|hour|day|month|year).
* Example
<pre><code>
// Undeclared Numbers must be wrapped with parentheses. Requirment of JavaScript.
(3).days().fromNow();
(6).months().fromNow();
// Declared Number variables do not require parentheses.
var n = 6;
n.months().fromNow();
</code></pre>
*
* @return {Date} A new Date instance
*/
$N.fromNow = $N.after = function(date) {
const c = {}
c[this._dateElement] = this
return (!date ? new Date() : date.clone()).add(c)
}
/**
* Creates a new Date() and subtract this (Number) from the date based on the preceding date element function (eg. second|minute|hour|day|month|year).
* Example
<pre><code>
// Undeclared Numbers must be wrapped with parentheses. Requirment of JavaScript.
(3).days().ago();
(6).months().ago();
// Declared Number variables do not require parentheses.
var n = 6;
n.months().ago();
</code></pre>
*
* @return {Date} A new Date instance
*/
$N.ago = $N.before = function(date) {
const c = {}
c[this._dateElement] = this * -1
return (!date ? new Date() : date.clone()).add(c)
}
// Do NOT modify the following string tokens. These tokens are used to build dynamic functions.
// All culture-specific strings can be found in the CultureInfo files. See /trunk/src/globalization/.
let dx = 'sunday monday tuesday wednesday thursday friday saturday'.split(/\s/),
mx = 'january february march april may june july august september october november december'.split(
/\s/
),
px = 'Millisecond Second Minute Hour Day Week Month Year'.split(/\s/),
pxf = 'Milliseconds Seconds Minutes Hours Date Week Month FullYear'.split(/\s/),
nth = 'final first second third fourth fifth'.split(/\s/),
de
/**
* Returns an object literal of all the date parts.
* Example
<pre><code>
var o = new Date().toObject();
// { year: 2008, month: 4, week: 20, day: 13, hour: 18, minute: 9, second: 32, millisecond: 812 }
// The object properties can be referenced directly from the object.
alert(o.day); // alerts "13"
alert(o.year); // alerts "2008"
</code></pre>
*
* @return {Date} An object literal representing the original date object.
*/
$P.toObject = function() {
const o = {}
for (let i = 0; i < px.length; i++) {
o[px[i].toLowerCase()] = this['get' + pxf[i]]()
}
return o
}
/**
* Returns a date created from an object literal. Ignores the .week property if set in the config.
* Example
<pre><code>
var o = new Date().toObject();
return Date.fromObject(o); // will return the same date.
var o2 = {month: 1, day: 20, hour: 18}; // birthday party!
Date.fromObject(o2);
</code></pre>
*
* @return {Date} An object literal representing the original date object.
*/
$D.fromObject = function(config) {
config.week = null
return Date.today().set(config)
}
// Create day name functions and abbreviated day name functions (eg. monday(), friday(), fri()).
const df = function(n) {
return function() {
if (this._is) {
this._is = false
return this.getDay() == n
}
if (this._nth !== null) {
// If the .second() function was called earlier, remove the _orient
// from the date, and then continue.
// This is required because 'second' can be used in two different context.
//
// Example
//
// Date.today().add(1).second();
// Date.march().second().monday();
//
// Things get crazy with the following...
// Date.march().add(1).second().second().monday(); // but it works!!
//
if (this._isSecond) {
this.addSeconds(this._orient * -1)
}
// make sure we reset _isSecond
this._isSecond = false
const ntemp = this._nth
this._nth = null
const temp = this.clone().moveToLastDayOfMonth()
this.moveToNthOccurrence(n, ntemp)
if (this > temp) {
throw new RangeError(
$D.getDayName(n) +
' does not occur ' +
ntemp +
' times in the month of ' +
$D.getMonthName(temp.getMonth()) +
' ' +
temp.getFullYear() +
'.'
)
}
return this
}
return this.moveToDayOfWeek(n, this._orient)
}
}
const sdf = function(n) {
return function() {
let t = $D.today(),
shift = n - t.getDay()
if (n === 0 && $C.firstDayOfWeek === 1 && t.getDay() !== 0) {
shift += 7
}
return t.addDays(shift)
}
}
for (let i = 0; i < dx.length; i++) {
// Create constant static Day Name variables. Example: Date.MONDAY or Date.MON
$D[dx[i].toUpperCase()] = $D[dx[i].toUpperCase().substring(0, 3)] = i
// Create Day Name functions. Example: Date.monday() or Date.mon()
$D[dx[i]] = $D[dx[i].substring(0, 3)] = sdf(i)
// Create Day Name instance functions. Example: Date.today().next().monday()
$P[dx[i]] = $P[dx[i].substring(0, 3)] = df(i)
}
// Create month name functions and abbreviated month name functions (eg. january(), march(), mar()).
const mf = function(n) {
return function() {
if (this._is) {
this._is = false
return this.getMonth() === n
}
return this.moveToMonth(n, this._orient)
}
}
const smf = function(n) {
return function() {
return $D.today().set({month: n, day: 1})
}
}
for (let j = 0; j < mx.length; j++) {
// Create constant static Month Name variables. Example: Date.MARCH or Date.MAR
$D[mx[j].toUpperCase()] = $D[mx[j].toUpperCase().substring(0, 3)] = j
// Create Month Name functions. Example: Date.march() or Date.mar()
$D[mx[j]] = $D[mx[j].substring(0, 3)] = smf(j)
// Create Month Name instance functions. Example: Date.today().next().march()
$P[mx[j]] = $P[mx[j].substring(0, 3)] = mf(j)
}
// Create date element functions and plural date element functions used with Date (eg. day(), days(), months()).
const ef = function(j) {
return function() {
// if the .second() function was called earlier, the _orient
// has alread been added. Just return this and reset _isSecond.
if (this._isSecond) {
this._isSecond = false
return this
}
if (this._same) {
this._same = this._is = false
let o1 = this.toObject(),
o2 = (arguments[0] || new Date()).toObject(),
v = '',
k = j.toLowerCase()
for (let m = px.length - 1; m > -1; m--) {
v = px[m].toLowerCase()
if (o1[v] != o2[v]) {
return false
}
if (k == v) {
break
}
}
return true
}
if (j.substring(j.length - 1) != 's') {
j += 's'
}
return this['add' + j](this._orient)
}
}
const nf = function(n) {
return function() {
this._dateElement = n
return this
}
}
for (let k = 0; k < px.length; k++) {
de = px[k].toLowerCase()
// Create date element functions and plural date element functions used with Date (eg. day(), days(), months()).
$P[de] = $P[de + 's'] = ef(px[k])
// Create date element functions and plural date element functions used with Number (eg. day(), days(), months()).
$N[de] = $N[de + 's'] = nf(de)
}
$P._ss = ef('Second')
const nthfn = function(n) {
return function(dayOfWeek) {
if (this._same) {
return this._ss(arguments[0])
}
if (dayOfWeek || dayOfWeek === 0) {
return this.moveToNthOccurrence(dayOfWeek, n)
}
this._nth = n
// if the operator is 'second' add the _orient, then deal with it later...
if (n === 2 && (dayOfWeek === undefined || dayOfWeek === null)) {
this._isSecond = true
return this.addSeconds(this._orient)
}
return this
}
}
for (let l = 0; l < nth.length; l++) {
$P[nth[l]] = l === 0 ? nthfn(-1) : nthfn(l)
}
})()

View File

@ -21,11 +21,13 @@ import {renderIntoDiv as render} from './rce/root'
import {headerFor, originFromHost} from './rcs/api'
import getTranslations from './getTranslations'
import defaultTinymceConfig from './defaultTinymceConfig'
import {setLocale} from './canvasFileBrowser/natcompare'
export const defaultConfiguration = defaultTinymceConfig
export function renderIntoDiv(editorEl, props, cb) {
const language = normalizeLocale(props.language)
setLocale(language)
if (process.env.BUILD_LOCALE || language === 'en') {
render(editorEl, props, cb)
} else {

View File

@ -41,9 +41,6 @@ module.exports = {
test: /\.(woff(2)?|otf|ttf|eot|svg)(\?v=[0-9]\.[0-9]\.[0-9])?$/,
use: 'file-loader'
}
],
noParse: [
/i18nliner\/dist\/lib\/i18nliner/ // i18nLiner has a `require('fs')` that it doesn't actually need, ignore it.
]
},
plugins: [

View File

@ -13891,19 +13891,6 @@ i18n-js@^3:
resolved "https://registry.yarnpkg.com/i18n-js/-/i18n-js-3.8.0.tgz#b8fd6b12e1d88cb71f9806c29bca7c31c012e504"
integrity sha512-hDsGgPuvw/2P+lXSbOafAwspK8Ste8YrwuuUg17W3wEcO1JkQxBlPgsN1t2+852nTnz4YSYTjZc/1nAA2PC/nw==
i18nliner@~0.2.0:
version "0.2.6"
resolved "https://codeload.github.com/instructure/i18nliner-js/tar.gz/089da4716c98dc536a8c4f79f187b37697591a03"
dependencies:
"@babel/parser" "^7"
"@babel/traverse" "^7"
cli-color "^1"
crc32 "~0.2.2"
gglobby "0.0.3"
minimist "~1.2.0"
mkdirp "~0.5.1"
speakingurl "13.0.0"
ic-ajax@~2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/ic-ajax/-/ic-ajax-2.0.2.tgz#50c8ab1c494320fac66830ce1ee3990ab827f596"