-
Notifications
You must be signed in to change notification settings - Fork 77
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Convert Protocol JS components to modern JS formats (Fixes #255)
- Loading branch information
1 parent
e0566d7
commit d4abc06
Showing
68 changed files
with
13,487 additions
and
10,295 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
/** | ||
* This script is used to determine that JS is enabled in the browser, | ||
* and provides `.js` and `.no-js` styling hooks used in component CSS. | ||
* In order to avoid content flashing and repaints on page load, it is | ||
* recommended that this script should be run in the <head>, before | ||
* page CSS is parsed. | ||
*/ | ||
|
||
const doc = document.documentElement; | ||
|
||
// Add class to reflect javascript availability for CSS | ||
doc.className = doc.className.replace(/\bno-js\b/, 'js'); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
const MzpDetails = {}; | ||
let _count = 0; | ||
|
||
MzpDetails.isSupported = () => { | ||
if (typeof MzpSupports !== 'undefined' && typeof MzpUtils !== 'undefined') { | ||
return MzpSupports.classList; | ||
} else { | ||
return false; | ||
} | ||
}; | ||
|
||
/** | ||
* open | ||
* @param {String} id - id of the container to open | ||
* @param {Object} options - configurable options | ||
*/ | ||
MzpDetails.open = (id, options) => { | ||
const control = document.querySelector(`[aria-controls=${id}]`); | ||
const details = document.getElementById(id); | ||
control.setAttribute('aria-expanded', true); | ||
details.setAttribute('aria-hidden', false); | ||
details.classList.remove('is-closed'); | ||
if (typeof options.onDetailsOpen === 'function') { | ||
options.onDetailsOpen(details); | ||
} | ||
}; | ||
|
||
/** | ||
* close | ||
* @param {String} id - id of the container to close | ||
* @param {Object} options - configurable options | ||
*/ | ||
MzpDetails.close = (id, options) => { | ||
const control = document.querySelector(`[aria-controls=${id}]`); | ||
const details = document.getElementById(id); | ||
control.setAttribute('aria-expanded', false); | ||
details.setAttribute('aria-hidden', true); | ||
details.classList.add('is-closed'); | ||
if (typeof options.onDetailsClose === 'function') { | ||
options.onDetailsClose(details); | ||
} | ||
}; | ||
|
||
/** | ||
* toggle | ||
* @param {String} id - id of the container to toggle | ||
* @param {Object} options - configurable options | ||
*/ | ||
MzpDetails.toggle = (id, options) => { | ||
const details = document.getElementById(id); | ||
const isClosed = details.getAttribute('aria-hidden'); | ||
|
||
if (isClosed === 'true') { | ||
MzpDetails.open(id, options); | ||
} else { | ||
MzpDetails.close(id, options); | ||
} | ||
}; | ||
|
||
/** | ||
* handleControlActivation | ||
* @param {Event} e - event to handle | ||
* @param {Object} options - configurable options | ||
*/ | ||
MzpDetails.handleControlActivation = (e, options) => { | ||
const control = e.target; | ||
const id = control.getAttribute('aria-controls'); | ||
MzpDetails.toggle(id, options); | ||
}; | ||
|
||
/** | ||
* initItem | ||
* @param {Object} el - Element to place the control inside of | ||
* @param {String} selector - Selector for all control wrappers | ||
* - assumes every sibling until the next control is associated with the control | ||
* @param {Object} options - configurable options | ||
*/ | ||
MzpDetails.initItem = (el, selector, options) => { | ||
const summary = el; | ||
const control = document.createElement('button'); | ||
let details; | ||
const parent = summary.parentNode; | ||
|
||
// if it's already been initialized, don't do it again | ||
if (summary.querySelectorAll('button').length !== 0) { | ||
return; | ||
} | ||
|
||
// Expand | ||
// siblings of the summary, until next summary | ||
const summarySiblings = MzpUtils.nextUntil(summary, selector); | ||
|
||
// look to see if all children are already in a wrapper we can use | ||
if (summarySiblings.length === 1) { | ||
details = summarySiblings[0]; | ||
} else if (summarySiblings.length > 1){ | ||
details = document.createElement('div'); | ||
summarySiblings.forEach(function(sibling) { | ||
details.appendChild(sibling); | ||
}); | ||
summary.parentNode.insertBefore(details, summary.nextSibling); | ||
} else { | ||
// no children were found, something is probably wrong, let's stop here | ||
return; | ||
} | ||
|
||
// add class to parent to indicate js initialized | ||
parent.classList.add('is-details'); | ||
|
||
// add class to content wrapper | ||
details.classList.add('mzp-js-details-wrapper'); | ||
|
||
// look for existing ID to use | ||
if(!details.id) { | ||
// if details already has ID, use that, if not assign one using the selector minus all not-letters | ||
const unique = selector.replace(/[^a-zA-Z]+/g, ''); | ||
details.id = 'expand-' + unique + '-'+ _count; | ||
_count += 1; | ||
} | ||
|
||
// close by default | ||
// TODO: add support for open attribute | ||
details.setAttribute('aria-hidden', true); | ||
details.classList.add('is-closed'); | ||
|
||
// Control | ||
control.setAttribute('type', 'button'); | ||
// add aria-controls | ||
control.setAttribute('aria-controls', details.id); | ||
// add aria-expanded | ||
control.setAttribute('aria-expanded', false); | ||
// add listener | ||
control.addEventListener('click', function(e) { | ||
MzpDetails.handleControlActivation(e, options); | ||
}, false); | ||
// copy the summary's contents into the control | ||
const summaryChildren = Array.prototype.slice.call(summary.childNodes); | ||
summaryChildren.forEach(function(child) { | ||
control.appendChild(child); | ||
}); | ||
// append control element | ||
summary.appendChild(control); | ||
summary.classList.add('is-summary'); | ||
}; | ||
|
||
/** | ||
* destroyItem | ||
* @param {Object} el - Element the control was placed inside of | ||
* - does not attempt to remove the details wrapper | ||
*/ | ||
MzpDetails.destroyItem = (el) => { | ||
const summary = el; | ||
const parent = summary.parentNode; | ||
const details = summary.nextElementSibling; | ||
const control = summary.querySelector('button'); | ||
|
||
// if it's already been destroyed, don't do it again | ||
if (summary.querySelectorAll('button').length === 0) { | ||
return; | ||
} | ||
|
||
parent.classList.remove('is-details'); | ||
details.removeAttribute('aria-hidden'); | ||
details.classList.remove('is-closed'); | ||
// move control's contents back to summary | ||
const controlChildren = Array.prototype.slice.call(control.childNodes); | ||
controlChildren.forEach(function(child) { | ||
summary.appendChild(child); | ||
}); | ||
summary.removeChild(control); | ||
summary.classList.remove('is-summary'); | ||
}; | ||
|
||
/** | ||
* Init | ||
* @param {Object} selector - CSS selector matching "summary" elements | ||
* @param {Object} options - configurable options | ||
- passed in to the init function and passed around from there | ||
example: | ||
var testOptions = { | ||
onDetailsOpen : myDetailsOpenCallback(), | ||
onDetailsClose : function(){ //anonymous callback } | ||
}; | ||
*/ | ||
MzpDetails.init = (selector, options) => { | ||
if (!MzpDetails.isSupported()) { | ||
return; | ||
} | ||
if (typeof options === 'undefined') { | ||
options = {}; | ||
} | ||
|
||
const summaries = document.querySelectorAll(selector); | ||
// loop through controls on the page and init them one at a time | ||
for (let i = 0; i < summaries.length; i++) { | ||
MzpDetails.initItem(summaries[i], selector, options); | ||
} | ||
}; | ||
|
||
/** | ||
* Destroy | ||
* @param {Object} selector - CSS selector matching "summary" elements | ||
* @param {Object} options - configurable options | ||
*/ | ||
MzpDetails.destroy = (selector, options) => { | ||
const summaries = document.querySelectorAll(selector, options); | ||
// loop through controls on the page and destroy them one at a time | ||
for (let i = 0; i < summaries.length; i++) { | ||
MzpDetails.destroyItem(summaries[i], selector, options); | ||
} | ||
}; | ||
|
||
// check if details is supported, if not, init this as a polyfill | ||
if (typeof MzpSupports !== 'undefined') { | ||
// not supported, add support | ||
if(!MzpSupports.details) { | ||
MzpDetails.init('summary'); | ||
} | ||
} | ||
|
||
// init generic class indicating headings should be made into open/close component | ||
MzpDetails.init('.mzp-c-details > h2'); | ||
MzpDetails.init('.mzp-c-details > h3'); | ||
MzpDetails.init('.mzp-c-details > h4'); | ||
MzpDetails.init('.mzp-c-details > h5'); | ||
MzpDetails.init('.mzp-c-details > h6'); | ||
|
||
module.exports = MzpDetails; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
const footerHeadings = '.mzp-c-footer-sections .mzp-c-footer-heading'; | ||
|
||
// removes Details component if screen size is big | ||
function screenChange(mq) { | ||
if (mq.matches) { | ||
MzpDetails.init(footerHeadings); | ||
} else { | ||
MzpDetails.destroy(footerHeadings); | ||
} | ||
} | ||
|
||
// check we have global Supports and Details library | ||
if (typeof MzpSupports !== 'undefined' && typeof MzpDetails !== 'undefined') { | ||
|
||
// check browser supports matchMedia | ||
if (MzpSupports.matchMedia) { | ||
const _mqWide = matchMedia('(max-width: 479px)'); | ||
|
||
// initialize details if screen is small | ||
if (_mqWide.matches) { | ||
MzpDetails.init(footerHeadings); | ||
} | ||
|
||
if (window.matchMedia('all').addEventListener) { | ||
_mqWide.addEventListener('change', screenChange, false); | ||
} else if (window.matchMedia('all').addListener) { | ||
_mqWide.addListener(screenChange); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
/* This Source Code Form is subject to the terms of the Mozilla Public | ||
* License, v. 2.0. If a copy of the MPL was not distributed with this | ||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */ | ||
|
||
const MzpLangSwitcher = {}; | ||
|
||
/** | ||
* Returns URL pathname with preceded by a new page locale. | ||
* Assumes first path immediately after hostname is the page locale. | ||
* @param {Object} Location interface | ||
* @param {String} Newly selected language code e.g. `de` | ||
* @return {String} pathname e.g. `/de/firefox/` | ||
*/ | ||
MzpLangSwitcher.switchPath = (location, newLang) => { | ||
const parts = location.pathname.slice(1).split('/'); | ||
const currentLang = '/' + parts[0] + '/'; | ||
|
||
// check that first path is a valid lang code. | ||
if (!/^(\/\w{2}-\w{2}\/|\/\w{2,3}\/)/.test(currentLang)) { | ||
return false; | ||
} | ||
|
||
const urlpath = parts.slice(1).join('/'); | ||
return '/' + newLang + '/' + urlpath + location.search; | ||
}; | ||
|
||
/** | ||
* Redirect page to destination URL if valid | ||
* @param {String} destination | ||
*/ | ||
MzpLangSwitcher.doRedirect = (destination) => { | ||
if (destination) { | ||
window.location.href = destination; | ||
} | ||
}; | ||
|
||
/** | ||
* Initialize footer lang switcher. | ||
* @param {function} Custom callback for analytics. | ||
*/ | ||
MzpLangSwitcher.init = (callback) => { | ||
const language = document.querySelectorAll('.mzp-js-language-switcher-select'); | ||
|
||
for (let i = 0; i < language.length; i++) { | ||
language[i].setAttribute('data-previous-language', language[i].value); | ||
|
||
language[i].addEventListener('change', function(e) { | ||
const newLanguage = e.target.value; | ||
const previousLanguage = e.target.getAttribute('data-previous-language'); | ||
|
||
// support custom callback for page analytics. | ||
if (typeof callback === 'function') { | ||
callback(previousLanguage, newLanguage); | ||
} | ||
|
||
MzpLangSwitcher.doRedirect(MzpLangSwitcher.switchPath(window.location, newLanguage)); | ||
}, false); | ||
} | ||
}; | ||
|
||
module.exports = MzpLangSwitcher; |
Oops, something went wrong.