diff --git a/_config.yml b/_config.yml index a75f55c80..9ee37d810 100644 --- a/_config.yml +++ b/_config.yml @@ -20,7 +20,6 @@ defaults: layout: license exclude: - - app.coffee - CNAME - CONTRIBUTING.md - Gemfile @@ -40,7 +39,6 @@ plugins: - jekyll-sitemap - jekyll-redirect-from - jekyll-seo-tag - - jekyll-coffeescript - jekyll-github-metadata # For 'Improve this page' links sass: diff --git a/assets/js/app.coffee b/assets/js/app.coffee deleted file mode 100644 index a9212c1fc..000000000 --- a/assets/js/app.coffee +++ /dev/null @@ -1,155 +0,0 @@ ---- ---- - -class Choosealicense - # Selects the content of a given element - selectText: (element) -> - if document.body.createTextRange - range = document.body.createTextRange() - range.moveToElementText(element) - range.select() - else if window.getSelection - selection = window.getSelection() - range = document.createRange() - - range.selectNodeContents(element) - selection.removeAllRanges() - selection.addRange(range) - - tooltipAttributesMapperByRuleType: - permissions: - heading: 'Permission' - color: 'tooltip--permissions' - conditions: - heading: 'Condition' - color: 'tooltip--conditions' - limitations: - heading: 'Limitation' - color: 'tooltip--limitations' - - - # fire on document.ready - constructor: -> - @initTooltips() - @initClipboard() - @initLicenseSuggestion() - - # Init tooltip action - initTooltips: -> - - # Dynamically add annotations as title attribute to rule list items - for ruletype, rules of window.annotations - for rule in rules - # Exclude license elements in the legend - licenseLiElement = $(".license-#{ruletype} .#{rule["tag"]}").not("dd.license-#{ruletype} .#{rule["tag"]}") - tooltipAttr = @tooltipAttributesMapperByRuleType[ruletype] - licenseLiElement.attr "aria-label", "#{rule.label} #{tooltipAttr.heading.toLowerCase()}: #{rule.description}" - licenseLiElement.addClass("hint--bottom - hint--large - hint--no-animate - #{tooltipAttr.color} - override-hint-inline") - - # Initializes Clipboard.js - initClipboard: -> - # Backup the clipboard button's original text. - $(".js-clipboard-button").data "clipboard-prompt", $(".js-clipboard-button").text() - - # Hook up copy to clipboard buttons - clip = new Clipboard ".js-clipboard-button" - clip.on "mouseout", @clipboardMouseout - clip.on "complete", @clipboardComplete - - # Callback to restore the clipboard button's original text - clipboardMouseout: (client, args) -> - @textContent = $(this).data("clipboard-prompt") - - # Post-copy user feedback callback - clipboardComplete: (client, args) -> - @textContent = "Copied!" - - # Initializes the repository suggestion feature - initLicenseSuggestion: -> - inputEl = $("#repository-url") - licenseId = inputEl.attr("data-license-id") - statusIndicator = $(".status-indicator") - new LicenseSuggestion(inputEl, licenseId, statusIndicator) - -class LicenseSuggestion - constructor: (@inputEl, @licenseId, @statusIndicator) -> - @bindEventHandlers() - - inputWraper: $('.input-wrapper') - tooltipErrorClasses: 'hint--bottom tooltip--error hint--always' - - # Main event handlers for user input - bindEventHandlers: => - @inputEl.on "input", (event) => - @setStatus "" - .on "keyup", (event) => - if event.keyCode == 13 and event.target.value - # Validate the user input first - try - repositoryFullName = @parseUserInput event.target.value - catch - @setStatus "Error", "Invalid URL." - return - - @setStatus "Fetching" - @fetchInfoFromGithubAPI repositoryFullName, (err, repositoryInfo=null) => - if (err) - @setStatus "Error", err.message - return - if repositoryInfo.license # The repository already has a license - license = repositoryInfo.license - @setStatus "Error", @repositoryLicense repositoryFullName, license - else # The repository is not licensed - licenseUrl = encodeURIComponent "https://github.com/#{repositoryFullName}/community/license/new?template=#{@licenseId}" - # Provide the chance to the user log-in, since the URL to suggest a license is restricted - window.location.href = "https://github.com/login?return_to=#{licenseUrl}" - @setStatus "" - @inputEl.val("") - - # Try to extract the repository full name from the user input - parseUserInput: (userInput) -> - repository = /https?:\/\/github\.com\/([^\/]+)\/([^\/\?#]+)/.exec userInput - [_, username, project] = repository - return username + '/' + project.replace /(\.git)$/, '' - - # Displays an indicator and tooltips to the user about the current status - setStatus: (status="", message="") => - statusClass = status.toLowerCase() - displayTooltip = (status, message) => - @inputWraper.attr('aria-label', "#{status}: #{message}") - @inputWraper.addClass(@tooltipErrorClasses) - - switch status - when "Fetching" - @statusIndicator.removeClass("error #{@tooltipErrorClasses}").addClass(statusClass) - when "Error" - @statusIndicator.removeClass('fetching').addClass(statusClass) - displayTooltip status, message - else - @statusIndicator.removeClass('fetching error') - @inputWraper.removeClass(@tooltipErrorClasses) - - # Fetches information about a repository from the Github API - fetchInfoFromGithubAPI: (repositoryFullName, callback) -> - $.getJSON "https://api.github.com/repos/"+repositoryFullName, (info) -> - callback null, info - .fail (e) -> - if e.status == 404 - callback new Error "Repository #{repositoryFullName} not found." - else - callback new Error "Network error when trying to get information about #{repositoryFullName}." - - # Generates a message showing that a repository is already licensed - repositoryLicense: (repositoryFullName, license) -> - foundLicense = window.licenses.find (lic) -> lic.spdx_id == license.spdx_id - if foundLicense # Links the license to its page on this site - "The repository #{repositoryFullName} is already licensed under the #{foundLicense.title}." - else - "The repository #{repositoryFullName} is already licensed." - -$ -> - new Choosealicense() diff --git a/assets/js/app.js b/assets/js/app.js new file mode 100644 index 000000000..c58cf6734 --- /dev/null +++ b/assets/js/app.js @@ -0,0 +1,204 @@ +class Choosealicense { + constructor() { + this.tooltipAttributesMapperByRuleType = { + permissions: { + heading: 'Permission', + color: 'tooltip--permissions' + }, + conditions: { + heading: 'Condition', + color: 'tooltip--conditions' + }, + limitations: { + heading: 'Limitation', + color: 'tooltip--limitations' + } + }; + + this.initTooltips(); + this.initClipboard(); + this.initLicenseSuggestion(); + } + + // Selects the content of a given element + selectText(element) { + if (document.body.createTextRange) { + const range = document.body.createTextRange(); + range.moveToElementText(element); + range.select(); + } else if (window.getSelection) { + const selection = window.getSelection(); + const range = document.createRange(); + + range.selectNodeContents(element); + selection.removeAllRanges(); + selection.addRange(range); + } + } + + // Init tooltip action + initTooltips() { + const annotations = window.annotations || {}; + + Object.entries(annotations).forEach(([ruletype, rules]) => { + rules.forEach((rule) => { + const licenseLiElement = $(`.license-${ruletype} .${rule.tag}`).not( + `dd.license-${ruletype} .${rule.tag}` + ); + const tooltipAttr = this.tooltipAttributesMapperByRuleType[ruletype]; + if (!tooltipAttr) return; + + licenseLiElement.attr( + 'aria-label', + `${rule.label} ${tooltipAttr.heading.toLowerCase()}: ${rule.description}` + ); + licenseLiElement.addClass( + `hint--bottom + hint--large + hint--no-animate + ${tooltipAttr.color} + override-hint-inline` + ); + }); + }); + } + + // Initializes Clipboard.js + initClipboard() { + const clipboardPrompt = $('.js-clipboard-button').text(); + $('.js-clipboard-button').data('clipboard-prompt', clipboardPrompt); + + const clip = new Clipboard('.js-clipboard-button'); + clip.on('mouseout', this.clipboardMouseout); + clip.on('complete', this.clipboardComplete); + } + + // Callback to restore the clipboard button's original text + clipboardMouseout(client, args) { + this.textContent = $(this).data('clipboard-prompt'); + } + + // Post-copy user feedback callback + clipboardComplete(client, args) { + this.textContent = 'Copied!'; + } + + // Initializes the repository suggestion feature + initLicenseSuggestion() { + const inputEl = $('#repository-url'); + const licenseId = inputEl.attr('data-license-id'); + const statusIndicator = $('.status-indicator'); + new LicenseSuggestion(inputEl, licenseId, statusIndicator); + } +} + +class LicenseSuggestion { + constructor(inputEl, licenseId, statusIndicator) { + this.inputEl = inputEl; + this.licenseId = licenseId; + this.statusIndicator = statusIndicator; + this.inputWrapper = $('.input-wrapper'); + this.tooltipErrorClasses = 'hint--bottom tooltip--error hint--always'; + + this.bindEventHandlers(); + } + + // Main event handlers for user input + bindEventHandlers() { + this.inputEl + .on('input', () => { + this.setStatus(''); + }) + .on('keyup', (event) => { + if (event.keyCode === 13 && event.target.value) { + let repositoryFullName; + try { + repositoryFullName = this.parseUserInput(event.target.value); + } catch (error) { + this.setStatus('Error', 'Invalid URL.'); + return; + } + + this.setStatus('Fetching'); + this.fetchInfoFromGithubAPI( + repositoryFullName, + (err, repositoryInfo = null) => { + if (err) { + this.setStatus('Error', err.message); + return; + } + if (repositoryInfo.license) { + const license = repositoryInfo.license; + this.setStatus('Error', this.repositoryLicense(repositoryFullName, license)); + } else { + const licenseUrl = encodeURIComponent( + `https://github.com/${repositoryFullName}/community/license/new?template=${this.licenseId}` + ); + window.location.href = `https://github.com/login?return_to=${licenseUrl}`; + this.setStatus(''); + this.inputEl.val(''); + } + } + ); + } + }); + } + + // Try to extract the repository full name from the user input + parseUserInput(userInput) { + const repository = /https?:\/\/github\.com\/([^\/]+)\/([^\/?#]+)/.exec(userInput); + if (!repository) throw new Error('Invalid URL.'); + + const [, username, project] = repository; + return `${username}/${project.replace(/(\.git)$/, '')}`; + } + + // Displays an indicator and tooltips to the user about the current status + setStatus(status = '', message = '') { + const statusClass = status.toLowerCase(); + const displayTooltip = (s, m) => { + this.inputWrapper.attr('aria-label', `${s}: ${m}`); + this.inputWrapper.addClass(this.tooltipErrorClasses); + }; + + switch (status) { + case 'Fetching': + this.statusIndicator.removeClass(`error ${this.tooltipErrorClasses}`).addClass(statusClass); + break; + case 'Error': + this.statusIndicator.removeClass('fetching').addClass(statusClass); + displayTooltip(status, message); + break; + default: + this.statusIndicator.removeClass('fetching error'); + this.inputWrapper.removeClass(this.tooltipErrorClasses); + break; + } + } + + // Fetches information about a repository from the Github API + fetchInfoFromGithubAPI(repositoryFullName, callback) { + $.getJSON(`https://api.github.com/repos/${repositoryFullName}`, (info) => { + callback(null, info); + }).fail((e) => { + if (e.status === 404) { + callback(new Error(`Repository ${repositoryFullName} not found.`)); + } else { + callback(new Error(`Network error when trying to get information about ${repositoryFullName}.`)); + } + }); + } + + // Generates a message showing that a repository is already licensed + repositoryLicense(repositoryFullName, license) { + const foundLicense = window.licenses.find((lic) => lic.spdx_id === license.spdx_id); + if (foundLicense) { + return `The repository ${repositoryFullName} is already licensed under the ${foundLicense.title}.`; + } + return `The repository ${repositoryFullName} is already licensed.`; + } +} + +$(() => { + new Choosealicense(); +});