Run prettier on app/coffeescripts/ from object to util

This was achieved by turning the prettier rule on in .eslintrc.js
then running:

./node_modules/eslint app/cofeescripts/<folder_name>/**/*.js

on each folder from the start directory to the end directory listed
above.

Test Plan:
  - Automated tests pass

refs COREFE-347

flag = none

Change-Id: I60a6b87e1df6ef027c22784b78abfa084ac0706b
Reviewed-on: https://gerrit.instructure.com/212258
Tested-by: Jenkins
Reviewed-by: Ryan Shaw <ryan@instructure.com>
QA-Review: Ryan Shaw <ryan@instructure.com>
Product-Review: Ryan Shaw <ryan@instructure.com>
This commit is contained in:
Clay Diffrient 2019-10-07 10:00:29 -06:00
parent b3c801819b
commit 5643aa7693
52 changed files with 530 additions and 391 deletions

View File

@ -16,7 +16,7 @@
// with this program. If not, see <http://www.gnu.org/licenses/>.
// turns {foo: {bar: 1}} into {'foo[bar]': 1}
export default function flatten (obj, options = {}, result = {}, prefix) {
export default function flatten(obj, options = {}, result = {}, prefix) {
for (let key in obj) {
const value = obj[key]
key = prefix ? `${prefix}[${key}]` : key

View File

@ -18,7 +18,7 @@
import _ from 'underscore'
// turns {'foo[bar]': 1} into {foo: {bar: 1}}
export default function unflatten (obj) {
export default function unflatten(obj) {
return _(obj).reduce((newObj, val, key) => {
let keys = key.split('][')
let lastKey = keys.length - 1
@ -31,7 +31,10 @@ export default function unflatten (obj) {
// Split first keys part into two parts on the [ and add them back onto
// the beginning of the keys array.
keys = keys.shift().split('[').concat(keys)
keys = keys
.shift()
.split('[')
.concat(keys)
lastKey = keys.length - 1
} else {
// Basic 'foo' style key.
@ -53,7 +56,8 @@ export default function unflatten (obj) {
while (i <= lastKey) {
key = keys[i] === '' ? cur.length : keys[i]
cur = cur[key] = i < lastKey ? cur[key] || (keys[i + 1] && isNaN(keys[i + 1]) ? {} : []) : val
cur = cur[key] =
i < lastKey ? cur[key] || (keys[i + 1] && isNaN(keys[i + 1]) ? {} : []) : val
i++
}
} else {

View File

@ -29,26 +29,52 @@ import toUnderscore from '../str/underscore'
// declare all used i18n keys here to cluttering up the logic
const keys = {
limited: {
get TeacherEnrollment(){ return I18n.t('enrolled_as_limited_teacher', 'enrolled as a teacher with section-only access')},
get TaEnrollment(){ return I18n.t('enrolled_as_limited_ta', 'enrolled as a TA with section-only access')},
get ObserverEnrollment(){ return I18n.t('enrolled_as_limited_observer', 'enrolled as a observer with section-only access')},
get DesignerEnrollment(){ return I18n.t('enrolled_as_limited_designer', 'enrolled as a designer with section-only access')},
get StudentEnrollment(){ return I18n.t('enrolled_as_limited_student', 'enrolled as a student with section-only access')},
get TeacherEnrollment() {
return I18n.t('enrolled_as_limited_teacher', 'enrolled as a teacher with section-only access')
},
get TaEnrollment() {
return I18n.t('enrolled_as_limited_ta', 'enrolled as a TA with section-only access')
},
get ObserverEnrollment() {
return I18n.t(
'enrolled_as_limited_observer',
'enrolled as a observer with section-only access'
)
},
get DesignerEnrollment() {
return I18n.t(
'enrolled_as_limited_designer',
'enrolled as a designer with section-only access'
)
},
get StudentEnrollment() {
return I18n.t('enrolled_as_limited_student', 'enrolled as a student with section-only access')
}
},
standard: {
get TeacherEnrollment(){ return I18n.t('enrolled_as_teacher', 'enrolled as a teacher')},
get TaEnrollment(){ return I18n.t('enrolled_as_ta', 'enrolled as a TA')},
get ObserverEnrollment(){ return I18n.t('enrolled_as_observer', 'enrolled as a observer')},
get DesignerEnrollment(){ return I18n.t('enrolled_as_designer', 'enrolled as a designer')},
get StudentEnrollment(){ return I18n.t('enrolled_as_student', 'enrolled as a student')},
},
get TeacherEnrollment() {
return I18n.t('enrolled_as_teacher', 'enrolled as a teacher')
},
get TaEnrollment() {
return I18n.t('enrolled_as_ta', 'enrolled as a TA')
},
get ObserverEnrollment() {
return I18n.t('enrolled_as_observer', 'enrolled as a observer')
},
get DesignerEnrollment() {
return I18n.t('enrolled_as_designer', 'enrolled as a designer')
},
get StudentEnrollment() {
return I18n.t('enrolled_as_student', 'enrolled as a student')
}
}
}
// #
// begin returned function here
// @param {array} array of enrollments returned from /courses/:course_id/enrollments
export default data =>
_.map(data, (enrollment) => {
_.map(data, enrollment => {
const scope = enrollment.limit_privileges_to_course_section ? 'limited' : 'standard'
// add extra fields to enrollments

View File

@ -24,22 +24,30 @@ import '../jquery.rails_flash_notifications'
$(() => {
let resending = false
$('.re_send_confirmation_link').click(preventDefault(function () {
const $this = $(this)
const text = $this.text()
$('.re_send_confirmation_link').click(
preventDefault(function() {
const $this = $(this)
const text = $this.text()
if (resending) return
resending = true
$this.text(I18n.t('resending', 'resending...'))
if (resending) return
resending = true
$this.text(I18n.t('resending', 'resending...'))
$.ajaxJSON($this.attr('href'), 'POST', {}, (data) => {
resending = false
$this.text(text)
$.flashMessage(I18n.t('done_resending', 'Done! Message delivery may take a few minutes.'))
}, (data) => {
resending = false
$this.text(text)
$.flashError(I18n.t('failed_resending', 'Request failed. Try again.'))
$.ajaxJSON(
$this.attr('href'),
'POST',
{},
data => {
resending = false
$this.text(text)
$.flashMessage(I18n.t('done_resending', 'Done! Message delivery may take a few minutes.'))
},
data => {
resending = false
$this.text(text)
$.flashError(I18n.t('failed_resending', 'Request failed. Try again.'))
}
)
})
}))
)
})

View File

@ -16,7 +16,7 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import eventManager from '../quizzes/log_auditing'
import eventManager from './log_auditing'
export default function(unregister) {
if (unregister) {

View File

@ -116,7 +116,7 @@ export default {
'aria-selected': this.props.isSelected,
draggable: !this.state.editing,
ref: 'FolderChild',
onDragStart: (event) => {
onDragStart: event => {
if (!this.props.isSelected) {
this.props.toggleSelected()
}

View File

@ -23,7 +23,7 @@ import PropTypes from 'prop-types'
import BBTreeBrowserView from '../modules/BBTreeBrowserView'
import RootFoldersFinder from '../../views/RootFoldersFinder'
import customPropTypes from '../modules/customPropTypes'
import filesEnv from '../../react_files/modules/filesEnv'
import filesEnv from '../modules/filesEnv'
import page from 'page'
import '../../jquery.rails_flash_notifications'

View File

@ -120,22 +120,22 @@ export default {
},
/*
* Returns true if all the models passed in have usage rights
*/
* Returns true if all the models passed in have usage rights
*/
usageRightsOnAll() {
return this.props.models.every(model => model.get('usage_rights'))
},
/*
* Returns true if all the models passed in are folders.
*/
* Returns true if all the models passed in are folders.
*/
allFolders() {
return this.props.models.every(model => model instanceof Folder)
},
/*
* Returns true if all the models passed in are folders.
*/
* Returns true if all the models passed in are folders.
*/
anyFolders() {
return this.props.models.filter(model => model instanceof Folder).length
},

View File

@ -54,8 +54,8 @@ export default {
const errors = _.isArray(responseText.errors)
? this.translateErrors(responseText.errors)
: responseText.errors && responseText.errors.base
? [{message: `${responseText.errors.base}, ${responseText.status}`}]
: [{message}]
? [{message: `${responseText.errors.base}, ${responseText.status}`}]
: [{message}]
this.setState({errors})
$.screenReaderFlashMessageExclusive(_.map(errors, error => error.message).join(' '))
@ -68,14 +68,12 @@ export default {
} else {
return error
}
});
})
},
updateResults(props) {
const oldUrl = this.state.collection.url
this.state.collection.url = `${window.location.origin}/api/v1/${this.props.contextType}/${
this.props.contextId
}/files`
this.state.collection.url = `${window.location.origin}/api/v1/${this.props.contextType}/${this.props.contextId}/files`
updateAPIQuerySortParams(this.state.collection, this.props.query)
if (this.state.collection.url === oldUrl && this.state.collection.models.length > 0) {

View File

@ -110,7 +110,7 @@ export default {
}
}
)
.done(this.clearSelectedItems);
.done(this.clearSelectedItems)
}
}
// @clearSelectedItems()

View File

@ -52,7 +52,7 @@ export default class BaseUploader {
onPreflightComplete = data => {
this.uploadData = data
return this._actualUpload()
};
}
// kickoff / preflight upload process
upload() {
@ -79,14 +79,14 @@ export default class BaseUploader {
.catch(this.deferred.reject)
}
onUploadPosted = event => {};
onUploadPosted = event => {}
// should be implemented in extensions
trackProgress = e => {
this.progress = e.loaded / e.total
return this.onProgress(this.progress, this.file)
};
}
getProgress() {
return this.progress

View File

@ -17,7 +17,7 @@
*/
import _ from 'underscore'
import UploadQueue from '../modules/UploadQueue'
import UploadQueue from './UploadQueue'
import ReactDOM from 'react-dom'
/*

View File

@ -24,7 +24,7 @@ export default class FileUploader extends BaseUploader {
onUploadPosted = fileJson => {
const file = this.addFileToCollection(fileJson)
return this.deferred.resolve(file)
};
}
addFileToCollection = attrs => {
const uploadedFile = new BBFile(attrs, 'no/url/needed/') // we've already done the upload, no preflight needed
@ -39,5 +39,5 @@ export default class FileUploader extends BaseUploader {
}
}
return uploadedFile
};
}
}

View File

@ -54,11 +54,11 @@ export default class ZipUploader extends BaseUploader {
this.uploadData = data.pre_attachment
this.contentMigrationId = data.id
return this._actualUpload()
};
}
onUploadPosted = () => {
return this.getContentMigration()
};
}
// get the content migration when ready and use progress api to pull migration progress
getContentMigration = () => {
@ -71,7 +71,7 @@ export default class ZipUploader extends BaseUploader {
return this.pullMigrationProgress(results.progress_url)
}
})
};
}
pullMigrationProgress = url => {
return $.getJSON(url).then(results => {
@ -86,7 +86,7 @@ export default class ZipUploader extends BaseUploader {
return this.onMigrationComplete()
}
})
};
}
onMigrationComplete() {
// reload to get new files to appear
@ -98,7 +98,7 @@ export default class ZipUploader extends BaseUploader {
trackProgress = e => {
this.progress = e.loaded / e.total
return this.onProgress(this.progress, this.file)
};
}
// migration progress is [0..100]
trackMigrationProgress(value) {

View File

@ -90,5 +90,5 @@ export default function deleteStuff(filesAndFolders, args) {
if (args && args.returnFocusTo) {
$(args.returnFocusTo).focus()
}
});
})
}

View File

@ -98,5 +98,5 @@ export default function downloadStuffAsAZip(filesAndFolders, {contextType, conte
.always(() => {
$(window).off('beforeunload', promptBeforeLeaving)
$progressIndicator.remove()
});
})
}

View File

@ -22,11 +22,11 @@ import File from '../../models/File'
import ModuleFile from '../../models/ModuleFile'
/*
* Sets usage rights on the models in memory based on the response from the
* API.
* apiData - the response from the API
* models - an array containing the files/folders to update
*/
* Sets usage rights on the models in memory based on the response from the
* API.
* apiData - the response from the API
* models - an array containing the files/folders to update
*/
// Grab the ids affected from the apiData
export default function updateModelsUsageRights(apiData, models) {
const affectedIds = apiData && apiData.file_ids

View File

@ -15,6 +15,6 @@
// 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 rEscape (string) {
export default function rEscape(string) {
return string.replace(/[\\\^\$\*\+\?\.\(\)\|\{\}\[\]]/g, '\\$&')
}

View File

@ -1,5 +1,25 @@
/*
* Copyright (C) 2019 - 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 TextHelper from '../TextHelper'
test('truncateText', () => {
expect(TextHelper.truncateText('this is longer than 30 characters')).toBe('this is longer than 30...')
expect(TextHelper.truncateText('this is longer than 30 characters')).toBe(
'this is longer than 30...'
)
})

View File

@ -24,24 +24,26 @@ const apiUserContent = {
/*
xsslint safeString.identifier mathml
*/
translateMathmlForScreenreaders ($equationImage) {
translateMathmlForScreenreaders($equationImage) {
// note, it is safe to treat the x-canvaslms-safe-mathml as html because it
// only ever gets put there by us (in Api::Html::Content::apply_mathml).
// Any user content that gets sent to the server will have the
// x-canvaslms-safe-mathml attribute stripped out.
const mathml = $('<div/>').html($equationImage.attr('x-canvaslms-safe-mathml')).html()
const mathml = $('<div/>')
.html($equationImage.attr('x-canvaslms-safe-mathml'))
.html()
const mathmlSpan = $('<span class="hidden-readable"></span>')
mathmlSpan.html(mathml)
return mathmlSpan
},
toMediaCommentLink (node) {
toMediaCommentLink(node) {
const $link = $(
`<a
id='media_comment_${htmlEscape($(node).data('media_comment_id'))}'
data-media_comment_type='${htmlEscape($(node).data('media_comment_type'))}'
class='instructure_inline_media_comment ${htmlEscape(node.nodeName.toLowerCase())}_comment'
data-alt='${htmlEscape($(node).attr("data-alt"))}'
data-alt='${htmlEscape($(node).attr('data-alt'))}'
/>`
)
$link.html($(node).html())
@ -53,12 +55,14 @@ const apiUserContent = {
*/
// use this method to process any user content fields returned in api responses
// this is important to handle object/embed tags safely, and to properly display audio/video tags
convert (html, options = {}) {
convert(html, options = {}) {
const $dummy = $('<div />').html(html)
// finds any <video/audio class="instructure_inline_media_comment"> and turns them into media comment thumbnails
$dummy.find('video.instructure_inline_media_comment,audio.instructure_inline_media_comment').replaceWith(function () {
return apiUserContent.toMediaCommentLink(this)
})
$dummy
.find('video.instructure_inline_media_comment,audio.instructure_inline_media_comment')
.replaceWith(function() {
return apiUserContent.toMediaCommentLink(this)
})
// remove any embed tags inside an object tag, to avoid repeated translations
$dummy.find('object.instructure_user_content:not(#kaltura_player) embed').remove()
@ -71,50 +75,54 @@ const apiUserContent = {
//
// see the corresponding code in lib/user_content.rb for non-api user
// content handling
$dummy.find('object.instructure_user_content,embed.instructure_user_content').replaceWith(function () {
const $this = $(this)
if (!$this.data('uc_snippet') || !$this.data('uc_sig')) {
return this
}
$dummy
.find('object.instructure_user_content,embed.instructure_user_content')
.replaceWith(function() {
const $this = $(this)
if (!$this.data('uc_snippet') || !$this.data('uc_sig')) {
return this
}
const uuid = _.uniqueId('uc_')
let action = '/object_snippet'
if (ENV.files_domain) {
action = `//${ENV.files_domain}${action}`
}
const $form = $(
`<form
const uuid = _.uniqueId('uc_')
let action = '/object_snippet'
if (ENV.files_domain) {
action = `//${ENV.files_domain}${action}`
}
const $form = $(
`<form
action='${htmlEscape(action)}'
method='post'
class='user_content_post_form'
target='${htmlEscape(uuid)}'
id='form-${htmlEscape(uuid)}'
/>`
)
$form.append(
$("<input type='hidden'/>").attr({
name: 'object_data',
value: $this.data('uc_snippet'),
})
)
$form.append(
$("<input type='hidden'/>").attr({
name: 's',
value: $this.data('uc_sig'),
})
)
$('body').append($form)
setTimeout(() => $form.submit(), 0)
return $(
`<iframe
)
$form.append(
$("<input type='hidden'/>").attr({
name: 'object_data',
value: $this.data('uc_snippet')
})
)
$form.append(
$("<input type='hidden'/>").attr({
name: 's',
value: $this.data('uc_sig')
})
)
$('body').append($form)
setTimeout(() => $form.submit(), 0)
return $(
`<iframe
class='user_content_iframe'
name='${htmlEscape(uuid)}'
style='width: ${htmlEscape($this.data('uc_width'))}; height: ${htmlEscape($this.data('uc_height'))};'
style='width: ${htmlEscape($this.data('uc_width'))}; height: ${htmlEscape(
$this.data('uc_height')
)};'
frameborder='0'
title='${htmlEscape(I18n.t('User Content'))}'
/>`
)
})
)
})
$dummy.find('img.equation_image').each((index, equationImage) => {
const $equationImage = $(equationImage)

View File

@ -21,11 +21,11 @@ const formatter = {
}
// see also lib/i18n/lolcalize.rb
function letThereBeLols (str) {
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) => {
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
@ -40,7 +40,7 @@ function letThereBeLols (str) {
return result.join('')
}
export default function i18nLolcalize (strOrObj) {
export default function i18nLolcalize(strOrObj) {
if (typeof strOrObj === 'string') return letThereBeLols(strOrObj)
const result = {}

View File

@ -17,7 +17,7 @@
import pluralize from 'str/pluralize'
export default function splitAssetString (assetString = '', toPlural = true) {
export default function splitAssetString(assetString = '', toPlural = true) {
const match = assetString.match(/(.*)_(\d+)$/)
if (match) {
const contextType = toPlural ? pluralize(match[1]) : match[1]

View File

@ -27,7 +27,7 @@
// @param {String} string - the string to convert to underscores
//
// @return String
export default function underscore (string) {
export default function underscore(string) {
if (typeof string !== 'string' || string === '') return string
return string
.replace(/([A-Z])/g, '_$1')

View File

@ -22,7 +22,7 @@ import $ from 'jquery'
import AvatarDialogView from '../views/profiles/AvatarDialogView'
export default class AvatarWidget {
constructor (el) {
constructor(el) {
this.$el = $(el)
this._attachEvents()
}
@ -30,7 +30,7 @@ export default class AvatarWidget {
// Internal: Add click event to @$el to open widget.
//
// Returns nothing.
_attachEvents () {
_attachEvents() {
return this.$el.on('click', this._openAvatarDialog)
}
@ -47,5 +47,5 @@ export default class AvatarWidget {
this.avatarDialog = new AvatarDialogView()
}
return this.avatarDialog.show()
};
}
}

View File

@ -28,7 +28,7 @@ import jQuery from 'jquery'
import 'jquery.ajaxJSON'
export default class BackoffPoller {
constructor (url, handler, opts = {}) {
constructor(url, handler, opts = {}) {
this.url = url
this.handler = handler
this.baseInterval = opts.baseInterval != null ? opts.baseInterval : 1000
@ -38,7 +38,7 @@ export default class BackoffPoller {
this.initialDelay = opts.initialDelay != null ? opts.initialDelay : true
}
start () {
start() {
if (this.running) {
this.reset()
} else {
@ -47,19 +47,19 @@ export default class BackoffPoller {
return this
}
then (callback) {
(this.callbacks || (this.callbacks = [])).push(callback)
then(callback) {
;(this.callbacks || (this.callbacks = [])).push(callback)
}
reset () {
reset() {
this.nextInterval = this.baseInterval
this.attempts = 0
}
stop (success = false) {
stop(success = false) {
if (this.running) clearTimeout(this.running)
delete this.running
if (success && this.callbacks) this.callbacks.forEach((callback) => callback())
if (success && this.callbacks) this.callbacks.forEach(callback => callback())
delete this.callbacks
}
@ -69,7 +69,7 @@ export default class BackoffPoller {
return jQuery.ajaxJSON(this.url, 'GET', {}, this.handle, (data, xhr) =>
this.handleErrors ? this.handle(data, xhr) : this.stop()
)
};
}
handle = (data, xhr) => {
switch (this.handler(data, xhr)) {
@ -82,9 +82,9 @@ export default class BackoffPoller {
default:
return this.stop()
}
};
}
nextPoll (reset = false) {
nextPoll(reset = false) {
if (reset) {
this.reset()
if (!this.initialDelay) return this.poll()

View File

@ -23,7 +23,7 @@ xsslint xssable.receiver.whitelist builder
*/
export default {
fromCanvas (canvas, type = 'image/jpeg') {
fromCanvas(canvas, type = 'image/jpeg') {
const url = canvas.toDataURL(type)
const binary = atob(url.split(',')[1])
const codes = _.map(binary, char => char.charCodeAt(0))
@ -31,11 +31,11 @@ export default {
return this._newBlob(data, type)
},
fromXHR (response, type = 'image/jpeg') {
fromXHR(response, type = 'image/jpeg') {
return this._newBlob(response, type)
},
_newBlob (src, type) {
_newBlob(src, type) {
const builder = this._blobBuilder()
if (builder) {
builder.append(src)
@ -45,10 +45,14 @@ export default {
}
},
_blobBuilder () {
_blobBuilder() {
if (typeof window.Blob === 'function') return null
window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder
window.BlobBuilder =
window.BlobBuilder ||
window.WebKitBlobBuilder ||
window.MozBlobBuilder ||
window.MSBlobBuilder
if (typeof window.BlobBuilder === 'undefined') return null
return new window.BlobBuilder()
}

View File

@ -25,42 +25,71 @@ import DateHelper from 'jsx/shared/helpers/dateHelper'
const DATE_RANGE_ERRORS = {
due_at: {
start_range: {
get section(){ return I18n.t('Due date cannot be before section start')},
get course(){ return I18n.t('Due date cannot be before course start')},
get term(){ return I18n.t('Due date cannot be before term start')},
get section() {
return I18n.t('Due date cannot be before section start')
},
get course() {
return I18n.t('Due date cannot be before course start')
},
get term() {
return I18n.t('Due date cannot be before term start')
}
},
end_range: {
get section(){ return I18n.t('Due date cannot be after section end')},
get course(){ return I18n.t('Due date cannot be after course end')},
get term(){ return I18n.t('Due date cannot be after term end')},
},
get section() {
return I18n.t('Due date cannot be after section end')
},
get course() {
return I18n.t('Due date cannot be after course end')
},
get term() {
return I18n.t('Due date cannot be after term end')
}
}
},
unlock_at: {
start_range: {
get section(){ return I18n.t('Unlock date cannot be before section start')},
get course(){ return I18n.t('Unlock date cannot be before course start')},
get term(){ return I18n.t('Unlock date cannot be before term start')},
get section() {
return I18n.t('Unlock date cannot be before section start')
},
get course() {
return I18n.t('Unlock date cannot be before course start')
},
get term() {
return I18n.t('Unlock date cannot be before term start')
}
},
end_range: {
get due(){ return I18n.t('Unlock date cannot be after due date')},
get lock(){ return I18n.t('Unlock date cannot be after lock date')},
},
get due() {
return I18n.t('Unlock date cannot be after due date')
},
get lock() {
return I18n.t('Unlock date cannot be after lock date')
}
}
},
lock_at: {
start_range: {
get due(){ return I18n.t('Lock date cannot be before due date')},
get due() {
return I18n.t('Lock date cannot be before due date')
}
},
end_range: {
get section(){ return I18n.t('Lock date cannot be after section end')},
get course(){ return I18n.t('Lock date cannot be after course end')},
get term(){ return I18n.t('Lock date cannot be after term end')},
},
},
get section() {
return I18n.t('Lock date cannot be after section end')
},
get course() {
return I18n.t('Lock date cannot be after course end')
},
get term() {
return I18n.t('Lock date cannot be after term end')
}
}
}
}
export default class DateValidator {
constructor (params) {
constructor(params) {
this.dateRange = params.date_range
this.data = params.data
this.forIndividualStudents = params.forIndividualStudents
@ -70,7 +99,7 @@ export default class DateValidator {
this.dueDateRequired = params.postToSIS && ENV.DUE_DATE_REQUIRED_FOR_ACCOUNT
}
validateDatetimes () {
validateDatetimes() {
const lockAt = this.data.lock_at
const unlockAt = this.data.unlock_at
const dueAt = this.data.due_at
@ -78,15 +107,19 @@ export default class DateValidator {
const currentDateRange = section ? this.getSectionRange(section) : this.dateRange
const datetimesToValidate = []
if (currentDateRange.start_at && currentDateRange.start_at.date && !this.forIndividualStudents) {
if (
currentDateRange.start_at &&
currentDateRange.start_at.date &&
!this.forIndividualStudents
) {
datetimesToValidate.push({
date: currentDateRange.start_at.date,
validationDates: {
due_at: dueAt,
unlock_at: unlockAt,
unlock_at: unlockAt
},
range: 'start_range',
type: currentDateRange.start_at.date_context,
type: currentDateRange.start_at.date_context
})
}
if (currentDateRange.end_at && currentDateRange.end_at.date && !this.forIndividualStudents) {
@ -94,42 +127,42 @@ export default class DateValidator {
date: currentDateRange.end_at.date,
validationDates: {
due_at: dueAt,
lock_at: lockAt,
lock_at: lockAt
},
range: 'end_range',
type: currentDateRange.end_at.date_context,
type: currentDateRange.end_at.date_context
})
}
if (dueAt) {
datetimesToValidate.push({
date: dueAt,
validationDates: {
lock_at: lockAt,
lock_at: lockAt
},
range: 'start_range',
type: 'due',
type: 'due'
})
datetimesToValidate.push({
date: dueAt,
validationDates: {
unlock_at: unlockAt,
unlock_at: unlockAt
},
range: 'end_range',
type: 'due',
type: 'due'
})
}
if (this.dueDateRequired) {
datetimesToValidate.push({
date: dueAt,
dueDateRequired: this.dueDateRequired,
dueDateRequired: this.dueDateRequired
})
}
if (this.hasGradingPeriods && !this.userIsAdmin && this.data.persisted === false) {
datetimesToValidate.push({
date: dueAt,
range: 'grading_period_range',
range: 'grading_period_range'
})
}
@ -137,36 +170,36 @@ export default class DateValidator {
datetimesToValidate.push({
date: lockAt,
validationDates: {
unlock_at: unlockAt,
unlock_at: unlockAt
},
range: 'end_range',
type: 'lock',
type: 'lock'
})
}
const errs = {}
return this._validateDatetimeSequences(datetimesToValidate, errs)
}
getSectionRange (section) {
getSectionRange(section) {
if (!section.override_course_and_term_dates) return this.dateRange
if (section.start_at) {
this.dateRange.start_at = {
date: section.start_at,
date_context: 'section',
date_context: 'section'
}
}
if (section.end_at) {
this.dateRange.end_at = {
date: section.end_at,
date_context: 'section',
date_context: 'section'
}
}
return this.dateRange
}
_validateMultipleGradingPeriods (date, errs) {
_validateMultipleGradingPeriods(date, errs) {
const helper = new GradingPeriodsHelper(this.gradingPeriods)
const dueAt = date === null ? null : new Date(this._formatDatetime(date))
if (!helper.isDateInClosedGradingPeriod(dueAt)) return
@ -175,15 +208,15 @@ export default class DateValidator {
if (earliestDate) {
const formatted = DateHelper.formatDateForDisplay(earliestDate)
errs.due_at = I18n.t('Please enter a due date on or after %{earliestDate}', {
earliestDate: formatted,
earliestDate: formatted
})
} else {
errs.due_at = I18n.t('Due date cannot fall in a closed grading period')
}
}
_validateDatetimeSequences (datetimesToValidate, errs) {
datetimesToValidate.forEach((datetimeSet) => {
_validateDatetimeSequences(datetimesToValidate, errs) {
datetimesToValidate.forEach(datetimeSet => {
if (datetimeSet.dueDateRequired && !datetimeSet.date) {
errs.due_at = I18n.t('Please add a due date')
}
@ -193,14 +226,20 @@ export default class DateValidator {
switch (datetimeSet.range) {
case 'start_range':
_.each(datetimeSet.validationDates, (validationDate, dateType) => {
if (validationDate && this._formatDatetime(datetimeSet.date) > this._formatDatetime(validationDate)) {
if (
validationDate &&
this._formatDatetime(datetimeSet.date) > this._formatDatetime(validationDate)
) {
errs[dateType] = DATE_RANGE_ERRORS[dateType][datetimeSet.range][datetimeSet.type]
}
})
break
case 'end_range':
_.each(datetimeSet.validationDates, (validationDate, dateType) => {
if (validationDate && this._formatDatetime(datetimeSet.date) < this._formatDatetime(validationDate)) {
if (
validationDate &&
this._formatDatetime(datetimeSet.date) < this._formatDatetime(validationDate)
) {
errs[dateType] = DATE_RANGE_ERRORS[dateType][datetimeSet.range][datetimeSet.type]
}
})
@ -211,7 +250,7 @@ export default class DateValidator {
return errs
}
_formatDatetime (date) {
_formatDatetime(date) {
return tz.format(tz.parse(date), '%F %R')
}
}

View File

@ -17,10 +17,14 @@
import _ from 'underscore'
export default function numberCompare (a, b, options = {}) {
return !_.isNumber(a) ?
(!_.isNumber(b) ? 0 : 1)
: !_.isNumber(b) ?
-1 :
options.descending ? b - a : a - b
export default function numberCompare(a, b, options = {}) {
return !_.isNumber(a)
? !_.isNumber(b)
? 0
: 1
: !_.isNumber(b)
? -1
: options.descending
? b - a
: a - b
}

View File

@ -30,7 +30,7 @@ export default class PandaPubPoller {
// wrap your normal poll method. It is passed another function that should
// be called when the poll is complete.
constructor (pollInterval, rarePollInterval, pollCB) {
constructor(pollInterval, rarePollInterval, pollCB) {
this.pollInterval = pollInterval
this.rarePollInterval = rarePollInterval
this.pollCB = pollCB
@ -55,7 +55,7 @@ export default class PandaPubPoller {
// Set the function to call when data is received via the streaming
// channel.
setOnData = (streamingCB) => {
setOnData = streamingCB => {
this.streamingCB = streamingCB
}
@ -82,8 +82,7 @@ export default class PandaPubPoller {
//
// @api private
startTimeout = () => this.timeout = setTimeout(this.considerPoll, this.pollInterval)
startTimeout = () => (this.timeout = setTimeout(this.considerPoll, this.pollInterval))
// Stop the timeout
//
@ -126,7 +125,7 @@ export default class PandaPubPoller {
// don't attempt to subscribe until we get a channel and a token
if (!this.channel || !this.token) return
this.subscription = pandapub.subscribe(this.channel, this.token, (message) => {
this.subscription = pandapub.subscribe(this.channel, this.token, message => {
this.lastUpdate = Date.now()
this.streamingCB(message)
})

View File

@ -21,7 +21,7 @@ import $ from 'jquery'
// it will be passed the position cordinates and a feedback object which,
// among other things, tells you where it positioned it relative to the target. we use it to add some
// css classes that handle putting the pointer triangle (aka: caret) back to the trigger.
function using (position, feedback) {
function using(position, feedback) {
if (position.top < 0) position.top = 0
return $(this)
.css(position)
@ -32,63 +32,61 @@ let idCounter = 0
const activePopovers = []
function trapFocus(element) {
const focusableEls = element.querySelectorAll('a[href], button, textarea, input[type="text"], input[type="radio"], input[type="checkbox"], select');
const firstFocusableEl = focusableEls[0];
const lastFocusableEl = focusableEls[focusableEls.length - 1];
const KEYCODE_TAB = 9;
const focusableEls = element.querySelectorAll(
'a[href], button, textarea, input[type="text"], input[type="radio"], input[type="checkbox"], select'
)
const firstFocusableEl = focusableEls[0]
const lastFocusableEl = focusableEls[focusableEls.length - 1]
const KEYCODE_TAB = 9
element.addEventListener('keydown', e => {
const isTabPressed = (e.key === 'Tab' || e.keyCode === KEYCODE_TAB);
if (!isTabPressed) {
return;
const isTabPressed = e.key === 'Tab' || e.keyCode === KEYCODE_TAB
if (!isTabPressed) {
return
}
if (e.shiftKey) {
/* shift + tab */ if (document.activeElement === firstFocusableEl) {
lastFocusableEl.focus()
e.preventDefault()
}
if ( e.shiftKey ) /* shift + tab */ {
if (document.activeElement === firstFocusableEl) {
lastFocusableEl.focus();
e.preventDefault();
}
} else /* tab */ {
if (document.activeElement === lastFocusableEl) {
setTimeout(() => {
firstFocusableEl.focus();
})
e.preventDefault();
}
}
});
} /* tab */ else if (document.activeElement === lastFocusableEl) {
setTimeout(() => {
firstFocusableEl.focus()
})
e.preventDefault()
}
})
}
export default class Popover {
ignoreOutsideClickSelector = '.ui-dialog'
constructor (triggerEvent, content, options = {}) {
constructor(triggerEvent, content, options = {}) {
this.content = content
this.options = options
this.trigger = $(triggerEvent.currentTarget)
this.triggerAction = triggerEvent.type
this.focusTrapped = false
this.el = $(this.content).addClass('carat-bottom').data('popover', this).keydown((event) => {
// if the user hits the escape key, reset the focus to what it was.
if (event.keyCode === $.ui.keyCode.ESCAPE) this.hide()
this.el = $(this.content)
.addClass('carat-bottom')
.data('popover', this)
.keydown(event => {
// if the user hits the escape key, reset the focus to what it was.
if (event.keyCode === $.ui.keyCode.ESCAPE) this.hide()
// If the user tabs or shift-tabs away, close.
if (event.keyCode !== $.ui.keyCode.TAB) return
// If the user tabs or shift-tabs away, close.
if (event.keyCode !== $.ui.keyCode.TAB) return
const tabbables = $(':tabbable', this.el)
const index = $.inArray(event.target, tabbables)
if (index === -1) return
const tabbables = $(':tabbable', this.el)
const index = $.inArray(event.target, tabbables)
if (index === -1) return
if (event.shiftKey) {
if (!this.focusTrapped && index === 0) this.hide()
} else {
if (!this.focusTrapped && index === tabbables.length - 1) this.hide()
}
})
if (event.shiftKey) {
if (!this.focusTrapped && index === 0) this.hide()
} else if (!this.focusTrapped && index === tabbables.length - 1) this.hide()
})
this.el.delegate('.popover_close', 'keyclick click', (event) => {
this.el.delegate('.popover_close', 'keyclick click', event => {
event.preventDefault()
this.hide()
})
@ -96,12 +94,12 @@ export default class Popover {
this.show(triggerEvent)
}
trapFocus (element) {
trapFocus(element) {
this.focusTrapped = true
trapFocus(element)
}
show (triggerEvent) {
show(triggerEvent) {
// when the popover is open, we don't want SR users to be able to navigate to the flash messages
let popoverToHide
$.screenReaderFlashMessageExclusive('')
@ -113,7 +111,7 @@ export default class Popover {
const id = `popover-${idCounter++}`
this.trigger.attr({
'aria-expanded': true,
'aria-controls': id,
'aria-controls': id
})
this.previousTarget = triggerEvent.currentTarget
@ -123,8 +121,18 @@ export default class Popover {
.show()
this.position()
if (triggerEvent.type !== 'mouseenter') {
this.el.find(':tabbable').first().focus()
setTimeout(() => this.el.find(':tabbable').first().focus(), 100)
this.el
.find(':tabbable')
.first()
.focus()
setTimeout(
() =>
this.el
.find(':tabbable')
.first()
.focus(),
100
)
}
document.querySelector('#application').setAttribute('aria-hidden', 'true')
@ -136,14 +144,19 @@ export default class Popover {
const actualOffset = triggerEvent.pageX - this.trigger.offset().left
const leftBound = Math.max(0, this.trigger.width() / 2 - this.el.width() / 2) + 20
const rightBound = this.trigger.width() - leftBound
const caratOffset = Math.min(Math.max(leftBound, actualOffset), rightBound) + differenceInOffset + additionalOffset
$('<span class="ui-menu-carat"><span /></span>').css('left', caratOffset).prependTo(this.el)
const caratOffset =
Math.min(Math.max(leftBound, actualOffset), rightBound) +
differenceInOffset +
additionalOffset
$('<span class="ui-menu-carat"><span /></span>')
.css('left', caratOffset)
.prependTo(this.el)
this.positionInterval = setInterval(this.position, 200)
$(window).click(this.outsideClickHandler)
}
hide () {
hide() {
// remove this from the activePopovers array
for (let index = 0; index < activePopovers.length; index++) {
const popover = activePopovers[index]
@ -164,23 +177,27 @@ export default class Popover {
}
// uses a fat arrow so that it has a unique guid per-instance for jquery event unbinding
outsideClickHandler = (event) => {
if (!$(event.target).closest(this.el.add(this.trigger).add(this.ignoreOutsideClickSelector)).length) {
outsideClickHandler = event => {
if (
!$(event.target).closest(this.el.add(this.trigger).add(this.ignoreOutsideClickSelector))
.length
) {
this.hide()
}
}
position = () => this.el.position({
my: `center ${this.options.verticalSide === 'bottom' ? 'top' : 'bottom'}`,
at: `center ${this.options.verticalSide || 'top'}`,
of: this.trigger,
offset: `0px ${this.offsetPx()}px`,
within: 'body',
collision: `flipfit ${this.options.verticalSide ? 'none' : 'flipfit'}`,
using,
})
position = () =>
this.el.position({
my: `center ${this.options.verticalSide === 'bottom' ? 'top' : 'bottom'}`,
at: `center ${this.options.verticalSide || 'top'}`,
of: this.trigger,
offset: `0px ${this.offsetPx()}px`,
within: 'body',
collision: `flipfit ${this.options.verticalSide ? 'none' : 'flipfit'}`,
using
})
offsetPx () {
offsetPx() {
const offset = this.options.verticalSide === 'bottom' ? 10 : -10
if (this.options.invertOffset) {
return offset * -1
@ -189,7 +206,7 @@ export default class Popover {
}
}
restoreFocus () {
restoreFocus() {
// set focus back to the previously focused item.
if (this.previousTarget && $(this.previousTarget).is(':visible')) {
this.previousTarget.focus()

View File

@ -16,7 +16,7 @@
// with this program. If not, see <http://www.gnu.org/licenses/>.
export default class SisValidationHelper {
constructor (params) {
constructor(params) {
this.postToSIS = params.postToSIS
this.allDates = params.allDates
this.dueDateRequired = params.dueDateRequired
@ -26,7 +26,7 @@ export default class SisValidationHelper {
this.maxNameLength = params.maxNameLength
}
nameTooLong () {
nameTooLong() {
if (!this.postToSIS) return false
if (this.maxNameLengthRequired) {
return this.nameLengthComparison()
@ -35,26 +35,28 @@ export default class SisValidationHelper {
}
}
nameLengthComparison () {
nameLengthComparison() {
return this.modelName.length > this.maxNameLength
}
dueAtNotValid (date) {
if(!date) return true
return (date.dueAt === null || date.dueAt === undefined || date.dueAt === '')
dueAtNotValid(date) {
if (!date) return true
return date.dueAt === null || date.dueAt === undefined || date.dueAt === ''
}
dueDateMissingDifferentiated () {
dueDateMissingDifferentiated() {
if (!this.allDates) return false
return (this.allDates.map(this.dueAtNotValid).indexOf(true) !== -1)
return this.allDates.map(this.dueAtNotValid).indexOf(true) !== -1
}
baseDueDateMissing () {
return ((!this.allDates || this.allDates.length === 0) && !this.dueDate)
baseDueDateMissing() {
return (!this.allDates || this.allDates.length === 0) && !this.dueDate
}
dueDateMissing () {
dueDateMissing() {
if (!this.postToSIS) return false
return this.dueDateRequired && (this.baseDueDateMissing() || this.dueDateMissingDifferentiated())
return (
this.dueDateRequired && (this.baseDueDateMissing() || this.dueDateMissingDifferentiated())
)
}
}

View File

@ -18,7 +18,7 @@
import $ from 'jquery'
import I18n from 'i18n!site'
export default function addPrivacyLinkToDialog ($dialog) {
export default function addPrivacyLinkToDialog($dialog) {
if (!(ENV.ACCOUNT && ENV.ACCOUNT.privacy_policy_url)) return
const $privacy = $('<a>', {

View File

@ -20,8 +20,7 @@ import {isRTL} from 'jsx/shared/helpers/rtlHelper'
const loadedStylesheets = {}
const brandableCss = {
getCssVariant () {
getCssVariant() {
const variant = window.ENV.use_responsive_layout ? 'responsive_layout' : 'new_styles'
const contrast = window.ENV.use_high_contrast ? 'high_contrast' : 'normal_contrast'
const rtl = isRTL() ? '_rtl' : ''
@ -31,10 +30,8 @@ const brandableCss = {
// combinedChecksum should be like '09f833ef7a'
// and includesNoVariables should be true if this bundle does not
// "@include" variables.scss, brand_variables.scss or variant_variables.scss
urlFor (bundleName, {combinedChecksum, includesNoVariables}) {
const brandAndVariant = includesNoVariables
? 'no_variables'
: brandableCss.getCssVariant()
urlFor(bundleName, {combinedChecksum, includesNoVariables}) {
const brandAndVariant = includesNoVariables ? 'no_variables' : brandableCss.getCssVariant()
return [
window.ENV.ASSET_HOST || '',
@ -46,7 +43,7 @@ const brandableCss = {
},
// bundleName should be something like 'jst/foo'
loadStylesheet (bundleName, opts) {
loadStylesheet(bundleName, opts) {
if (bundleName in loadedStylesheets) return
const linkElement = document.createElement('link')

View File

@ -18,17 +18,17 @@
import $ from 'jquery'
import _ from 'underscore'
import h from 'str/htmlEscape'
import listWithOthers from '../util/listWithOthers'
import listWithOthers from './listWithOthers'
import 'jquery.instructure_misc_helpers'
function prepare (context, filters) {
function prepare(context, filters) {
context = _.clone(context)
context.activeFilter = _.includes(filters, `${context.type}_${context.id}`)
context.sortBy = `${context.activeFilter ? 0 : 1}_${context.name.toLowerCase()}`
return context
}
function format (context, linkToContexts) {
function format(context, linkToContexts) {
let html = h(context.name)
if (context.activeFilter) {
html = `<span class='active-filter'>${html}</span>`
@ -43,7 +43,7 @@ function format (context, linkToContexts) {
// and a map of possible contexts by type,
// return an html sentence/list of the contexts (maybe links, etc., see
// options)
export default function contextList (contextMap, allContexts, options = {}) {
export default function contextList(contextMap, allContexts, options = {}) {
const filters = options.filters != null ? options.filters : []
let contexts = []
for (const type in contextMap) {

View File

@ -15,14 +15,14 @@
// 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 coupleTimeFields ($start, $end, $date) {
export default function coupleTimeFields($start, $end, $date) {
// construct blur callback that couples them in order so that $start can
// never be less than $end
function blur ($blurred) {
function blur($blurred) {
if ($date && $blurred === $date) {
const date = $date.data('unfudged-date')
if (date) {
[$start, $end].forEach((el) => {
;[$start, $end].forEach(el => {
const instance = el.data('instance')
if (instance) instance.setDate(date)
})

View File

@ -25,7 +25,7 @@ xsslint safeString.identifier i
*/
const builders = {
year (options, htmlOptions) {
year(options, htmlOptions) {
const step = options.startYear < options.endYear ? 1 : -1
const $result = $('<select />', htmlOptions)
if (options.includeBlank) $result.append('<option />')
@ -36,7 +36,7 @@ const builders = {
}
return $result
},
month (options, htmlOptions, dateSettings) {
month(options, htmlOptions, dateSettings) {
const months = dateSettings.month_names
const $result = $('<select />', htmlOptions)
if (options.includeBlank) $result.append('<option />')
@ -45,7 +45,7 @@ const builders = {
}
return $result
},
day (options, htmlOptions) {
day(options, htmlOptions) {
const $result = $('<select />', htmlOptions)
if (options.includeBlank) $result.append('<option />')
for (let i = 1; i <= 31; i++) {
@ -57,7 +57,7 @@ const builders = {
// generates something like rails' date_select/select_date
// TODO: feature parity
export default function dateSelect (name, options, htmlOptions = _.clone(options)) {
export default function dateSelect(name, options, htmlOptions = _.clone(options)) {
const validOptions = ['type', 'startYear', 'endYear', 'includeBlank', 'order']
validOptions.forEach(opt => delete htmlOptions[opt])
@ -88,7 +88,11 @@ export default function dateSelect (name, options, htmlOptions = _.clone(options
const $result = $('<span>')
// in coffeescript: for i in [0...options.order.length]
for (let i = 0, end = options.order.length, asc = end >= 0; asc ? i < end : i > end; asc ? i++ : i--) {
for (
let i = 0, end = options.order.length, asc = end >= 0;
asc ? i < end : i > end;
asc ? i++ : i--
) {
const type = options.order[i]
const tName = name.replace(/(\]?)$/, `(${position[type]}i)$1`)
const html = builders[type](options, {name: tName, ...htmlOptions}, dateSettings)

View File

@ -23,10 +23,10 @@ import unflatten from '../object/unflatten'
const coerceTypes = {
true: true,
false: false,
null: null,
null: null
}
export default function deparam (params, coerce) {
export default function deparam(params, coerce) {
// shortcut for just deparam'ing the current querystring
if (!params || typeof params === 'boolean') {
const currentQueryString = window.location.search
@ -35,24 +35,28 @@ export default function deparam (params, coerce) {
}
const obj = {}
params.replace(/\+/g, ' ').split('&').forEach((param) => {
let [key, val] = param.split('=')
key = decodeURIComponent(key)
val = decodeURIComponent(val)
params
.replace(/\+/g, ' ')
.split('&')
.forEach(param => {
let [key, val] = param.split('=')
key = decodeURIComponent(key)
val = decodeURIComponent(val)
// coerce values.
if (coerce) {
val = val && !isNaN(val)
? +val // number
: val === 'undefined'
// coerce values.
if (coerce) {
val =
val && !isNaN(val)
? +val // number
: val === 'undefined'
? undefined // undefined
: coerceTypes[val] !== undefined
? coerceTypes[val] // true, false, null
: val // string
}
? coerceTypes[val] // true, false, null
: val // string
}
obj[key] = val
})
obj[key] = val
})
return unflatten(obj)
}

View File

@ -35,6 +35,6 @@ const types = {
}
}
export default function enrollmentName (type) {
export default function enrollmentName(type) {
return types[type] || type
}

View File

@ -20,7 +20,7 @@ import I18n from 'i18nObj'
const units = ['byte', 'bytes', 'KB', 'MB', 'GB', 'TB']
// converts bytes into a nice representation with unit. e.g. 13661855 -> 13.7 MB, 825399 -> 825 KB, 1396 -> 1 KB
export default function friendlyBytes (value) {
export default function friendlyBytes(value) {
let resInt, resValue
const bytes = parseInt(value, 10)
if (bytes.toString() === 'NaN') return '--'

View File

@ -15,10 +15,10 @@
// 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 generateUUID () {
export default function generateUUID() {
let now = Date.now()
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (char) => {
const r = ((now + Math.random() * 16) % 16) | 0
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, char => {
const r = (now + Math.random() * 16) % 16 | 0
now = Math.floor(now / 16)
const newChar = char === 'x' ? r : (r & 0x7) | 0x8
return newChar.toString(16)

View File

@ -15,8 +15,13 @@
// 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 groupHasSubmissions (group) {
return group.get('has_submission') || group.users().models.reduce((hasSubmission, user) => {
return hasSubmission || (user.has('group_submissions') && user.get('group_submissions').length > 0)
}, false)
export default function groupHasSubmissions(group) {
return (
group.get('has_submission') ||
group.users().models.reduce((hasSubmission, user) => {
return (
hasSubmission || (user.has('group_submissions') && user.get('group_submissions').length > 0)
)
}, false)
)
}

View File

@ -52,7 +52,7 @@ class KalturaAnalytics {
data['event:eventTimestamp'] = new Date().getTime()
return this.queueApiCall(this.apiUrl + $.param(data))
};
}
// kaltura expects a persistent analytic session token for the user
// this generates a simple session id for analytic purposes
@ -67,7 +67,7 @@ class KalturaAnalytics {
).replace(/\./g, '')
return $.cookie('kaltura_analytic_tracker', this.kaSession, {path: '/'})
}
};
}
// pulls the kaltura domain from the plugin settins and sets up the base
// url for sending analytics events
@ -80,7 +80,7 @@ class KalturaAnalytics {
}
return (this.apiUrl = `${domain}/api_v3/index.php?`)
};
}
// Since the analytic call is a cross-domain call, set the url in an iFrame
setupApiIframes = count => {
@ -95,16 +95,16 @@ class KalturaAnalytics {
// iframe, so just send them every 4 seconds
const queue = []
const f = ((iframe, queue) =>
(function() {
function() {
let url
if ((url = queue.shift())) {
return (iframe.src = url)
}
}))(iframe, queue)
})(iframe, queue)
this.iframes[i] = {iframe, queue, pinger: _.throttle(f, 4000)}
}
return this.iframes
};
}
queueApiCall = url => {
if (!this.iframes) {
@ -114,7 +114,7 @@ class KalturaAnalytics {
this.iframes[this.qIndex].pinger()
this.qIndex = (this.qIndex + 1) % this.iframes.length
return this.qIndex
};
}
// Adds event listenrs to the mediaElement player
//
@ -208,7 +208,7 @@ class KalturaAnalytics {
},
false
)
};
}
}
export default function(mediaId, mediaElement, pluginSettings) {

View File

@ -20,14 +20,15 @@ import $ from 'jquery'
import h from 'str/htmlEscape'
import 'jquery.instructure_misc_helpers'
export default function listWithOthers (strings, cutoff = 2) {
export default function listWithOthers(strings, cutoff = 2) {
if (strings.length > cutoff) {
strings = strings.slice(0, cutoff).concat([strings.slice(cutoff, strings.length)])
}
return $.toSentence(strings.map(strOrArray =>
typeof strOrArray === 'string' || strOrArray instanceof h.SafeString
? `<span>${h(strOrArray)}</span>`
: `
return $.toSentence(
strings.map(strOrArray =>
typeof strOrArray === 'string' || strOrArray instanceof h.SafeString
? `<span>${h(strOrArray)}</span>`
: `
<span class='others'>
${h(I18n.t('other', 'other', {count: strOrArray.length}))}
<span>
@ -39,4 +40,3 @@ export default function listWithOthers (strings, cutoff = 2) {
)
)
}

View File

@ -19,7 +19,7 @@ import $ from 'jquery'
import 'jquery.ajaxJSON'
export default {
toggle (button) {
toggle(button) {
const data = $(button).data.bind($(button))
return $.ajaxJSON(data('url'), data('isChecked') ? 'DELETE' : 'PUT', {}, () => {
data('isChecked', !data('isChecked'))

View File

@ -27,7 +27,9 @@ import I18n from 'i18n!mimeClass'
// (and app/stylesheets/components/deprecated/_fancy_links.scss if it is still being used)
const mimeClasses = {
audio: {
get displayName(){return I18n.t('Audio')},
get displayName() {
return I18n.t('Audio')
},
mimeTypes: [
'audio/x-mpegurl',
'audio/x-pn-realaudio',
@ -40,7 +42,9 @@ const mimeClasses = {
]
},
code: {
get displayName(){return I18n.t('Source code')},
get displayName() {
return I18n.t('Source code')
},
mimeTypes: [
'text/xml',
'text/css',
@ -51,7 +55,9 @@ const mimeClasses = {
]
},
doc: {
get displayName(){return I18n.t('Text document')},
get displayName() {
return I18n.t('Text document')
},
mimeTypes: [
'application/x-docx',
'text/rtf',
@ -62,50 +68,48 @@ const mimeClasses = {
]
},
flash: {
get displayName(){return I18n.t('Flash')},
mimeTypes: [
'application/x-shockwave-flash'
]
get displayName() {
return I18n.t('Flash')
},
mimeTypes: ['application/x-shockwave-flash']
},
html: {
get displayName(){return I18n.t('Web page')},
mimeTypes: [
'text/html',
'application/xhtml+xml'
]
get displayName() {
return I18n.t('Web page')
},
mimeTypes: ['text/html', 'application/xhtml+xml']
},
image: {
get displayName(){return I18n.t('Image')},
mimeTypes: [
'image/png',
'image/x-psd',
'image/gif',
'image/pjpeg',
'image/jpeg'
]
get displayName() {
return I18n.t('Image')
},
mimeTypes: ['image/png', 'image/x-psd', 'image/gif', 'image/pjpeg', 'image/jpeg']
},
ppt: {
get displayName(){return I18n.t('Presentation')},
get displayName() {
return I18n.t('Presentation')
},
mimeTypes: [
'application/vnd.openxmlformats-officedocument.presentationml.presentation',
'application/vnd.ms-powerpoint'
]
},
pdf: {
get displayName(){return I18n.t('PDF')},
mimeTypes: [
'application/pdf'
]
get displayName() {
return I18n.t('PDF')
},
mimeTypes: ['application/pdf']
},
text: {
get displayName(){return I18n.t('Plain text')},
mimeTypes: [
'text',
'text/plain'
]
get displayName() {
return I18n.t('Plain text')
},
mimeTypes: ['text', 'text/plain']
},
video: {
get displayName(){return I18n.t('Video')},
get displayName() {
return I18n.t('Video')
},
mimeTypes: [
'video/mp4',
'video/x-ms-asf',
@ -118,7 +122,9 @@ const mimeClasses = {
]
},
xls: {
get displayName(){return I18n.t('Spreadsheet')},
get displayName() {
return I18n.t('Spreadsheet')
},
mimeTypes: [
'application/vnd.oasis.opendocument.spreadsheet',
'text/csv',
@ -127,7 +133,9 @@ const mimeClasses = {
]
},
zip: {
get displayName(){return I18n.t('Archive')},
get displayName() {
return I18n.t('Archive')
},
mimeTypes: [
'application/x-rar-compressed',
'application/x-zip-compressed',
@ -138,17 +146,17 @@ const mimeClasses = {
}
}
export default function mimeClass (contentType) {
export default function mimeClass(contentType) {
return mimeClass.mimeClasses[contentType] || 'file'
}
mimeClass.displayName = function (contentType) {
mimeClass.displayName = function(contentType) {
const found = mimeClasses[mimeClass(contentType)]
return found && found.displayName || I18n.t('Unknown')
return (found && found.displayName) || I18n.t('Unknown')
}
mimeClass.mimeClasses = {}
for (const cls in mimeClasses) {
const value = mimeClasses[cls]
value.mimeTypes.forEach(mimeType => mimeClass.mimeClasses[mimeType] = cls)
value.mimeTypes.forEach(mimeType => (mimeClass.mimeClasses[mimeType] = cls))
}

View File

@ -18,7 +18,7 @@
import I18n from 'i18nObj'
export default {
strings (x, y) {
strings(x, y) {
let locale = I18n.locale || 'en-US'
const locale_map = {zh_Hant: 'zh-Hant'}
locale = locale_map[locale] || locale
@ -27,19 +27,19 @@ export default {
return x.localeCompare(y, locale, {
sensitivity: 'variant',
ignorePunctuation: false,
numeric: true,
numeric: true
})
},
by (f) {
by(f) {
return (x, y) => this.strings(f(x), f(y))
},
byKey (key) {
byKey(key) {
return this.by(x => x[key])
},
byGet (key) {
byGet(key) {
return this.by(x => x.get(key))
}
}

View File

@ -18,9 +18,9 @@
import $ from 'jquery'
import 'jquery.disableWhileLoading'
export default function newCourseForm () {
export default function newCourseForm() {
const changeEvents = 'change keyup input'
function showCourseCodeIfNeeded () {
function showCourseCodeIfNeeded() {
if ($nameInput.val().trim().length > 20) {
$nameInput.unbind(changeEvents, showCourseCodeIfNeeded)
$('#course_code_wrapper').slideDown('fast')
@ -29,7 +29,7 @@ export default function newCourseForm () {
const $nameInput = $('#new_course_form [name="course[name]"]')
$nameInput.bind(changeEvents, showCourseCodeIfNeeded)
$('#new_course_form').submit(function () {
$('#new_course_form').submit(function() {
return $(this).disableWhileLoading($.Deferred())
})
}

View File

@ -16,13 +16,15 @@
// with this program. If not, see <http://www.gnu.org/licenses/>.
// taken from http://jsfiddle.net/Mottie/xcqpF/1/light/
export default function rgb2hex (rgb) {
export default function rgb2hex(rgb) {
rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i)
if (rgb && rgb.length === 4) {
return '#' +
('0' + parseInt(rgb[1],10).toString(16)).slice(-2) +
('0' + parseInt(rgb[2],10).toString(16)).slice(-2) +
('0' + parseInt(rgb[3],10).toString(16)).slice(-2)
return (
'#' +
('0' + parseInt(rgb[1], 10).toString(16)).slice(-2) +
('0' + parseInt(rgb[2], 10).toString(16)).slice(-2) +
('0' + parseInt(rgb[3], 10).toString(16)).slice(-2)
)
} else {
return ''
}

View File

@ -16,7 +16,7 @@
// with this program. If not, see <http://www.gnu.org/licenses/>.
// rounds a number to m digits
export default function round (n, digits = 0) {
export default function round(n, digits = 0) {
if (typeof n !== 'number' && !(n instanceof Number)) {
n = parseFloat(n)
}

View File

@ -17,7 +17,7 @@
const {floor} = Math
const pad = function (duration) {
const pad = function(duration) {
const padding = duration >= 0 && duration < 10 ? '0' : ''
return padding + duration.toFixed()
}
@ -29,7 +29,7 @@ const pad = function (duration) {
// 84 seconds => 01:24
// 7230 seconds => 02:00:30
// 7530 seconds => 02:05:30
export default function (seconds) {
export default function(seconds) {
if (seconds > 3600) {
const hh = floor(seconds / 3600)
const mm = floor((seconds - hh * 3600) / 60)

View File

@ -22,22 +22,18 @@ import tz from 'timezone'
import htmlEscape from 'str/htmlEscape'
import 'jquery.instructure_date_and_time'
export default function semanticDateRange (startISO, endISO) {
export default function semanticDateRange(startISO, endISO) {
if (!startISO) {
return (
`<span class="date-range date-range-no-date">
return `<span class="date-range date-range-no-date">
${htmlEscape(I18n.t('no_date', 'No Date'))}
</span>`
)
}
const startAt = tz.parse(startISO)
const endAt = tz.parse(endISO)
if (+startAt !== +endAt) {
if (!$.sameDate(startAt, endAt)) {
return (
`<span class="date-range">
return `<span class="date-range">
<time datetime='${startAt.toISOString()}'>
${$.datetimeString(startAt)}
</time> -
@ -45,10 +41,8 @@ export default function semanticDateRange (startISO, endISO) {
${$.datetimeString(endAt)}
</time>
</span>`
)
} else {
return (
`<span class="date-range">
return `<span class="date-range">
<time datetime='${startAt.toISOString()}'>
${$.dateString(startAt)}, ${$.timeString(startAt)}
</time> -
@ -56,15 +50,12 @@ export default function semanticDateRange (startISO, endISO) {
${$.timeString(endAt)}
</time>
</span>`
)
}
} else {
return (
`<span class="date-range">
return `<span class="date-range">
<time datetime='${startAt.toISOString()}'>
${$.datetimeString(startAt)}
</time>
</span>`
)
}
}

View File

@ -18,17 +18,16 @@
import $ from 'jquery'
import 'jqueryui/tooltip'
export default function vddTooltip () {
export default function vddTooltip() {
return $('.vdd_tooltip_link').tooltip({
position: {my: 'center bottom', at: 'center top-10', collision: 'fit fit'},
tooltipClass: 'center bottom vertical',
content () {
content() {
const selector = $(this).data('tooltipSelector')
try {
const el = document.querySelector(selector)
return $(el).html()
} catch(err) {
}
} catch (err) {}
}
})
}