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:
parent
14668a3c2d
commit
0301bb58aa
|
@ -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')
|
||||
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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
|
|
@ -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(),
|
||||
|
|
|
@ -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))
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
})()
|
|
@ -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'
|
|
@ -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 = {
|
||||
'&': '&',
|
||||
'<': '<',
|
||||
'>': '>',
|
||||
'"': '"',
|
||||
"'": ''',
|
||||
'/': '/',
|
||||
'`': '`', // for old versions of IE
|
||||
'=': '=' // 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
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
@ -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
|
|
@ -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]]
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
})()
|
|
@ -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 {
|
||||
|
|
|
@ -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: [
|
||||
|
|
13
yarn.lock
13
yarn.lock
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue