@lawrencesim/web-common
v5.0.4
Published
Web Common is a collection of polyfills, extensions, and modules I repeatedly found myself reapplying on new projects.
Downloads
17
Readme
Web Common
Web Common is a collection of polyfills, extensions, and modules I repeatedly found myself reapplying on new projects.
Lawrence Sim © 2024
This library is licensed under the MIT License. See LICENSE file for full text.
Content
- Usage
- Polyfills
- Global Browser Variable
- Prototype Modifications
- Date (UTC) Modifications
- Common
- Common UI
- CommonTable
- Acknowledgments
Usage
Installation is best handled via NPM:
npm install @lawrencesim/web-common
Libraries can be imported with ES6 syntax as follows:
import common from '@lawrencesim/web-common';
import '@lawrencesim/web-common/style.css';
import CommonTable from '@lawrencesim/web-common/CommonTable';
The first import will bring in the common module, including polyfills/extensions, and the UI submodule. If using the UI submodule or CommonTable module, you will also need to load the styles (second line), but otherwise, this can be left out. Note that depending on build configuration (e.g. Webpack) you may need proper style handlers to load the CSS styles. The final line loads the optional CommonTable class.
If using script imports in HTML, import the paths to common.js, style.css, and/or CommonTable.js as needed in the main directory. The main module will be added as common
and CommonTable as CommonTable
to the global namespace.
Version 5 breaking changes
- Number.prototype.addCommasSmart() is removed. Use Number.prototype.stringFormat() instead.
- String.prototype.heuristicCompare() is removed. Use String.prototype.semanticCompare() instead.
- common.extend(), parameters are renamed
overwrite
,deep
, andmodify
fromallowOverwrite
,deepCopy
, andmodifyObj
. While detection is still left in for older names for backwards compatibility, it may be deprecated at some point. - common.newWindow() no longer accepts flat parameters. All parameters except for
url
(and optoinallyname
) must be specified in an options object. - common.animate(), parameters are renamed
duration
andtiming
fromdurationMs
, andtimingFunction
. While detection is still left in for older names for backwards compatibility, it may be deprecated at some point.
Polyfills
Ensures the below functions exists, many of which are missing in Internet Explorer (pre-Edge) and Opera Mini.
Note that this is just a personal list of functions I tended to require (combined with a history of having to work with gov't clients that were still stuck in IE land). These days, probably less necessary, and if so, better to use a more complete polyfill library like core-js.
# Array.from(arrayLike[, mapFn[, thisArg]])
Create array from array-like or iterable.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from
# Array.prototype.find(callback[, thisArg])
Find item in an array.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
# Array.prototype.findIndex(callback[, thisArg])
Find index of an item in an array.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
# Array.prototype.findLast(callback[, thisArg])
Find item in an array, searching in reverse.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLast
# Array.prototype.findLastIndex(callback[, thisArg])
Find index of an item in an array, searching in reverse.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findLastIndex
# Array.prototype.flat([depth])
Flatten an array to a desired depth (or default single-depth).
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat
# Array.prototype.includes(searchElement[, fromIndex])
Find if an item exists in an array.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
# Element.prototype.remove()
Remove element.
See https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove
# Element.prototype.append(nodes)
Append to element.
See https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/append
# Element.prototype.prepend(nodes)
Prepend to element.
See https://developer.mozilla.org/en-US/docs/Web/API/ParentNode/prepend
# Element.prototype.matches(selectors)
Check if element matches selector.
See https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
# Element.prototype.closest(selectors)
Find closest element matching selector.
See https://developer.mozilla.org/en-US/docs/Web/API/Element/closest
# Element.classList
Ensures existence of contains()
, add()
, remove()
, toggle()
, and replace()
functions in element's classList
property.
See https://developer.mozilla.org/en-US/docs/Web/API/Element/classList
Note that IE and Edge cannot support classList
on SVG elements (no polyfill available).
# NodeList.prototype.forEach(callback[, thisArg])
Functionally iterate through a NodeList
.
See https://developer.mozilla.org/en-US/docs/Web/API/NodeList/forEach
# String.prototype.startsWith(searchString[, position])
Check string starts with sequence.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
# String.prototype.endsWith(searchString[, length])
Check string ends with sequence.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
# String.prototype.repeat(count)
Repeat string content.
See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
Promises
Internally, taylorhakes/promise-polyfill is called, if necessary, to polyfill for Promises. However, it is only used locally and not added to the global namespace. As it's very lightweight (and I don't want to simply wrap/repackage Taylor's work), I recommend installing his library directly to your projects if you need a polyfill for Promises. Thus, if you think you will need to polyfill for Promises, bring in this library to your dependencies or else a error will occur when using common.ajax() or common.animate() in a browser without support for Promises.
Global Browser Variable
Two variables are added to the window
namespace (if it exists) that stores browser information.
| Param | Description |
| --- | :--- |
| browser
| Stores information on browser type and version. |
| browserType
| Alias for browser
, left for backwards compatibility. |
Note there are two formats in which data exists as browser information. One is a simple string parse of the UserAgent and version as key name and value. For certain user agents this may return multiple results. However there may exist a secondary is-
variable, which is heuristically determined, that will give the specific browser it is most likely determined to be.
E.g. for Opera browsers, with an example user agent of "Mozilla/5.0 (Windows NT 10.0; WOW64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/88.0.4324.150 Safari/537.36 OPR/74.0.3911.75"
, the browser
object will show three separate browser versions and an isOpera
variable:
{
chrome: 88,
safari: 537,
opera: 74,
isOpera: true
}
Similarly, the variable might be isChrome
or isFirefox
or isEdge
, as the case dictates.
Currently, this checks for the following known browsers: isChrome
, isFirefox
, isEdge
, isIE
, isSafari
, isOpera
, isBrave
, isSamsungInternet
, isUCBrowser
, isYandex
, is360SecureBrowser
, isQQBrowser
, isInstabridge
, isVivaldi
, isCocCoc
(Cốc Cốc), isWhale
(Navar Whale), isPuffin
, isSleipnir
, isAmazonSilk
, and isQtWebEngine
.
For browsers on iOS – which Apple forces to be basically skins of Safari Mobile – these may not always be identified correctly as Safari through the user agent name. For what it's worth, Chrome, Firefox, and Edge flavors will have versions under crios
, fiox
, and edgios
while correctly identifying it as a Safari Browser.
Note that this method of parsing the UserAgent string is somewhat brittle and can be unreliable, especially for those lesser-seen browsers or those specific to devices (like tablets, smart TVs, or gaming consoles). Where browser detection critical, it is generally preferred to use feature detection instead.
Prototype Modifications
These useful functions are added to common object prototypes.
#
Array.getOverlaps(a, b) ⇒ Array
#
Array.prototype.getOverlaps(arr) ⇒ Array
Get overlapping values with second array. Can be called from array instance or Array
global. Uses strict equality.
#
Array.overlaps(a, b) ⇒ boolean
#
Array.prototype.overlaps(arr) ⇒ boolean
Check if at least one value overlaps with second array. Can be called from array instance or Array
global. Uses strict equality.
#
Array.prototype.remove(value[, index[, limit]]) ⇒ Array
Remove all instances of a value from an array. Value matching uses strict equality. Creates a copy of the array without modifying the original array.
Set index
to define the index at which to start indexing. Negatives are allowed to find a position from reverse. If the index is greater than or equal to the length of the array, the array is not searched and nothing is removed.
Set limit
to a positive value to define a limit to the number of times the value will be removed. Otherwise, the removal allowance is unlimited.
#
Element.prototype.isVisible() ⇒ boolean
Check is element is visible. Uses getBoundingClientRect
method, which is more reliable than the old offsetParent
trick.
# Element.prototype.setAttributes(attrs)
Sets multiple attributes (given as a dictionary-like object of key-value pairs) at once.
# Element.prototype.css(style[, value])
Much like the JQuery css() function, sets inline style, either as style name and value provided as strings, or as a dictionary-like object of style names and values and key-value pairs.
# Element.prototype.center()
Will center an element on screen using absolute positioning.
#
Number.prototype.addCommas(precision) ⇒ string
Will convert a given number to a string, using the supplied precision, with commas.
#
Number.prototype.stringFormat([minimum=0.001, [zeroFormat="0.0"]) ⇒ string
Basically wraps Number.prototype.addCommas() with heuristic guessing on precision to use. The minimum
parameter rounds any value whose absolute value is less than this to zero. The zeroFormat
parameter can be used to customize how zero values are printed. By default it is "0.0".
Current heuristics are:
- Evaluation to zero is always written in the zero format (default "0.0")
- <0.01 as scientific notation with three significant figures
- <0.1 as scientific notation with two significant figures
- <0.3 as number with three decimal places
- <1.0 as number with two decimal places
- <100 as number with one decimal place
- ≥100 as number with no decimal places
#
Object.isObject(obj) ⇒ boolean
Check is given object is an object-type. That is, not a primitive, string, or array. This includes any inheritance of the object prototype, except for arrays.
Uses typeof
check with extra handling to invalidate array types.
#
Object.isObjectLiteral(obj) ⇒ boolean
Check is given object is an object literal-type. That is, not a primitive, string, array, or even any inheritance of the Object prototype. Must be a base object created either as an object literal or via new Object()
. Useful for when parameters must be ensured as an object-literal/dictionary.
Uses Object.getPrototypeOf() check.
#
String.prototype.capitalize([breaks]) ⇒ string
Will capitalize the each word in the string (using whitespace to delineate words).
Additional break characters can be provided as either an array of characters or a string of all characters in the optional parameter breaks
. E.g., to include hyphens, "up-to-date".capitalize("-")
will output Up-To-Date
.
#
String.prototype.semanticCompare(compareString[, options]) ⇒ number
A semantic comparison of strings with numeric values within them. Compare the numbers in a string such that a "number" is not compared alphabetically by character but as the entire numeric value.
E.g., a typical string comparisons would result in '20' coming before '5', because string comparisons evaluate character by character, first looking at the '2' and '5' characters. This function ensures the entire '20' is considered as one number.
Returns numeric indicating whether this
string comes before (-1), after (1), or is equal (0) to compared string. As such, can be inserted into most sort functions such as Array.prototype.sort() within the compare function.
"x01x02".semanticCompare("x1x2"); // 0 is semantically equal
"x20".semanticCompare("x1"); // 1 comes after
"x9".semanticCompare("x999"); // -1 comes before
"b1".semanticCompare("a2"); // 1 comes after
Each string is broken into chunks of parsable numeric and non-numeric chunks. Each chunk is compared in similar sequences. When both compared chunks are parsable numbers, they will be compared numerically. If either is not, they will be compared as strings. E.g. "a10bc40" and "a10b50c" would be broken up into ['a', '10', 'bc', '40']
and ['a', '10', 'b', '50', 'c']
respectively. The crux of the comparison would happen at the chunks "bc" vs "b" (wherein "b" comes before "bc"), and the comparison of chunks "40" and "50" would be irrelevant.
"a10bc40".semanticCompare("a10b50c"); // 1
By default, negative numbers and decimals are not handled as dashes and periods may not be considered part of the number, depending on the string. This can be switched by setting as truthy either/both the optional parameters options.handleNegative
and/or options.handleDeciaml
. If enabling decimals in particular, ensure numbers are properly formatted. E.g. a value of "3.2.1" would result in a numeric parsing two separate values of "3.2" and "0.1".
"x-2".semanticCompare("x-1"); // 1
"x-2".semanticCompare("x-1", {handleNegative: true}); // -1
Date (UTC) Modifications
Additional functions for handling basic Date objects are added. Specifically to ensure UTC handling.
#
DateUTC(year, month, day[, hour[, min[, sec]]]) ⇒ Date
Creates a datetime, forced as UTC. Month is to be indicated as number from 1-12 (unlike traditional Date constructor as 0-11).
#
Date.prototype.asUTC() ⇒ Date
Converts datetime to UTC assuming time given (assumed localtime) was actually meant as UTC time. That is to say, Does not convert localtime to UTC and simply passes the time values as they exist. The date/time in localtime will be kept as the UTC date/time, only changing the timezone to UTC.
d = new Date(2019, 0, 1, 20); // Tue Jan 01 2019 20:00:00 GMT-0800 (Pacific Standard Time)
d.asUTC(); // Tue Jan 01 2019 12:00:00 GMT-0800 (Pacific Standard Time)
In the above conversion, assumed the date of Jan 1 2019 at 20:00 was meant as UTC and adjust the timezone without converting the time. When printing the date, which in javascript is by default converted to localtime (in this case PST), it 8 hours earlier but corresponds to 20:00 UTC.
#
Date.prototype.toUTC() ⇒ Date
Creates new DateUTC
using the UTC datetime of this object, converted from localtime.
d = new Date(2019, 0, 1, 20); // Tue Jan 01 2019 20:00:00 GMT-0800 (Pacific Standard Time)
d.toUTC(); // Tue Jan 01 2019 20:00:00 GMT-0800 (Pacific Standard Time)
As printing is always done in localtime, conversion is mostly symbolic. This function only left in for completeness, but really doesn't do anything.
#
Date.prototype.asUTCDate() ⇒ Date
Converts date by dropping any time information and assuming 12:00 AM UTC. Does not convert localtime to UTC and simply passes the time values as they exist.
d = new Date(2019, 0, 1, 20); // Tue Jan 01 2019 20:00:00 GMT-0800 (Pacific Standard Time)
d.asUTCDate(); // Mon Dec 31 2018 16:00:00 GMT-0800 (Pacific Standard Time)
In the above conversion, converts Jan 1, 2019 (date-only) in UTC time (though constructed with localtime). Drops time information, making it Jan 1, 2019 at 00:00 UTC. Printed, which is in localtime by default in javascript, it shows as 16:00 PST the previous day.
#
Date.prototype.toUTCDate() ⇒ Date
Converts date by first converting the time to UTC, then dropping any time information and assuming 12:00 AM UTC.
d = new Date(2019, 0, 1, 20); // Tue Jan 01 2019 20:00:00 GMT-0800 (Pacific Standard Time)
d.toUTCDate(); // Tue Jan 01 2019 16:00:00 GMT-0800 (Pacific Standard Time)
In the above conversion, first converts Jan 1, 2019 at 20:00 in PST to 04:00 UTC the following day. Then it drops time information, making it Jan 2, 2019 at 00:00 UTC. Printed, which is in localtime by default in javascript, it shows as 16:00 PST the previous day (which is still Jan 1).
#
Date.prototype.addDays(days) ⇒ Date
Returns new date with days added (or removed if negative).
#
Date.prototype.monthOfYear() ⇒ number
Returns the month of the year as 1-12 number (as opposed to 0-11 for getMonth()).
#
Date.prototype.daysInMonth() ⇒ number
Returns number of days in the month for this date.
Common Object
Returned as object if instantiated via CommonJS or AMD import. Otherwise appended to root as common (e.g. window.common
).
#
common.getElement(element) ⇒ Element
Given an input, returns an Element (or object derived from the Element prototype) as best determined from what is provided.
| Param | Type | Description |
| :--- | :---: | :--- |
| element
| -- | Input to filter for and/or convert to Element. |
Returns:
- If a single Element is provided, simply returns it.
- If a jQuery object is provided, returns first Element given by calling get() on it.
- If an array is provided, returns the first item this is an Element or undefined.
- If a NodeList or other iterable is provided, returns value of next() or null if done.
- If string is provided, returns result of document.querySelector() using the string as the selector.
- If none of the above apply, returns undefined.
#
common.getElementList(input) ⇒ Element[]
Given an input, converts it into an array of Elements (or objects derived from the Element prototype).
| Param | Type | Description |
| :--- | :---: | :--- |
| input
| -- | Input to filter for and/or convert to an array of Elements. |
Returns:
- If a NodeList, array, or other iterable is provided, converts to an array via Array.from(), then filters for elements that are derived from the Element prototype.
- If a jQuery object is provided, returns array given by calling get() on it.
- If a string is provided, returns result of document.querySelectorAll(), using the string as the selector, converted into an array.
- Otherwise, wraps the input in an array, then filters for elements that are derived from the Element prototype.
#
common.extend(obj, extend[, options]) ⇒ Object
#
common.extend(obj, extend[, overwrite[, deep[, modify]]]) ⇒ Object
Copy given object and extended with new values. The passed objects are not modified in any way unless modify
is set as truthy.
| Param | Type | Description |
| :--- | :---: | :--- |
| obj
| Object | Base object. |
| extend
| Object | Object of extensions to the copy of the base object. |
| options
| Object | Options object, or options may be specified in flat series of parameters. |
| options.overwrite
| Boolean | Unless true, items in extend
matching existing values in obj
by key are not copied over. |
| options.allowOverwrite
| Boolean | Same as above. |
| options.deep
| Boolean | If true, all values are copied via structuredClone()
or, as a fallback, JSON.parse(JSON.stringify())
. |
| options.deepCopy
| Boolean | Same as above. |
| options.modify
| Boolean | If true, the input base object (obj
) is modified directly, instead of cloning. |
| options.modifyObj
| Boolean | Same as above. |
If either extend
or obj
is null or undefined (or evaluates as falsy), a copy of whatever remaining object is returned. Otherwise, values in obj
and extend
are copied to a cloned object by passing the value. Thus primitive types are copied by value, but objects will be copied by reference, unless deepCopy
is true.
In the case that the value being copied from and the value being copied over are both object literals, the copying will be recursed into the next level for each the origin and extending object.
Deep copy is done via structuredClone(), if available, or fallbacks to the JSON.parse(JSON.stringify())
method. Note that the former method may throw a DataCloneError exception and the latter will results in some values (such as dates, functions, or circular references) not being correctly carried over.
Returns: The object with extended values (if modify
is falsy, this is a new object).
#
common.getUrlGetVars() ⇒ Object
Retrieve GET parameters in current URL as an object literal (dictionary format).
Returns: Object literal of GET parameters found in URL.
#
common.newWindow(url[, options]) ⇒ WindowProxy
#
common.newWindow(url, name[, options]) ⇒ WindowProxy
Creates a new, centered window.
| Param | Type | Description |
| :--- | :---: | :--- |
| url
| String | URL for new window or an object literal with all parameters as properties. |
| name
| String | New window name. |
| options
| Object | |
| options.name
| String | New window name may also be specified in the options. |
| options.width
| Number | Width in pixels. If not specified, defaults to 600. |
| options.height
| Number | Height in pixels. If not specified, defaults to 400. |
| options.minimal
| Boolean | Optional. If truthy, forces hiding of menubar, statusbar, and location – although with many modern browsers this has no effect as it is not allowed. |
| options.options
| Object | Optional. Additional window options (passed as windowFeatures
parameter). Specify as key-value pairing. Will overwrite any options set by function or other parameters. |
| options.error
| Callback | Optional. Callback to run when the new window is detected to have been immediately closed (likely due to pop-up blocking). Given the WindowProxy returned by window.open()
. |
Returns: The WindowProxy returned by window.open().
#
common.ajax(params) ⇒ XMLHttpRequest
| Promise
Mimics jQuery.ajax() function call with XMLHttpRequest
.
However, if the project allows, I'd nowadays recommend using the Fetch API instead (if needed, a polyfill is also available as whatwg-fetch
in NPM).
| Param | Type | Default | Description |
| :--- | :---: | :---: | :--- |
| params.url
| String | | The URL of the request. |
| params.async
| Boolean | true
| Asynchronous. Defaults to true. |
| params.method
| String | "GET"
| Method for passing data. |
| params.data
| Object | | Optional dictionary of data to send with request. |
| params.dataType
| String | | Type of returned data given by XMLHttpRequest.responseType
. |
| params.success
| Callback | | Callback on success. Passes parameters of XMLHttpRequest.responseText
, XMLHttpRequest.statusText
, and the XMLHttpRequest
instance itself. |
| params.error
| Callback | | Callback on error. Passes parameters the XMLHttpRequest
instance, XMLHttpRequest.statusText
, and XMLHttpRequest.responseText
. |
| params.complete
| Callback | | Callback on completion (whether success or error). Passes parameters the XMLHttpRequest
instance and XMLHttpRequest.statusText
. |
| params.user
| String | | Optional username, if necessitated. |
| params.password
| String | | Optional password, if necessitated. |
| params.promise
| Boolean | | Optionally return as Promise that resolves when the request resolves. |
Returns: XMLHttpRequest or Promise on completion for the request.
#
common.animate(options) ⇒ Promise
#
common.animate(element, options) ⇒ Promise
#
common.animate(element, properties[, options]) ⇒ Promise
#
common.animate(element, properties, duration[, options]) ⇒ Promise
Mimics jQuery.animate() function using CSS transitions by first applying a transition property for the requisite CSS properties to be applied, then, after a short delay (5 ms), applying the properties. All this is done as modifications to the element's inline styles, and will thus overwrite any existing inline styles and will be subject to any CSS rule overrides (such as an existing, applicable CSS rule with the !imporant
flag).
| Param | Type | Description |
| :--- | :---: | :--- |
| element
| Element | The Element to animate |
| properties
| Object | CSS properties to animate to. Note that not all properties are can be animated. |
| duration
| Number | Duration of animation, in milliseconds. Optional, but if not supplied, the animation is somewhat pointless as the transition is instant. |
| options
| Object | |
| options.element
| Element | The element
parameter may also be specified in the options. |
| options.properties
| Object | The properties
parameter may also be specified in the options. |
| options.duration
| Number | The duration
parameter may also be specified in the options. |
| options.durationMs
| Number | Same as above. |
| options.timing
| String | Timing/easing function, defaults to "ease". See: transition-timing-function. |
| options.timingFunction
| String | Same as above. |
| options.complete
| Callback | Optional callback to run on completion. |
Returns: A Promise tied to the animation duration, if the Promise API is available.
Common UI
Packaged with common
as common.ui
.
The Common UI modules allow for some simple, commonly-used UI functionality, mostly through CSS. As such, common.min.css
is required.
For modal dialog usage, ensure your dependency-manager/import-function is caching requires/imports of the common
object, or that you are passing the object by reference. Calling multiple instances of common.ui
in the same window can result in odd behavior for modal management.
# common.ui.addGrabCursorFunctionality(element)
Adds grab cursor functionality to draggable element. The input may be a single Element, a NodeList or array of Elements, or a jQuery selection.
Adds class "grab" to element, and class "grabbing" when being dragged.
| Param | Type | Description |
| :--- | :---: | :--- |
| element
| -- | Element(s) to add functionality to. See common.getElementList() for evaluation of this parameter. |
# common.ui.createDropdown(element, menu)
Create a dropdown menu on an element. menu parameter is an array of object literals defining the menu. The parameters id
, class
, style
, and html
/text
, if they exist, are applied. For functionality, either add href
and optionally target
parameters or supply a callback to an onClick
parameter. To create a submenu, simply add a menu
parameter with the same nested structure.
Elements with be created with classes prefixed by "cm-dropdown".
| Param | Type | Description |
| :--- | :---: | :--- |
| element
| -- | Element(s) to add dropdown to. See common.getElementList() for evaluation of this parameter. |
| menu
| Object[] | JSON map of menu |
Example usage:
common.ui.createDropdown("#menu",
[
{
id: "menu-btn-1",
text: "Homepage",
href: "index.html",
style: {"font-weight": "bold"},
onClick: () => console.log("menu item 1 clicked")
},
{
id: "submenu",
text: "Totally Work Related",
style: {"font-style": "italic"},
menu: [
{text: "Business Stuff", href: "https://facebook.com"},
{text: "Web Dev. Stuff", href: "https://reddit.com"}
]
}
]
);
# common.ui.clearDropdown(element)
Remove dropdown menu functionality from an element.
| Param | Type | Description |
| :--- | :---: | :--- |
| element
| -- | Element(s) to remove dropdown from. See common.getElementList() for evaluation of this parameter. |
Tooltips & help icons
The tooltips and help icons functionality can be applied via the functions (described below) or manually.
To add a tooltip manually, add the class cm-tooltip-left, cm-tooltip-top, cm-tooltip-right, or cm-tooltip-bottom and the attribute cm-tooltip-msg with the tooltip message. To create a help icon, simply create the element <i>?</i>
, with class cm-icon.
# common.ui.addTooltip(element, options) # common.ui.addTooltip(element, message[, direction[, force]])
Add hover tooltip to element(s).
Elements will be created with classes prefixed by cm-tooltip.
| Param | Type | Description |
| :--- | :---: | :--- |
| element
| -- | Element(s) to add tooltip to. See common.getElementList() for evaluation of this parameter. |
| options
| Object | Options object, or options may be specified in flat series of parameters. |
| options.message
| String | Tooltip message/HTML. |
| options.direction
| String | Direction of tooltip (defaults to top). |
| options.force
| Boolean | If truthy, forces tooltip visible. |
# common.ui.removeTooltip(element)
Remove hover tooltip from element(s).
| Param | Type | Description |
| :--- | :---: | :--- |
| element
| -- | Element(s) to remove tooltip from. See common.getElementList() for evaluation of this parameter. |
# common.ui.appendHelpIcon(element, options) # common.ui.appendHelpIcon(element, message[, direction[, style[, force]]])
Add help icon to element(s) as (?) styled icon with tooltip.
Icon element will be created with class cm-icon.
| Param | Type | Description |
| :--- | :---: | :--- |
| element
| -- | Element(s) to add help icon too. See common.getElementList() for evaluation of this parameter. |
| options
| Object | Options object, or options may be specified in flat series of parameters. |
| options.message
| String | Tooltip message/HTML. |
| options.direction
| String | Direction of tooltip (defaults to top). |
| options.style
| Object | Dictionary of inline style key-values for icon. |
| options.force
| Boolean | If truthy, forces tooltip visible. |
# common.ui.removeHelpIcon(element)
Remove help icon from element(s).
| Param | Type | Description |
| :--- | :---: | :--- |
| element
| -- | Element(s) to remove help icon from. See common.getElementList() for evaluation of this parameter. |
Modal dialogs
For modal dialog usage, ensure your dependency-manager/import-function is caching requires/imports of the common
object, or that you are passing the object by reference. Calling multiple instances of common.ui
in the same window can result in odd behavior for modal management.
common.getElementList()
Model elements will be created with classes prefixed by .cm-modal.
When a modal function is first called, this library appends a hidden div to body
to handle modals/dialogs. This includes a container div (#cm-modal-container), an outer modal div (#cm-modal-outer) with absolute positioning, and an inner div (.cm-modal-inner) which represents the actual dialog. You may (and are in fact recommended to) tweak the CSS rules attached to these as necessary.
Only one modal may be open at a time. Opening another modal will replace the current one.
#
common.ui.isModalOpen() ⇒ boolean
Check whether modal is open.
#
common.ui.setModal(visible, content[, options]) ⇒ Element
#
common.ui.openModal(content[, options]) ⇒ Element
Creates a new modal dialog (or closes, if visible
is falsy). Function openModal() is the same with visible
defaulted to true.
| Param | Type | Description |
| :--- | :---: | :--- |
| visible
| Boolean | Whether to open or close modal |
| content
| String | Modal content HTML |
| options
| Object | |
| options.id
| String | Id of inner modal dialog element. |
| options.showBackground
| Boolean | If truthy, creates a semi-transparent background over window. |
| options.notExitable
| Boolean | Normally modal closes on clicking anywhere outside modal dialog element. If truthy, this prevents this functionality. |
| options.hideCloser
| Boolean | If truthy, does not apply the automatically placed "X" to close dialog on upper-right. |
| options.onClose
| Callback | Callback to run on modal being closed. |
Returns: Element of modal content div (.cm-modal-inner).
#
common.ui.setModalAsLoading([content[, options]]) ⇒ Element
Opens a modal dialog with default values prepped for loading. As such, no options are required, but can be provided to overwrite defaults.
| Param | Type | Default | Description |
| :--- | :---: | :---: | :--- |
| content
| String | "Loading.."
| Modal content HTML |
| options
| Object | |
| options.id
| String | "modal-loading-dialog"
| Id of inner modal dialog element. |
| options.showBackground
| Boolean | true
| If truthy, creates a semi-transparent background over window. |
| options.notExitable
| Boolean | true
| Normally modal closes on clicking anywhere outside modal dialog element. If truthy, this prevents this functionality. |
| options.hideCloser
| Boolean | true
| If truthy, does not apply the automatically placed "X" to close dialog on upper-right. |
| options.addDetails
| Boolean | true
| If truthy, adds smaller subtext below the main modal content. |
| options.addDetailsText
| String | "Please wait.."
| The content for subtext below the main modal content, if addDetails
is truthy. |
Returns: Element of modal content div (.cm-modal-inner).
#
common.ui.changeModal(content[, prepContentCallback[, hideCloser]]) ⇒ Element
Change modal dialog content while leaving all other options the same. Keeps the content-size changes from being too jarring when swapping content by adding small CSS animation to fit new size. If there was a custom width/height defined in the modal's style, these will be lost.
| Param | Type | Description |
| :--- | :---: | :--- |
| content
| String | Modal content HTML |
| prepContentCallback
| Callback | If some prep work is needed before determining the new dimensions of the modal for size change animation. |
| hideCloser
| Boolean | Due to HTML refresh, closer will be readded unless this is set as truthy. |
Returns: Element of modal content div (.cm-modal-inner
).
# common.ui.closeModal([suppressOnClose]) # common.ui.hideModal([suppressOnClose])
Hide any currently visible modal.
| Param | Type | Description |
| :--- | :---: | :--- |
| suppressOnClose
| Boolean | If truthy, suppresses onClose
event callback, if one is attached. |
CommonTable Class
Table handling object which handles data formatting, grouped columns, column sorting, and basic styling.
Must be separately imported. Returned as object if instantiated via CommonJS or AMD import. Otherwise appended to root as CommonTable class. Require base Common module to have been imported, as it depends on some the prototype modifications defined there.
To use, begin by creating instance and adding columns with CommonTable.prototype.addColumn(). The key
parameter defines how to assign the data to each column. Other parameters allow various style and formatting methods. Once all columns are added, add data and draw the table with CommonTable.prototype.populateTable(). The data, sent as an array of object literals/dictionaries, is mapped to the columns automatically with the key
defined for each column.
# CommonTable([tableId[, tableClass[, container]]])
Creates new CommonTable. The table will be given the class of cm-table, more classes can be appended through the options.
| Param | Type | Description |
| :--- | :---: | :--- |
| tableId
| String | Table ID |
| tableClass
| String | String[] | Table classname (use array to add multiple) |
| container
| Element | Element to append table to |
# CommonTable.prototype.appendTo(container)
Appends table to element.
| Param | Type | Description |
| :--- | :---: | :--- |
| container
| Element | Element to append table in |
# CommonTable.prototype.prependTo(container)
Prepends table to element.
| Param | Type | Description |
| :--- | :---: | :--- |
| container
| Element | Element to prepend table in |
# CommonTable.prototype.addColumn(options) # CommonTable.prototype.addColumn(group, title, key[, options])
Add a column to the table.
| Param | Type | Description |
| :--- | :---: | :--- |
| group
| String | The header group. If not null, used to group two or more headers as subheaders under a banner header (via colspan
). |
| title
| String | The title to display the header as. |
| key
| String | The key used to retrieve data from this header. |
| options
| Object | |
| options.group
| String | group
may be specified in the options instead. |
| options.title
| String | title
may be specified in the options instead. |
| options.key
| String | key
may be specified in the options instead. |
| options.format
| Callback | Optional function such that format(value)
, returns the formatted value for the table cell. Run in try-catch block, so if it fails, simply continues with raw value. |
| options.hdrStyles
| String | Object | Optional styles to apply to the header. Overrides any colStyles properties. |
| options.colStyles
| String | Object | Optional styles to apply to every row in this column (including header). If you only want to apply to non-header cells, must override values in hdrStyles. |
| options.onClick
| Callback | Optional onClick listener to add to each cell (excluding header). Callback will be given the entire row's data as the parameter. |
| options.sortable
| Boolean | Optional flag to set/disable sortable column on this column. By default columns are sortable, so set as falsy or null to disable. |
# CommonTable.prototype.createHeaders([options])
[Re]draw table. Unlike populateTable()
, this only redraws the headers (rest of the rows are lost).
| Param | Type | Description |
| :--- | :---: | :--- |
| options
| Object | |
| options.sortOnKey
| String | Optional key to sort on. |
| options.ascending
| Boolean | If sorting, whether ascending or descending order. |
Alternatively, parameters may be expanded out as individual arguments.
# CommonTable.prototype.createHeaders([sortOnKey[, ascending]])
| Param | Type | Description |
| :--- | :---: | :--- |
| sortOnKey
| String | Optional key to sort on. |
| ascending
| Boolean | If sorting, whether ascending or descending order. |
See above.
# CommonTable.prototype.populateTable(options)
Populate and [re]draw table.
| Param | Type | Description |
| :--- | :---: | :--- |
| options
| Object | |
| options.tableData | Object[] | Array of objects, representing data by row. Data is not stored to object or dynamically bound in any way. To update table, must be redrawn, passing the updated data array. |
| options.sortOnKey
| String | Optional key to sort on. |
| options.ascending
| Boolean | If sorting, whether ascending or descending order. |
Alternatively, parameters may be expanded out as individual arguments.
# CommonTable.prototype.populateTable(tableData[, sortOnKey[, ascending]]])
See above.
| Param | Type | Description |
| :--- | :---: | :--- |
| tableData
| Object[] | Array of objects, representing data by row. Data is not stored to object or dynamically bound in any way. To update table, must be redrawn, passing the updated data array. |
| sortOnKey
| String | Optional key to sort on. |
| ascending
| Boolean | If sorting, whether ascending or descending order. |
Example usage:
var tbl = new CommonTable("my-table-id", "my-table-class");
tbl.appendTo(document.body);
// first three columns under "Name" header group
tbl.addColumn({group: "Name", title: "First", key: "firstName"});
tbl.addColumn({group: "Name", title: "Nickname", key: "nickName"});
tbl.addColumn({group: "Name", title: "Last", key: "lastName"});
// add generic meta-data (to be used later)
tbl.addColumn({
title: "Birthday",
key: "birthDate",
format: function(val) {
return (
(val.getMonth()+1).toString() + "/"
+ val.getDate().toString() + "/"
+ val.getFullYear().toString()
);
}
});
// other columns
tbl.addColumn({title: "Wins", key: "winCount"});
tbl.addColumn({title: "Losses", key: "lossCount"});
tbl.addColumn({title: "Draws", key: "drawCount"});
var data = [
{
firstName: "Tony",
nickName: "El Cucuy",
lastName: "Ferguson",
winCount: 25,
lossCount: 8,
drawCount: 0,
birthDate: new DateUTC(1984, 2, 12)
},
{
firstName: "Khabib",
nickName: "The Eagle",
lastName: "Nurmagomedov",
winCount: 29,
lossCount: 0,
drawCount: 0,
birthDate: new DateUTC(1988, 9, 20)
},
// etc...
];
tbl.populateTable({
tableData: data,
sortOnKey: "winCount",
ascending: false // sort by wins descending
});
Acknowledgments
A huge bulk of this library was built on solutions found through the Mozilla Developers Network, StackOverflow, and many other smart folks. I would also like to thank SFEI, podcasts, and coffee.