#= require ./classes/selector ###** DOM helpers =========== The `up.element` module offers functions for DOM manipulation and traversal. It complements [native `Element` methods](https://www.w3schools.com/jsref/dom_obj_all.asp) and works across all [supported browsers](/up.browser). @module up.element ### up.element = do -> u = up.util ###** Returns a null-object that mostly behaves like an `Element`. @function up.element.none() @internal ### NONE = { getAttribute: -> undefined } ###** Matches all elements that have a descendant matching the given selector. \#\#\# Example `up.element.all('div:has(span)')` matches all `
` elements with at least one `` among its descendants: ```html
Will be matched
Will NOT be matched
Will be matched
``` \#\#\# Compatibility `:has()` is supported by all Unpoly functions (like `up.element.all()`) and selectors (like `a[up-target]`). As a [level 4 CSS selector](https://drafts.csswg.org/selectors-4/#relational), `:has()` [has yet to be implemented](https://caniuse.com/#feat=css-has) in native browser functions like [`document.querySelectorAll()`](https://developer.mozilla.org/en-US/docs/Web/API/Element/querySelectorAll). You can also use [`:has()` in jQuery](https://api.jquery.com/has-selector/). @selector :has() @experimental ### parseSelector = (selector) -> up.Selector.parse(selector) ###** Returns the first descendant element matching the given selector. It is similar to [`element.querySelector()`](https://developer.mozilla.org/en-US/docs/Web/API/Document/querySelector), but also supports the [`:has()`](/has) selector. @function up.element.first @param {Element} [parent=document] The parent element whose descendants to search. If omitted, all elements in the `document` will be searched. @param {string} selector The CSS selector to match. @return {Element|undefined|null} The first element matching the selector. Returns `null` or `undefined` if no element macthes. @experimental ### first = (args...) -> selector = args.pop() parent = args[0] ? document parseSelector(selector).descendant(parent) ###** Returns all descendant elements matching the given selector. @function up.element.all @param {Element} [parent=document] The parent element whose descendants to search. If omitted, all elements in the `document` will be searched. @param {string} selector The CSS selector to match. @return {NodeList|Array} A list of all elements matching the selector. Returns an empty list if there are no matches. @experimental ### all = (args...) -> selector = args.pop() parent = args[0] ? document parseSelector(selector).descendants(parent) ###** Returns a list of the given parent's descendants matching the given selector. The list will also include the parent element if it matches the selector itself. @function up.element.subtree @param {Element} parent The parent element for the search. @param {string} selector The CSS selector to match. @return {NodeList|Array} A list of all matching elements. @experimental ### subtree = (root, selector) -> parseSelector(selector).subtree(root) ###** Returns the first element that matches the selector by testing the element itself and traversing up through its ancestors in the DOM tree. @function up.element.closest @param {Element} element The element on which to start the search. @param {string} selector The CSS selector to match. @return {Element|null|undefined} element The matching element. Returns `null` or `undefined` if no element matches. @experimental ### closest = (element, selector) -> parseSelector(selector).closest(element) ###** Returns whether the given element matches the given CSS selector. @function up.element.matches @param {Element} element The element to check. @param {string} selector The CSS selector to match. @return {boolean} Whether `element` matches `selector`. @experimental ### matches = (element, selector) -> parseSelector(selector).matches(element) ###** @function up.element.ancestor @internal ### ancestor = (element, selector) -> parseSelector(selector).ancestor(element) ###** Casts the given value to a native [Element](https://developer.mozilla.org/en-US/docs/Web/API/Element). This is useful when working with jQuery values, or to allow callers to pass CSS selectors instead of elements. \#\#\# Casting rules - If given an element, returns that element. - If given a CSS selector string, returns the [first element matching](/up.element.first) that selector. - If given a jQuery collection , returns the first element in the collection. Throws an error if the collection contains more than one element. - If given any other argument (`undefined`, `null`, `document`, `window`…), returns the argument unchanged. @function up.element.get @param {Element|jQuery|string} value The value to cast. @return {Element} The obtained `Element`. @experimental ### getOne = (value) -> if u.isElement(value) # Return an element before we run any other expensive checks value else if u.isString(value) first(value) else if u.isJQuery(value) if value.length > 1 up.fail('up.element.get(): Cannot cast multiple elements (%o) to a single element', value) value[0] else # undefined, null, Window, Document, DocumentFragment, ... value ###** Composes a list of elements from the given arguments. \#\#\# Casting rules - If given a string, returns the all elements matching that string. - If given any other argument, returns the argument [wrapped as a list](/up.util.wrapList). \#\#\# Example ```javascript $jquery = $('.jquery') // returns jQuery (2) [div.jquery, div.jquery] nodeList = document.querySelectorAll('.node') // returns NodeList (2) [div.node, div.node] element = document.querySelector('.element') // returns Element div.element selector = '.selector' // returns String '.selector' elements = up.element.list($jquery, nodeList, undefined, element, selector) // returns [div.jquery, div.jquery, div.node, div.node, div.element, div.selector] ``` @function up.element.list @param {Array|String|undefined|null>} ...args @return {Array} @internal ### getList = (args...) -> u.flatMap args, valueToList valueToList = (value) -> if u.isString(value) all(value) else u.wrapList(value) # assertIsElement = (element) -> # unless u.isElement(element) # up.fail('Not an element: %o', element) ###** Removes the given element from the DOM tree. If you don't need IE11 support you may also use the built-in [`Element#remove()`](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/remove) to the same effect. @function up.element.remove @param {Element} element The element to remove. @experimental ### remove = (element) -> if element.remove element.remove() # IE does not support Element#remove() else if parent = element.parentNode parent.removeChild(element) ###** Hides the given element. The element is hidden by setting an [inline style](https://www.codecademy.com/articles/html-inline-styles) of `{ display: none }`. Also see `up.element.show()`. @function up.element.hide @param {Element} element @experimental ### hide = (element) -> element.style.display = 'none' ###** Shows the given element. Also see `up.element.hide()`. \#\#\# Limitations The element is shown by setting an [inline style](https://www.codecademy.com/articles/html-inline-styles) of `{ display: '' }`. You might have CSS rules causing the element to remain hidden after calling `up.element.show(element)`. Unpoly will not handle such cases in order to keep this function performant. As a workaround, you may manually set the `element.style.display` property. Also see discussion in jQuery issues [#88](https://github.com/jquery/jquery.com/issues/88), [#2057](https://github.com/jquery/jquery/issues/2057) and [this WHATWG mailing list post](http://lists.w3.org/Archives/Public/public-whatwg-archive/2014Apr/0094.html). @function up.element.show @param {Element} element @experimental ### show = (element) -> element.style.display = '' ###** Display or hide the given element, depending on its current visibility. @function up.element.toggle @param {Element} element @param {Boolean} [newVisible] Pass `true` to show the element or `false` to hide it. If omitted, the element will be hidden if shown and shown if hidden. @experimental ### toggle = (element, newVisible) -> newVisible ?= !isVisible(element) if newVisible show(element) else hide(element) # trace = (fn) -> # (args...) -> # console.debug("Calling %o with %o", fn, args) # fn(args...) ###** Adds or removes the given class from the given element. If you don't need IE11 support you may also use the built-in [`Element#classList.toggle(className)`](https://developer.mozilla.org/en-US/docs/Web/API/Element/classList) to the same effect. @function up.element.toggleClass @param {Element} element The element for which to add or remove the class. @param {String} className The class which should be added or removed. @param {Boolean} [newPresent] Pass `true` to add the class to the element or `false` to remove it. If omitted, the class will be added if missing and removed if present. @experimental ### toggleClass = (element, klass, newPresent) -> list = element.classList newPresent ?= !list.contains(klass) if newPresent list.add(klass) else list.remove(klass) ###** Sets all key/values from the given object as attributes on the given element. \#\#\# Example up.element.setAttrs(element, { title: 'Tooltip', tabindex: 1 }) @function up.element.setAttrs @param {Element} element The element on which to set attributes. @param {object} attributes An object of attributes to set. @experimental ### setAttrs = (element, attributes) -> for key, value of attributes element.setAttribute(key, value) ###** @function up.element.metaContent @internal ### metaContent = (name) -> selector = "meta" + attributeSelector('name', name) first(selector)?.getAttribute('content') ###** @function up.element.insertBefore @internal ### insertBefore = (existingElement, newElement) -> existingElement.insertAdjacentElement('beforebegin', newElement) # insertAfter = (existingElement, newElement) -> # existingElement.insertAdjacentElement('afterend', newElement) ###** Replaces the given old element with the given new element. The old element will be removed from the DOM tree. If you don't need IE11 support you may also use the built-in [`Element#replaceWith()`](https://developer.mozilla.org/en-US/docs/Web/API/ChildNode/replaceWith) to the same effect. @function up.element.replace @param {Element} oldElement @param {Element} newElement @experimental ### replace = (oldElement, newElement) -> oldElement.parentElement.replaceChild(newElement, oldElement) ###** Creates an element matching the given CSS selector. The created element will not yet be attached to the DOM tree. Attach it with [`Element#appendChild()`](https://developer.mozilla.org/en-US/docs/Web/API/Node/appendChild) or use `up.element.affix()` to create an attached element. \#\#\# Examples To create an element with a given tag name: element = up.element.createFromSelector('span') // element is To create an element with a given class: element = up.element.createFromSelector('.klass') // element is
To create an element with a given ID: element = up.element.createFromSelector('#foo') // element is
To create an element with a given boolean attribute: element = up.element.createFromSelector('[attr]') // element is
To create an element with a given attribute value: element = up.element.createFromSelector('[attr="value"]') // element is
You may also pass an object of attribute names/values as a second argument: element = up.element.createFromSelector('div', { attr: 'value' }) // element is
You may set the element's inner text by passing a `{ text }` option: element = up.element.createFromSelector('div', { text: 'inner text' }) // element is
inner text
You may set inline styles by passing an object of CSS properties as a second argument: element = up.element.createFromSelector('div', { style: { color: 'red' }}) // element is
@function up.element.createFromSelector @param {string} selector The CSS selector from which to create an element. @param {Object} [attrs] An object of attributes to set on the created element. @param {Object} [attrs.text] The [text content](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) of the created element. @param {Object} [attrs.style] An object of CSS properties that will be set as the inline style of the created element. @return {Element} The created element. @experimental ### createFromSelector = (selector, attrs) -> # Extract attribute values before we do anything else. # Attribute values might contain spaces, and then we would incorrectly # split depths at that space. attrValues = [] selectorWithoutAttrValues = selector.replace /\[([\w-]+)(?:=(["'])?([^"'\]]*?)\2)?\]/g, (_match, attrName, _quote, attrValue) -> attrValues.push(attrValue || '') "[#{attrName}]" depths = selectorWithoutAttrValues.split(/[ >]+/) rootElement = undefined depthElement = undefined previousElement = undefined for depthSelector in depths tagName = undefined depthSelector = depthSelector.replace /^[\w-]+/, (match) -> tagName = match '' depthElement = document.createElement(tagName || 'div') rootElement ||= depthElement depthSelector = depthSelector.replace /\#([\w-]+)/, (_match, id) -> depthElement.id = id '' depthSelector = depthSelector.replace /\.([\w-]+)/g, (_match, className) -> depthElement.classList.add(className) '' # If we have stripped out attrValues at the beginning of the function, # they have been replaced with the attribute name only (as "[name]"). if attrValues.length depthSelector = depthSelector.replace /\[([\w-]+)\]/g, (_match, attrName) -> depthElement.setAttribute(attrName, attrValues.shift()) '' unless depthSelector == '' throw new Error('Cannot parse selector: ' + selector) previousElement?.appendChild(depthElement) previousElement = depthElement if attrs if classValue = u.pluckKey(attrs, 'class') for klass in u.wrapList(classValue) rootElement.classList.add(klass) if styleValue = u.pluckKey(attrs, 'style') setInlineStyle(rootElement, styleValue) if textValue = u.pluckKey(attrs, 'text') rootElement.innerText = textValue setAttrs(rootElement, attrs) rootElement ###** Creates an element matching the given CSS selector and attaches it to the given parent element. To create a detached element from a selector, see `up.element.createFromSelector()`. \#\#\# Example element = up.element.affix(document.body, '.klass') element.parentElement // returns document.body element.className // returns 'klass' @function up.element.affix @param {Element} parent The parent to which to attach the created element. @param {string} selector The CSS selector from which to create an element. @param {Object} attrs An object of attributes to set on the created element. @param {Object} attrs.text The [text content](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) of the created element. @param {Object} attrs.style An object of CSS properties that will be set as the inline style of the created element. @return {Element} The created element. @experimental ### affix = (parent, selector, attributes) -> element = createFromSelector(selector, attributes) parent.appendChild(element) element ###** Returns a CSS selector that matches the given element as good as possible. To build the selector, the following element properties are used in decreasing order of priority: - The element's `[up-id]` attribute - The element's `[id]` attribute - The element's `[name]` attribute - The element's `[class]` names - The element's [`[aria-label]`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-label_attribute) attribute - The element's tag name \#\#\# Example element = document.createElement('span') element.className = 'klass' selector = up.element.toSelector(element) // returns '.klass' @function up.element.toSelector @param {string|Element|jQuery} The element for which to create a selector. @experimental ### toSelector = (element) -> return element if u.isString(element) # resolveSelector() might be called with jQuery collections element = getOne(element) selector = undefined if isSingleton(element) selector = elementTagName(element) else if upId = element.getAttribute("up-id") selector = attributeSelector('up-id', upId) else if id = element.getAttribute("id") if id.match(/^[a-z0-9\-_]+$/i) selector = "##{id}" else selector = attributeSelector('id', id) else if name = element.getAttribute("name") selector = elementTagName(element) + attributeSelector('name', name) else if classes = u.presence(nonUpClasses(element)) selector = '' for klass in classes selector += ".#{klass}" else if ariaLabel = element.getAttribute("aria-label") selector = attributeSelector('aria-label', ariaLabel) else selector = elementTagName(element) return selector ###** Sets an unique identifier for this element. This identifier is used by `up.element.toSelector()` to create a CSS selector that matches this element precisely. If the element already has other attributes that make a good identifier, like a `[id]`, `[class]` or `[aria-label]`, it is not necessary to set `[up-id]`. \#\#\# Example Take this element: Homepage Unpoly cannot generate a good CSS selector for this element: up.element.toSelector(element) // returns 'a' We can improve this by assigning an `[up-id]`: Open user 4 The attribute value is used to create a better selector: up.element.toSelector(element) // returns '[up-id="link-to-home"]' @selector [up-id] @param {string} up-id A string that uniquely identifies this element. @stable ### ###** @function up.element.isSingleton @internal ### isSingleton = (element) -> matches(element, 'html, body, head, title') elementTagName = (element) -> element.tagName.toLowerCase() ###** @function up.element.attributeSelector @internal ### attributeSelector = (attribute, value) -> value = value.replace(/"/g, '\\"') "[#{attribute}=\"#{value}\"]" nonUpClasses = (element) -> classString = element.className classes = u.splitValues(classString) u.reject classes, (klass) -> klass.match(/^up-/) ###** @function up.element.createDocumentFromHtml @internal ### createDocumentFromHtml = (html) -> # IE9 cannot set innerHTML on a or element. parser = new DOMParser() return parser.parseFromString(html, 'text/html') ###** Creates an element from the given HTML fragment. \#\#\# Example element = up.element.createFromHtml('
text
') element.className // returns 'foo' element.children[0] // returns element element.children[0].textContent // returns 'text' @function up.element.createFromHtml @experimental ### createFromHtml = (html) -> doc = createDocumentFromHtml(html) return doc.body.children[0] ###** @function up.element.root @internal ### getRoot = -> document.documentElement ###** Forces the browser to paint the given element now. @function up.element.paint @internal ### paint = (element) -> element.offsetHeight ###** @function up.element.concludeCssTransition @internal ### concludeCssTransition = (element) -> undo = setTemporaryStyle(element, transition: 'none') # Browsers need to paint at least one frame without a transition to stop the # animation. In theory we could just wait until the next paint, but in case # someone will set another transition after us, let's force a repaint here. paint(element) return undo ###** Returns whether the given element has a CSS transition set. @function up.element.hasCssTransition @return {boolean} @internal ### hasCssTransition = (elementOrStyleHash) -> if u.isOptions(elementOrStyleHash) styleHash = elementOrStyleHash else styleHash = computedStyle(elementOrStyleHash) prop = styleHash.transitionProperty duration = styleHash.transitionDuration # The default transition for elements is actually "all 0s ease 0s" # instead of "none", although that has the same effect as "none". noTransition = (prop == 'none' || (prop == 'all' && duration == 0)) not noTransition ###** @function up.element.fixedToAbsolute @internal ### fixedToAbsolute = (element) -> elementRectAsFixed = element.getBoundingClientRect() # Set the position to 'absolute' so it gains an offsetParent element.style.position = 'absolute' offsetParentRect = element.offsetParent.getBoundingClientRect() setInlineStyle element, left: elementRectAsFixed.left - computedStyleNumber(element, 'margin-left') - offsetParentRect.left top: elementRectAsFixed.top - computedStyleNumber(element, 'margin-top') - offsetParentRect.top right: '' bottom: '' ###** On the given element, set attributes that are still missing. @function up.element.setMissingAttrs @internal ### setMissingAttrs = (element, attrs) -> for key, value of attrs if u.isMissing(element.getAttribute(key)) element.setAttribute(key, value) ###** @function up.element.unwrap @internal ### unwrap = (wrapper) -> parent = wrapper.parentNode; wrappedNodes = u.toArray(wrapper.childNodes) u.each wrappedNodes, (wrappedNode) -> parent.insertBefore(wrappedNode, wrapper) parent.removeChild(wrapper) # ###** # Returns the value of the given attribute on the given element, if the value is [present](/up.util.isPresent). # # Returns `undefined` if the attribute is not set, or if it is set to an empty string. # # @function up.element.presentAttr # @param {Element} element # The element from which to retrieve the attribute value. # @param {String} attribute # The attribute name. # @return {string|undefined} # The attribute value, if present. # @experimental # ### # presentAttr = (element, attribute) -> # value = element.getAttribute(attribute) # u.presence(value) ###** Returns the value of the given attribute on the given element, cast as a boolean value. If the attribute value cannot be cast to `true` or `false`, `undefined` is returned. \#\#\# Casting rules This function deviates from the [HTML Standard for boolean attributes](https://html.spec.whatwg.org/multipage/common-microsyntaxes.html#boolean-attributes) in order to allow `undefined` values. When an attribute is missing, Unpoly considers the value to be `undefined` (where the standard would assume `false`). Unpoly also allows `"true"` and `"false"` as attribute values. The table below shows return values for `up.element.booleanAttr(element, 'foo')` given different elements: | Element | Return value | |---------------------|--------------| | `
` | `true` | | `
` | `true` | | `
` | `true` | | `
` | `true` | | `
` | `false` | | `
` | `undefined` | | `
` | `undefined` | @function up.element.booleanAttr @param {Element} element The element from which to retrieve the attribute value. @param {String} attribute The attribute name. @return {boolean|undefined} The cast attribute value. @experimental ### booleanAttr = (element, attribute, pass) -> value = element.getAttribute(attribute) switch value when 'false' false when 'true', '', attribute true else value if pass ###** Returns the given attribute value cast as boolean. If the attribute value cannot be cast, returns the attribute value unchanged. @internal ### booleanOrStringAttr = (element, attribute) -> booleanAttr(element, attribute, true) ###** Returns the value of the given attribute on the given element, cast to a number. If the attribute value cannot be cast to a number, `undefined` is returned. @function up.element.numberAttr @param {Element} element The element from which to retrieve the attribute value. @param {String} attribute The attribute name. @return {number|undefined} The cast attribute value. @experimental ### numberAttr = (element, attribute) -> value = element.getAttribute(attribute) if value?.match(/^[\d\.]+$/) parseFloat(value) ###** Reads the given attribute from the element, parsed as [JSON](https://www.json.org/). Returns `undefined` if the attribute value is [blank](/up.util.isBlank). Throws a `SyntaxError` if the attribute value is an invalid JSON string. @function up.element.jsonAttr @param {Element} element The element from which to retrieve the attribute value. @param {String} attribute The attribute name. @return {Object|undefined} The cast attribute value. @experimental ### jsonAttr = (element, attribute) -> # The document does not respond to #getAttribute() if json = element.getAttribute?(attribute)?.trim() JSON.parse(json) ###** Temporarily sets the inline CSS styles on the given element. Returns a function that restores the original inline styles when called. \#\#\# Example element = document.querySelector('div') unhide = up.element.setTemporaryStyle(element, { 'visibility': 'hidden' }) // do things while element is invisible unhide() // element is visible again @function up.element.setTemporaryStyle @param {Element} element The element to style. @param {Object} styles An object of CSS property names and values. @return {Function()} A function that restores the original inline styles when called. @internal ### setTemporaryStyle = (element, newStyles, block) -> oldStyles = inlineStyle(element, Object.keys(newStyles)) setInlineStyle(element, newStyles) return -> setInlineStyle(element, oldStyles) ###** Receives [computed CSS styles](https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle) for the given element. \#\#\# Examples When requesting a single CSS property, its value will be returned as a string: value = up.element.style(element, 'font-size') // value is '16px' When requesting multiple CSS properties, the function returns an object of property names and values: value = up.element.style(element, ['font-size', 'margin-top']) // value is { 'font-size': '16px', 'margin-top': '10px' } @function up.element.style @param {Element} element @param {String|Array} propOrProps One or more CSS property names in kebab-case or camelCase. @return {string|object} @experimental ### computedStyle = (element, props) -> style = window.getComputedStyle(element) extractFromStyleObject(style, props) ###** Receives a [computed CSS property value](https://developer.mozilla.org/en-US/docs/Web/API/Window/getComputedStyle) for the given element, casted as a number. The value is casted by removing the property's [unit](https://www.w3schools.com/cssref/css_units.asp) (which is usually `px` for computed properties). The result is then parsed as a floating point number. Returns `undefined` if the property value is missing, or if it cannot be parsed as a number. \#\#\# Examples When requesting a single CSS property, its value will be returned as a string: value = up.element.style(element, 'font-size') // value is '16px' value = up.element.styleNumber(element, 'font-size') // value is 16 @function up.element.styleNumber @param {Element} element @param {String} prop A single property name in kebab-case or camelCase. @return {number|undefined} @experimental ### computedStyleNumber = (element, prop) -> rawValue = computedStyle(element, prop) if u.isGiven(rawValue) parseFloat(rawValue) else undefined ###** Gets the given inline style(s) from the given element's `[style]` attribute. @function up.element.inlineStyle @param {Element} element @param {String|Array} propOrProps One or more CSS property names in kebab-case or camelCase. @return {string|object} @internal ### inlineStyle = (element, props) -> style = element.style extractFromStyleObject(style, props) extractFromStyleObject = (style, keyOrKeys) -> if u.isString(keyOrKeys) style[keyOrKeys] else # array u.only(style, keyOrKeys...) ###** Sets the given CSS properties as inline styles on the given element. @function up.element.setStyle @param {Element} element @param {Object} props One or more CSS properties with kebab-case keys or camelCase keys. @return {string|object} @experimental ### setInlineStyle = (element, props) -> style = element.style for key, value of props value = normalizeStyleValueForWrite(key, value) style[key] = value normalizeStyleValueForWrite = (key, value) -> if u.isMissing(value) value = '' else if CSS_LENGTH_PROPS.has(key.toLowerCase().replace(/-/, '')) value = cssLength(value) value CSS_LENGTH_PROPS = u.arrayToSet [ 'top', 'right', 'bottom', 'left', 'padding', 'paddingtop', 'paddingright', 'paddingbottom', 'paddingleft', 'margin', 'margintop', 'marginright', 'marginbottom', 'marginleft', 'borderwidth', 'bordertopwidth', 'borderrightwidth', 'borderbottomwidth', 'borderleftwidth' 'width', 'height', 'maxwidth', 'maxheight', 'minwidth', 'minheight', ] ###** Converts the given value to a CSS length value, adding a `px` unit if required. @function cssLength @internal ### cssLength = (obj) -> if u.isNumber(obj) || (u.isString(obj) && /^\d+$/.test(obj)) obj.toString() + "px" else obj ###** Resolves the given CSS selector (which might contain `&` references) to a full CSS selector without ampersands. If passed an `Element` or `jQuery` element, returns a CSS selector string for that element. @function up.element.resolveSelector @param {string|Element|jQuery} selectorOrElement @param {string|Element|jQuery} origin The element that this selector resolution is relative to. That element's selector will be substituted for `&` ([like in Sass](https://sass-lang.com/documentation/file.SASS_REFERENCE.html#parent-selector)). @return {string} @internal ### resolveSelector = (selectorOrElement, origin) -> if u.isString(selectorOrElement) selector = selectorOrElement if u.contains(selector, '&') if u.isPresent(origin) # isPresent returns false for empty jQuery collection originSelector = toSelector(origin) selector = selector.replace(/\&/, originSelector) else up.fail("Found origin reference (%s) in selector %s, but no origin was given", '&', selector) else selector = toSelector(selectorOrElement) selector ###** Returns whether the given element is currently visible. An element is considered visible if it consumes space in the document. Elements with `{ visibility: hidden }` or `{ opacity: 0 }` are considered visible, since they still consume space in the layout. Elements not attached to the DOM are considered hidden. @function up.element.isVisible @param {Element} element The element to check. @experimental ### isVisible = (element) -> # From https://github.com/jquery/jquery/blame/9cb162f6b62b6d4403060a0f0d2065d3ae96bbcc/src/css/hiddenVisibleSelectors.js#L12 !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length) <% if ENV['JS_KNIFE'] %>knife: eval(Knife.point)<% end %> # also document :has()! first: first # same as document.querySelector all: all # same as document.querySelectorAll subtree: subtree # practical closest: closest # needed for IE11 matches: matches # needed for IE11 ancestor: ancestor # not practical. we use it to implement closest get: getOne # practical for code that also works with jQuery list: getList # practical for composing multiple collections, or wrapping. remove: remove # needed for IE11 toggle: toggle # practical toggleClass: toggleClass # practical hide: hide # practical show: show # practical metaContent: metaContent # internal replace: replace # needed for IE11 insertBefore: insertBefore # internal shortcut, people can use insertAdjacentElement and i don't want to support insertAfter when I don't need it. createFromSelector: createFromSelector # practical for element creation. setAttrs: setAttrs # practical affix: affix # practical for element creation toSelector: toSelector # practical isSingleton: isSingleton # internal attributeSelector: attributeSelector # internal createDocumentFromHtml: createDocumentFromHtml # internal createFromHtml: createFromHtml # practical for element creation root: getRoot # internal paint: paint # internal concludeCssTransition: concludeCssTransition # internal hasCssTransition: hasCssTransition # internal fixedToAbsolute: fixedToAbsolute # internal setMissingAttrs: setMissingAttrs # internal unwrap: unwrap # practical for jQuery migration # presentAttr: presentAttr # experimental booleanAttr: booleanAttr # it's practical, but i cannot find a good name. people might expect it to cast to number, too. but i don't need that for my own code. maybe booleanAttr? numberAttr: numberAttr # practical jsonAttr: jsonAttr # practical booleanOrStringAttr: booleanOrStringAttr setTemporaryStyle: setTemporaryStyle # practical style: computedStyle # practical. styleNumber: computedStyleNumber # practical. inlineStyle: inlineStyle # internal setStyle: setInlineStyle # practical. resolveSelector: resolveSelector # internal none: -> NONE # internal isVisible: isVisible # practical