+
+for tooltips which fetch their content with an AJAX call the tooltip may
+be temporarily shown with a progress indicator:
+
+
+ …
+
+
+
+
+
+
+
+### Options reference
+
+The tooltip can be configured through a `data-pat-tooltip` attribute.
+The available options are:
+
+| Property | Default value | Values | Description | Type |
+| ----- | --------| -------- | ------- | ----------- |
+| `position-list`| `auto` | `tl` `tm` `tr` `rt` `rm` `rb` `br` `bm` `bl` `lb` `lm` `lt` | The priority in which the pattern will try to position the tooltip. With the tooltip is positioned where the most space is on the screen. The two letters indicate the position of the triangle as opposed to the tooltip body. Adding `force` will force the tooltip position, even if it would end up out of view. | Multiple value |
+| `position-policy` | `auto` | `auto` `force` | Policy used to place a tooltip: either always use a listed position, or allow other positions if no space is available for the listed positions. | Mutually exclusive |
+| `trigger` | `click` | `click` `hover` | Sets which user action should make the tooltip appear. | Mutually exclusive |
+| `source` | `title` | `ajax` `content` `title` | Select where the contents of the tooltip is taken from: AJAX loading of the link target, the contents of element or its title attribute. | Mutually exclusive |
+| `delay` | `0` | *time* | `The delay for the tooltip to appear, expressed in milliseconds | Time |
+| `mark-inactive` | `true` | `true` `false` | Should we add inactive class to the tooltip trigger? | Bool |
+| `closing` | `auto` | `auto` `sticky` `close-button` | Auto means that the tooltip will disappear when the user clicks out of the tooltip, or — in case of hover triggered tooltips — hovers away from the trigger element. `close-button` will add a close button to the tooltip which must be used to close the tooltip. | Mutually exclusive |
+| `class` | *none* | *class value* | Assigns a class to the tooltip. For instance to give a specific tooltip a different colour | |
+| `ajax-data-type`| `html` | `html` `markdown` | Data type of content to be loaded when AJAX is used as source. | Mutually exclusive |
+| `target` | `body` | *selector* | Selects where the tooltip container is appended in the DOM | |
diff --git a/src/pat/tooltip-ng/index.html b/src/pat/tooltip-ng/index.html
new file mode 100644
index 000000000..3fb078343
--- /dev/null
+++ b/src/pat/tooltip-ng/index.html
@@ -0,0 +1,108 @@
+
+
+
+
+ pat-tooltip-ng demo
+
+
+
+
+
+
pat-tooltip-ng demo
+
Defaults
+
+ Click here to see a minimally configured tooltip.
+ By default tooltips are triggered by clicking and they will show the contents
+ of the title attribute of the element being clicked.
+
+
Configurations
+
Tooltips can be configured.
+
class
+
+ class: wasabi. A tooltip with CSS class wasabi is displayed when the element is clicked.
+
+
delay
+
+ delay: 1000. A tooltip is displayed with a delay of 1000 ms when hovering the mouse over the element.
+
+
mark-inactive
+
+ mark-inactive: false. The default for mark-inactive is true and it toggles the class of the trigger element between active and inactive every time the tooltip is shown or hidden. This example disables this behavior. All the other examples have it enabled (as per default). A tooltip is displayed when clicking the element.
+
+
trigger
+
+ trigger: click (default). A tooltip is displayed when the element is clicked.
+
+
+ trigger: hover. A tooltip is displayed when hovering the mouse pointer over the element.
+
+
source
+
+ source: title (default). Click here to see the title attribute in the tooltip.
+
+
+ source: content and href="#". Click the following link to show the same content in the link inside the tooltip.
+
+ ajax-data-type: markdown. Applies when injecting content via ajax. The default is html, but this example shows a tooltip with content in markdown on click.
+
target
+
+ target: body (default). When clicking this link the tooltip will be appended to the document.body.
+
+
+ target: parent. When clicking this link the tooltip will be appended to its parent container.
+
+
+ target: #tooltip-container. When clicking this link the tooltip will be appended to an element matching the given selector.
+
+ position-list: tm You can configure tooltips to set their preferred positions as well when they will appear.
+ This tooltip is configured to preferably appear in the top middle, but won't appear there if there isn't enough space.
+
+
+ position-list: rt; position-policy: force You can configure tooltips to set their preferred positions as well when they will appear.
+ This tooltip is configured to always appear in the right top, and doesn't care if there isn't enough space.
+
+
+ position-list: tl Only one position preference given to compare with previous one when scrolling.
+ Try this tooltip and then scroll the page up and down.
+
+
+ position-list: tl, rt The tooltip will flip its position according to your preferences when scrolling.
+ Try this tooltip and then scroll the page up and down.
+
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed sed urna augue. Nulla facilisi. Aliquam erat volutpat. Cras pharetra ante vel metus pulvinar vestibulum. Praesent mollis dapibus nisl, id condimentum quam lacinia ut. Fusce luctus felis ac mi lacinia porttitor. Vivamus sed nunc turpis. Phasellus quis dui sed mauris sollicitudin consequat at ut nisi. Mauris egestas enim ac nisi ultrices facilisis. Praesent at metus risus, vitae congue nulla. Integer dapibus orci eu tellus consectetur eu posuere nibh tincidunt. Proin lacinia, arcu quis ultrices porttitor, nisi libero convallis velit, sit amet vehicula nisi neque a justo. Suspendisse vitae sem quis diam pulvinar fermentum a vel augue. Vestibulum id sagittis dolor.
+
+ Proin gravida volutpat velit, sit amet pulvinar metus varius porttitor. Nulla dictum vulputate diam, nec placerat enim tempus at. Sed sodales sollicitudin nibh, a cursus leo pretium a. Morbi nisl magna, interdum ut interdum ut, volutpat nec enim. Integer enim nisl, consequat et elementum at, lobortis sit amet lectus. Aliquam egestas nisl condimentum diam hendrerit non congue augue facilisis. Suspendisse potenti.
+
+ Sed ultricies luctus molestie. Donec facilisis adipiscing molestie. Suspendisse in nunc sed felis bibendum pulvinar. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Mauris quis purus odio, non elementum elit. Nullam sed nisl sapien. Vestibulum rhoncus, risus ac suscipit eleifend, urna quam accumsan turpis, eget ullamcorper nunc risus ut dolor. Curabitur eget quam erat. Aenean in dolor id ipsum lobortis feugiat. Morbi porta fringilla venenatis. Pellentesque vitae est neque, hendrerit eleifend erat. Donec fermentum augue id arcu pretium eleifend. Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+
+
+
+ This should appear on the modal window
+
+
+ You can do selfHealing and
+ close the panel at the same time.
+
We hope you will enjoy watching these channels. If you feel there's anything missing, we've got thousands more channels in stock that you can subscribe too.
Barack Hussein Obama II (born August 4, 1961) is the 44th and current President of the United States, having taken office in 2009. He is the first African American to hold the office. Obama previously served as a United States senator from Illinois, from January 2005 until he resigned followinghis election to the presidency in November 2008.
+ A native of Honolulu, Hawaii, Obama is a graduate of Columbia University and Harvard Law School, where he was the president of the Harvard Law Review. He was acommunity organizer in Chicago before earning his law degree. He worked as a civil rights attorney in Chicago and taught constitutional law at the University of Chicago Law School from 1992 to 2004.
Coldplay are an English alternative rock band formed in 1996 by lead vocalist Chris Martin and lead guitarist Jonny Buckland at University College London. After forming Pectoralz, Guy Berryman joined the group as a bassist and they changed their name to Starfish. Will Champion joined as a drummer, backing vocalist, and multi-instrumentalist, completing the lineup. Manager Phil Harvey is often considered an unofficial fifth member. The band renamed themselves "Coldplay" in 1998, before recording and releasing three EPs; Safety in 1998, Brothers & Sisters as a single in 1999 and The Blue Room in the same year. The latter was their first release on a major label, after signing to Parlophone.
Hello everyone... Let me tell you a little about myself. I am known on many Cruise related message boards as Radio... it's an easy name to remember. There are lots of GREAT Cruise Boards on the internet, like Cruise Critic, Cruise Mates, and CrownCast.
+ I don't have a broadcast or Radio job, but more on that later.
Marshall Bruce Mathers III (born October 17, 1972), better known by his stage name Eminem or his alter ego Slim Shady, is an American rapper, record producer, and actor. Eminem quickly gained popularity in 1999 with his major-label debut album, The Slim Shady LP, which won a Grammy Award for Best Rap Album. The following album, The Marshall Mathers LP, became the fastest-selling solo album in United States history. It brought Eminem increased popularity, including his own record label, Shady Records, and brought his group project, D12, to mainstream recognition. As well as being a member of D12, Eminem is also one half of the hip-hop duo Bad Meets Evil (the other member being Royce da 5'9").
+
Facebook is a social networking service and website launched in February 2004, operated and privately owned by Facebook, Inc. As of January 2011, Facebook has more than 600 million active users. Users may create a personal profile, add other users as friends, and exchange messages, including automatic notifications when they update their profile. Additionally, users may join common interest user groups, organized by workplace, school or college, or other characteristics. The name of the service stems from the colloquial name for the book given to students at the start of the academic year by university administrations in the United States to help students get to know each other better. Facebook allows anyone who declares themselves to be at least 13 years old to become a registered user of the website.
Green Day is an American punk rock band formed in 1987. The band consists of lead vocalist and guitarist Billie Joe Armstrong, bassist and backing vocalist Mike Dirnt, and drummer Tre Cool. Cool replaced former drummer John Kiffmeyer in 1990, prior to the recording of the band's second studio album, Kerplunk, and has been a member of the band since.
Guns N' Roses (sometimes abbreviated as G N' R or GnR) is an American hard rock band. The band formed in Hollywood, Los Angeles, California, in 1985. Led by frontman and co-founder Axl Rose, the band has released six studio albums, three EPs, and one live album while going through numerous line-up changes and controversies since its formation. Rose is the only consistent and original member of Guns N' Roses.
Pearl Jam is an American rock band that formed in Seattle, Washington in 1990. The band's original line-up consisted of Eddie Vedder (lead vocals), Jeff Ament (bass), Stone Gossard (rhythm guitar), Mike McCready (lead guitar), and Dave Krusen (drums). The band's current drummer is Matt Cameron, formerly of Soundgarden, who has been with the band since 1998.
The Phoenix Suns are a professional basketball team based in Phoenix, Arizona. They are members of the Pacific Division of the Western Conference in the National Basketball Association (NBA) and the only team in their division not to be based in California. Their home arena since 1992 has been the US Airways Center, which was formerly known as America West Arena, in downtown Phoenix. The Arena is often referred to as the "Purple Palace" due to its purple seats.
Red Hot Chili Peppers (sometimes referred to as RHCP) is an American rock band. They formed in Los Angeles, California, in 1983.
+ The band's current line-up consists of Anthony Kiedis (vocals), Michael "Flea" Balzary (bass), Chad Smith (drums) and Josh Klinghoffer (guitar). The group's musical style has fused traditional funk with elements of other genres, including punk and psychedelic rock.
Shakira Isabel Mebarak Ripoll known professionally as Shakira is a Colombian singer, songwriter, musician, record producer, dancer, and philanthropist who emerged in the music scene of Colombia and Latin America in the early 1990s. Born and raised in Barranquilla, Colombia, Shakira revealed many of her talents in school as a live performer, demonstrating her vocal ability with rock and roll, Latin andMiddle Eastern influences with her own original twist on belly dancing. Shakira is a native Spanish speaker and also speaks fluent English and Portuguese as well as some Italian, French and Arabic.
U2 are a rock band which formed in Dublin, Ireland. Formed in 1976, the group consists of Bono (vocals and guitar), The Edge (guitar, keyboards and vocals), Adam Clayton (bass guitar), and Larry Mullen, Jr. (drums and percussion). U2's early sound was indebted to post-punk but eventually grew to incorporate influences from many genres of popular music. Throughout the group's musical pursuits, they have maintained a recognisable sound built on melodic instrumentals, highlighted by The Edge's textural guitar playing and Bono's expressive vocals. Their lyrics, often embellished with spiritual imagery, focus on personal themes and sociopolitical concerns.
Founded in February 2005, YouTube allows billions of people to discover, watch and share originally-created videos. YouTube provides a forum for people to connect, inform, and inspire others across the globe and acts as a distribution platform for original content creators and advertisers large and small. See our company timeline for more information on our company history.
Diggnation is Revision3's flagship video podcast produced weekly. It is hosted by Kevin Rose and Alex Albrecht, who had previously hosted TechTV's The Screen Savers together.
Leona Louise Lewis (born 3 April 1985) is a British singer-songwriter. Lewis was a contestant in third series of the British television series The X Factor, which she won.
+ Lewis is a multi-platinum selling artist and three time Grammy Award nominee. Her most successful single, "Bleeding Love", reached number one in over thirty countries around the world. She was proclaimed 'Top New Artist' by Billboard magazine in 2008. Lewis has released two albums to date, Spirit and Echo, in 2007 and 2009 respectively. Spirit became the fastest-selling debut album and the biggest seller of 2007 in both the United Kingdom and Ireland, and made Lewis the first British solo artist to top the Billboard 200 with a debut album. It has sold over 6.5 million copies worldwide.
DeAndre Cortez Way (born July 28, 1990), better known by his stage name Soulja Boy Tell 'Em, or simply Soulja Boy, is an American rapper and record producer.
Dolce&Gabbana specializes in luxury items influenced more by designers and is more formal and 'timeless', responding to long-term trends rather than seasonal changes. It also sells sunglasses and corrective eyewear, purses, and watches.
How did the universe begin? Where will it end? Are there other worlds like Earth? A groundbreaking new series combs the rubble of exploding stars and the collision of worlds in search of answers to our most searching questions about the cosmos. Original productions from SPACERIP TV in collaboration with SPACE.COM.
ESPN, Inc., The Worldwide Leader in Sports, is the leading multinational, multimedia sports entertainment company featuring the broadest portfolio of multimedia sports assets with over 50 business entities.
Diggnation is Revision3's flagship video podcast produced weekly. It is hosted by Kevin Rose and Alex Albrecht, who had previously hosted TechTV's The Screen Savers together.
Roy Kelton Orbison (April 23, 1936 – December 6, 1988) was an American singer-songwriter, well known for his distinctive, powerful voice, complex compositions, and dark emotional ballads. Orbison grew up in Texas and began singing in a rockabilly/country & western band in high school until he was signed by Sun Records in Memphis. His greatest success came with Monument Records in the early to mid 1960s when 22 of his songs placed on the Billboard Top Forty, including "Only the Lonely", "Crying", "In Dreams", and "Oh, Pretty Woman". His career stagnated through the 1970s, but several covers of his songs and the use of one in a film by David Lynch revived his career in the 1980s. In 1988, he joined the supergroup Traveling Wilburys with George Harrison, Bob Dylan, Tom Petty, and Jeff Lynne and also released a new solo album. He died of a heart attack in December that year, at the zenith of his resurgence. His life was marred by tragedy, including the death of his first wife and two of his children in separate accidents.
E! Online is the premiere destination for up-to-the-minute entertainment news and celebrity inside information presented in a fun, irreverent tone. E! Online delivers breaking news, fashion, movie reviews, photos, video and live-event coverage to over 6 million unique U.S. users per month, per ComScore.
+
Livestrong provides support to guide people through the cancer experience, bring them together to fight cancer—and work for a world in which our fight is no longer necessary.
Jennifer Lynn Lopez (born July 24, 1969), often nicknamed J.Lo, is an American actress, singer, record producer, dancer, television personality, fashion designer and television producer. Lopez began her career as a dancer on the television comedy program In Living Color. Subsequently venturing into acting, she gained recognition in the 1995 action-thriller Money Train.
Sports Illustrated is a North American sports magazine owned by media conglomerate Time Warner. It has over 3.5 million subscribers and is read by 23 million adults each week, including over 18 million men. It was the first magazine with circulation over one million to win the National Magazine Award for General Excellence twice. Its swimsuit issue, which has been published since 1964, is now an annual publishing event that generates its own television shows, videos and calendars.
Sir James Paul McCartney, MBE (born 18 June 1942) is an English musician, singer-songwriter and composer. Formerly of The Beatles (1960–1970) and Wings (1971–1981), McCartney is the most commercially successful songwriter in the history of popular music, according to Guinness World Records.
Today's American families come in all shapes and sizes. The cookie cutter mold of man + wife + 2.5 kids is a thing of the past, as it becomes quickly apparent in the bird's eye view of ABC's new half-hour comedy, "Modern Family," which takes an honest and often hilarious look at the composition and complexity of modern family life.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
+
+
+
+
diff --git a/src/pat/tooltip-ng/tests.js b/src/pat/tooltip-ng/tests.js
new file mode 100644
index 000000000..9f91c8e13
--- /dev/null
+++ b/src/pat/tooltip-ng/tests.js
@@ -0,0 +1,896 @@
+define(['pat-tooltip-ng', 'pat-logger'], (pattern, logger) => {
+ const _OAC = '_onAjaxCallback',
+ _OS = '_onShow',
+ _OSN = '_onShown',
+ _OT = '_onTrigger',
+ _OAB = '_onAjaxBypass',
+ _OACS = '_onAjaxContentSet',
+ _OH = '_onHidden',
+ _PD = 'preventDefault',
+ ANYOPTS = jasmine.any(Object), // jshint ignore:line
+ LOREM = `Lorem.`,
+ LONGLOREM = `Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.` // jshint ignore:line
+
+ var log = logger.getLogger('pat-tooltip-ng.tests'),
+ start,
+ utils = {
+ createTooltip(c) {
+ var cfg = c || {}
+ return $('', {
+ 'id': cfg.id || 'tooltip',
+ 'href': cfg.href || '#anchor',
+ 'title': cfg.title || 'tooltip title attribute',
+ 'data-pat-tooltip-ng': '' || cfg.data,
+ 'class': 'pat-tooltip-ng'
+ }).text(cfg.content
+ ).appendTo($('div#lab'))
+ },
+
+ removeTooltip() {
+ var $el = $('a#tooltip')
+ $el.trigger('destroy.pat-tooltip-ng')
+ $el.remove()
+ },
+
+ createTooltipSource() {
+ return $(``+
+ 'Local content')
+ .appendTo($('div#lab'))
+ },
+
+ dispatchEvent($target, event_name) {
+ $target[0].dispatchEvent(new Event(event_name))
+ },
+
+ click($target) {
+ utils.dispatchEvent($target, 'click')
+ },
+
+ mouseenter($target) {
+ utils.dispatchEvent($target, 'mouseenter')
+ },
+
+ mouseleave($target) {
+ utils.dispatchEvent($target, 'mouseleave')
+ },
+
+ delayed(name, timeout) {
+ return (...args) => {
+ setTimeout(() => {
+ pattern[name].and.callThrough()
+ pattern[name].apply(null, args)
+ pattern[name].and.callFake(utils.delayed(name, timeout))
+ }, timeout)
+ }
+ },
+
+ stopwatch(name, timer) {
+ return (...args) => {
+ timer[name] = Date.now()
+ pattern[name].and.callThrough()
+ pattern[name].apply(null, args)
+ pattern[name].and.callFake(utils.stopwatch(name, timer))
+ }
+ },
+
+ log(msg) {
+ log.debug( String(Date.now() - start) + ' ' + msg)
+ }
+ };
+
+ log.setLevel(20)
+
+ describe('pat-tooltip-ng', () => {
+
+ beforeEach(() => {
+ $('', {id: 'lab'}).appendTo(document.body)
+ start = Date.now()
+ })
+
+ afterAll(() => {
+ })
+
+ describe('A tooltip', () => {
+ afterEach(() => {
+ utils.removeTooltip()
+ $('#lab').remove()
+ })
+
+ describe(`if the 'class' parameter exists`, () => {
+ it('will assign a class to the tooltip container', (done) => {
+ var $el = utils.createTooltip({
+ data: 'source: title; trigger: hover; class: wasabi',
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.mouseenter($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ var $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ var expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($container.hasClass('wasabi')).toBeTruthy()
+ done()
+ }, 400)
+ })
+
+ it('and only to the corresponding container', (done) => {
+ var $el1 = utils.createTooltip({
+ data: 'source: title; trigger: click; class: wasabi',
+ title: 'tooltip1'
+ }),
+ $el2 = utils.createTooltip({
+ data: 'source: title; trigger: click',
+ title: 'tooltip2'
+ }),
+ title1 = $el1.attr('title'),
+ title2 = $el2.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el1)
+ pattern.init($el2)
+ utils.click($el2)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ var $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ var expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title2)
+ expect($container.hasClass('wasabi')).toBeFalsy()
+ utils.click($el1)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ var $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ var expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title1)
+ expect($container.hasClass('wasabi')).toBeTruthy()
+ utils.click($el2)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ var $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ var expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title2)
+ expect($container.hasClass('wasabi')).toBeFalsy()
+ done()
+ }, 400)
+ }, 400)
+ }, 400)
+ })
+ })
+
+ describe(`if the 'delay' parameter exists`, () => {
+ it('will wait accordingly before showing the tooltip', (done) => {
+ const $el = utils.createTooltip({
+ data: 'delay: 1000; trigger: hover',
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough(),
+ timer = {}
+
+ spyOn(pattern, _OT).and.callFake(
+ utils.stopwatch(_OT, timer)
+ )
+ spyOn(pattern, _OS).and.callFake(
+ utils.stopwatch(_OS, timer)
+ )
+ pattern.init($el)
+ utils.mouseenter($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ const duration = timer[_OS] - timer[_OT]
+ expect(duration/1000).toBeCloseTo(1,1)
+ done()
+ }, 1500)
+ })
+ })
+
+ describe(`if the 'position-list' parameter exists`, () => {
+ it(`'lt' will place the tooltip as 'right-start'`, (done) => {
+ const $el = utils.createTooltip({
+ data: 'position-list: lt',
+ title: LOREM,
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($container.attr('x-placement')).toBe('right-start')
+ done()
+ }, 500)
+ })
+ it(`'lb' will place the tooltip as 'right-end'`, (done) => {
+ const $el = utils.createTooltip({
+ data: 'position-list: lb',
+ title: LOREM,
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($container.attr('x-placement')).toBe('right-end')
+ done()
+ }, 500)
+ })
+ it(`'lm' will place the tooltip as 'right'`, (done) => {
+ const $el = utils.createTooltip({
+ data: 'position-list: lm',
+ title: LOREM,
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($container.attr('x-placement')).toBe('right')
+ done()
+ }, 500)
+ })
+ it(`'bl' will place the tooltip as 'top-start'`, (done) => {
+ const $el = utils.createTooltip({
+ data: 'position-list: bl',
+ title: LOREM,
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($container.attr('x-placement')).toBe('top-start')
+ done()
+ }, 500)
+ })
+ it(`'br' will place the tooltip as 'top-end'`, (done) => {
+ const $el = utils.createTooltip({
+ data: 'position-list: br',
+ title: LOREM,
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($container.attr('x-placement')).toBe('top-end')
+ done()
+ }, 500)
+ })
+ it(`'bm' will place the tooltip as 'top'`, (done) => {
+ const $el = utils.createTooltip({
+ data: 'position-list: bm',
+ title: LOREM,
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($container.attr('x-placement')).toBe('top')
+ done()
+ }, 500)
+ })
+
+ describe(`and 'position-policy' is 'force'`, () => {
+ it(`'tl;force' will place the tooltip as 'bottom-start'`, (done) => {
+ const $el = utils.createTooltip({
+ data: 'position-list: tl; position-policy: force',
+ title: LOREM,
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($container.attr('x-placement')).toBe('bottom-start')
+ done()
+ }, 500)
+ })
+
+ it(`'tr;force' will place the tooltip as 'bottom-end'`, (done) => {
+ const $el = utils.createTooltip({
+ data: 'position-list: tr; position-policy: force',
+ title: LOREM,
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($container.attr('x-placement')).toBe('bottom-end')
+ done()
+ }, 500)
+ })
+
+ it(`'tm;force' will place the tooltip as 'bottom'`, (done) => {
+ const $el = utils.createTooltip({
+ data: 'position-list: tm; position-policy: force',
+ title: LOREM,
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($container.attr('x-placement')).toBe('bottom')
+ done()
+ }, 500)
+ })
+
+ it(`'rt;force' will place the tooltip as 'left-start'`, (done) => {
+ const $el = utils.createTooltip({
+ data: 'position-list: rt; position-policy: force',
+ title: LOREM,
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($container.attr('x-placement')).toBe('left-start')
+ done()
+ }, 500)
+ })
+
+ it(`'rb;force' will place the tooltip as 'left-end'`, (done) => {
+ const $el = utils.createTooltip({
+ data: 'position-list: rb; position-policy: force',
+ title: LOREM,
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($container.attr('x-placement')).toBe('left-end')
+ done()
+ }, 500)
+ })
+
+ it(`'rm;force' will place the tooltip as 'left'`, (done) => {
+ const $el = utils.createTooltip({
+ data: 'position-list: rm; position-policy: force',
+ title: LOREM,
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($container.attr('x-placement')).toBe('left')
+ done()
+ }, 500)
+ })
+ })
+ })
+
+ describe(`the 'mark-inactive' paramater`, () => {
+ it('when true, toggles the active/inactive class on the trigger', (done) => {
+ const $el = utils.createTooltip({
+ data: 'mark-inactive: true',
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough(),
+ spy_hidden = spyOn(pattern, _OH).and.callThrough()
+
+ pattern.init($el)
+ expect($el.hasClass('inactive')).toBeTruthy()
+ expect($el.hasClass('active')).toBeFalsy()
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($el.hasClass('active')).toBeTruthy()
+ expect($el.hasClass('inactive')).toBeFalsy()
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_hidden).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(0)
+ expect($el.hasClass('active')).toBeFalsy()
+ expect($el.hasClass('inactive')).toBeTruthy()
+ done()
+ }, 500)
+ }, 500)
+ })
+ it('when false, the trigger does not get the active/inactive class', (done) => {
+ const $el = utils.createTooltip({
+ data: 'mark-inactive: false',
+ }),
+ title = $el.attr('title'),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough(),
+ spy_hidden = spyOn(pattern, _OH).and.callThrough()
+
+ pattern.init($el)
+ expect($el.hasClass('inactive')).toBeFalsy()
+ expect($el.hasClass('active')).toBeFalsy()
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(1)
+ const expected = $container.find('.tippy-content').text()
+ expect(expected).toBe(title)
+ expect($el.hasClass('active')).toBeFalsy()
+ expect($el.hasClass('inactive')).toBeFalsy()
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_hidden).toHaveBeenCalled()
+ const $container = $('.tippy-tooltip')
+ expect($container.length).toEqual(0)
+ expect($el.hasClass('active')).toBeFalsy()
+ expect($el.hasClass('inactive')).toBeFalsy()
+ done()
+ }, 500)
+ }, 500)
+ })
+ })
+
+ describe(`if the 'trigger' parameter is 'hover'`, () => {
+ describe(`if the 'source' parameter is 'title'`, () => {
+
+ it(`will show the contents of the 'title' attribute`, (done) => {
+ utils.createTooltip({
+ data: 'source: title; trigger: hover'
+ })
+ var $el = $('a#tooltip')
+ var title = $el.attr('title')
+ var spy = spyOn(pattern, _OSN).and.callThrough()
+ pattern.init($el)
+ // The 'title' attr gets removed, otherwise the browser's
+ // tooltip will appear
+ expect($el.attr('title')).toBeFalsy()
+
+ utils.mouseenter($el)
+ setTimeout(() => {
+ expect(spy).toHaveBeenCalled()
+ var $container = $('.tippy-popper')
+ expect($container.length).toEqual(1)
+ expect($container.find('.tippy-content').text()).toBe(title)
+ utils.mouseleave($el)
+ done()
+ }, 400)
+ })
+
+ it('will hide the tooltip on mouseleave', (done) => {
+ utils.createTooltip({
+ data: 'source: title; trigger: hover'
+ })
+ var $el = $('a#tooltip')
+ var spy = spyOn(pattern, '_onHidden').and.callThrough()
+ pattern.init($el)
+
+ utils.mouseenter($el)
+ setTimeout(() => {
+ expect(spy).not.toHaveBeenCalled()
+ utils.mouseleave($el)
+ }, 100)
+
+ setTimeout(() => {
+ expect(spy).toHaveBeenCalled()
+ expect($('.tippy-popper').length).toEqual(0)
+ done()
+ }, 250)
+ })
+ })
+
+ describe(`if the 'source' parameter is 'content'`, () => {
+ describe('if the href attribute is hashtag', () => {
+ it('will show the content of the link', (done) => {
+ var content = 'Local content',
+ $el = utils.createTooltip({
+ data: 'source: content; trigger: hover',
+ href: '#',
+ content: content
+ }),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+ pattern.init($el)
+ utils.mouseenter($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ var $container = $('.tippy-popper')
+ expect($container.text()).toBe(content)
+ done()
+ }, 400)
+ })
+ })
+ describe('if the href attribute is #tooltip-source', () => {
+ it('will clone a DOM element from the page', (done) => {
+ var $el = utils.createTooltip({
+ data: 'source: content; trigger: hover',
+ href: '#tooltip-source'
+ }),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+ utils.createTooltipSource()
+ pattern.init($el)
+ utils.mouseenter($el)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalled()
+ var $container = $('.tippy-popper')
+ expect($container.find('strong').text()).toBe('Local content')
+ done()
+ }, 400)
+ })
+ })
+ })
+ })
+ describe(`if the 'target' parameter is 'body'`, () => {
+ it('will append the .tippy-popper to the document.body', (done) => {
+ var $el = utils.createTooltip({
+ data: 'target: body',
+ href: '#',
+ })
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect($('.tippy-popper', 'body').length).toEqual(1)
+ done()
+ }, 200)
+ })
+ })
+ describe(`if the 'target' parameter is 'parent'`, () => {
+ it(`will append the .tippy-popper to the reference element's parent node`, (done) => {
+ var $el = utils.createTooltip({
+ data: 'target: parent',
+ href: '#',
+ })
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect($('.tippy-popper', '#lab').length).toEqual(1)
+ done()
+ }, 200)
+ })
+ })
+ describe(`if the 'target' parameter is a selector`, () => {
+ it('will append the .tippy-popper to the selected element', (done) => {
+ var $el = utils.createTooltip({
+ data: 'target: #child3',
+ href: '#',
+ })
+ $('', {id: 'child3'})
+ .appendTo($('', {id: 'child2'}))
+ .appendTo($('', {id: 'child1'}))
+ .appendTo(document.body)
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect($('.tippy-popper', '#child3').length).toEqual(1)
+ done()
+ }, 200)
+ })
+ })
+
+ describe(`if the "source" parameter is "auto"`, () => {
+ describe(`if the "href" points to a document fragment`, () => {
+ it(`will revert to "content"`, (done) => {
+ var $el = utils.createTooltip({
+ data: 'source: auto',
+ href: '#tooltip-source'
+ }),
+ spy = spyOn(pattern, '_setSource').and.callThrough()
+
+ utils.createTooltipSource()
+ pattern.init($el)
+ setTimeout(() => {
+ expect(spy).toHaveBeenCalledWith(ANYOPTS, 'content')
+ done()
+ }, 0)
+ })
+ })
+
+ describe(`if the "href" points to an external URL`, () => {
+ it(`will revert to "ajax"`, (done) => {
+ var $el = utils.createTooltip({
+ data: 'source: auto',
+ href: '/tests/content.html#content'
+ }),
+ spy = spyOn(pattern, '_setSource').and.callThrough()
+ utils.createTooltipSource()
+
+ pattern.init($el)
+ setTimeout(() => {
+ expect(spy).toHaveBeenCalledWith(ANYOPTS, 'ajax')
+ done()
+ }, 0)
+ })
+ })
+ })
+ })
+
+ describe(`if the 'source' parameter is 'ajax'`, () => {
+ afterEach((done) => {
+ utils.log('afterEach begins!')
+ setTimeout(() => {
+ utils.log('afterEach timeout is over!')
+ utils.removeTooltip()
+ $('#lab').remove()
+ done()
+ }, 600)
+ })
+
+ it('the default click action is prevented', done => {
+ var $el = utils.createTooltip({
+ data: 'source: ajax',
+ href: 'tests/content.html#content'
+ }),
+ click = new Event('click'),
+ spy_prevented = spyOn(click, _PD).and.callThrough(),
+ spy_shown = spyOn(pattern, _OAC).and.callThrough()
+
+ pattern.init($el)
+ $el[0].dispatchEvent(click)
+ $el[0].dispatchEvent(click)
+ $el[0].dispatchEvent(click)
+ setTimeout(() => {
+ expect(spy_shown).toHaveBeenCalledBefore(spy_prevented)
+ done()
+ }, 500)
+ })
+
+ it('will fetch its contents via ajax', (done) => {
+ var $el = utils.createTooltip({
+ data: 'source: ajax',
+ href: 'tests/content.html#content'
+ }),
+ spy_ajax = spyOn(pattern, _OAC).and.callThrough(),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_ajax).toHaveBeenCalled()
+ expect(spy_shown).toHaveBeenCalled()
+ var $container = $('.tippy-popper .tippy-content')
+ expect($container.text()).toBe(
+ 'External content fetched via an HTTP request.')
+ done()
+ }, 500)
+ })
+
+ it('will handle markdown content', (done) => {
+ var $el = utils.createTooltip({
+ data: 'source: ajax; ajax-data-type: markdown',
+ href: 'tests/content.md#Display'
+ }),
+ spy_ajax = spyOn(pattern, _OAC).and.callThrough()
+ // Not sure why _OSN is not called in this test
+ // spy_shown = spyOn(pattern, _OSN).and.callThrough()
+
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ expect(spy_ajax).toHaveBeenCalled()
+ // expect(spy_shown).toHaveBeenCalled()
+ var $container = $('.tippy-popper .tippy-content')
+ expect($container.text()).toContain(
+ 'Tooltips are shown')
+ expect($container.text()).not.toContain(
+ 'contextual')
+ done()
+ }, 500)
+ })
+
+ describe('will not fetch again until tooltip is hidden', () => {
+ it('with click', done => {
+ var $el = utils.createTooltip({
+ data: 'source: ajax; trigger: click',
+ href: 'tests/content.html#content'
+ }),
+ spy_ajax = spyOn(pattern, _OAC).and.callThrough(),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough(),
+ spy_byps = spyOn(pattern, _OAB).and.callThrough()
+
+ utils.log('pattern init')
+ pattern.init($el)
+ utils.click($el)
+ setTimeout(() => {
+ utils.log('click again')
+ utils.click($el)
+ setTimeout(() => {
+ utils.log('third click')
+ utils.click($el)
+ }, 20)
+ }, 10)
+ setTimeout(() => {
+ expect(spy_ajax).toHaveBeenCalledBefore(spy_byps)
+ expect(spy_shown).toHaveBeenCalled()
+ var $container = $('.tippy-popper .tippy-content')
+ expect($container.text()).toBe(
+ 'External content fetched via an HTTP request.')
+ done()
+ }, 600)
+ })
+
+ it('with hover', done => {
+ var $el = utils.createTooltip({
+ data: 'source: ajax; trigger: hover',
+ href: 'tests/content.html#content'
+ }),
+ spy_ajax = spyOn(pattern, _OAC).and.callThrough(),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough(),
+ spy_byps = spyOn(pattern, _OAB).and.callThrough()
+
+ utils.log('pattern init')
+ pattern.init($el)
+ utils.mouseenter($el)
+ setTimeout(() => {
+ utils.log('leaving')
+ utils.mouseleave($el)
+ setTimeout(() => {
+ utils.log('entering')
+ utils.mouseenter($el)
+ }, 20)
+ }, 10)
+ setTimeout(() => {
+ expect(spy_ajax).toHaveBeenCalledBefore(spy_byps)
+ expect(spy_shown).toHaveBeenCalled()
+ var $container = $('.tippy-popper .tippy-content')
+ expect($container.text()).toBe(
+ 'External content fetched via an HTTP request.')
+ done()
+ }, 600)
+ })
+ })
+
+ describe('will not fetch again until ajax is answered', () => {
+ it('with click', done => {
+
+
+ var $el = utils.createTooltip({
+ data: 'source: ajax; trigger: click',
+ href: 'tests/content.html#content'
+ }),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough(),
+ spy_byps = spyOn(pattern, _OAB).and.callThrough(),
+ spy_cset = spyOn(pattern, _OACS).and.callThrough()
+
+ spyOn(pattern, _OAC).and.callFake(
+ utils.delayed(_OAC, 500)
+ )
+ utils.log('pattern init')
+ pattern.init($el)
+ utils.click($el)
+ expect(spy_cset).not.toHaveBeenCalled()
+ setTimeout(() => {
+ utils.log('click again')
+ utils.click($el)
+ setTimeout(() => {
+ utils.log('third click')
+ utils.click($el)
+ }, 20)
+ }, 10)
+ // second click
+ setTimeout(() => {
+ expect(spy_byps).toHaveBeenCalledBefore(spy_cset)
+ expect(spy_shown).toHaveBeenCalled()
+ var $container = $('.tippy-popper .tippy-content')
+ expect($container.text()).toBe(
+ 'External content fetched via an HTTP request.')
+ done()
+ }, 600)
+ })
+
+ it('with hover', done => {
+ var $el = utils.createTooltip({
+ data: 'source: ajax; trigger: hover',
+ href: 'tests/content.html#content'
+ }),
+ spy_shown = spyOn(pattern, _OSN).and.callThrough(),
+ spy_byps = spyOn(pattern, _OAB).and.callThrough(),
+ spy_cset = spyOn(pattern, _OACS).and.callThrough()
+
+ spyOn(pattern, _OAC).and.callFake(
+ utils.delayed(_OAC, 500)
+ )
+ utils.log('pattern init')
+ pattern.init($el)
+ utils.mouseenter($el)
+ expect(spy_cset).not.toHaveBeenCalled()
+ setTimeout(() => {
+ utils.log('leaving')
+ utils.mouseleave($el)
+ setTimeout(() => {
+ utils.log('entering')
+ utils.mouseenter($el)
+ }, 20)
+ }, 10)
+ setTimeout(() => {
+ expect(spy_byps).toHaveBeenCalledBefore(spy_cset)
+ expect(spy_shown).toHaveBeenCalled()
+ var $container = $('.tippy-popper .tippy-content')
+ expect($container.text()).toBe(
+ 'External content fetched via an HTTP request.')
+ done()
+ }, 600)
+ })
+ })
+ })
+
+ })
+})
+// jshint indent: 4, browser: true, jquery: true, quotmark: single
+/*global describe, it, beforeEach, afterEach, spyOn, afterAll, expect */
diff --git a/src/pat/tooltip-ng/tooltip-ng.js b/src/pat/tooltip-ng/tooltip-ng.js
new file mode 100644
index 000000000..2cee62279
--- /dev/null
+++ b/src/pat/tooltip-ng/tooltip-ng.js
@@ -0,0 +1,454 @@
+(function (root, factory) {
+ // We use AMD (Asynchronous Module Definition) or browser globals to create
+ // this module.
+ if (typeof define === 'function' && define.amd) {
+ define([
+ 'jquery',
+ 'pat-base',
+ 'pat-registry',
+ 'pat-utils',
+ 'pat-parser',
+ 'pat-markdown',
+ 'pat-logger',
+ 'tippy.js',
+ 'tippy-theme.css',
+ ], function() {
+ return factory.apply(this, arguments)
+ })
+ } else {
+ // If require.js is not available, you'll need to make sure that these
+ // global variables are available.
+// factory($, patterns.Base, patterns, patterns.Parser, patterns.logger, tippy, tippytheme)
+ factory($, patterns.Base, patterns, patterns.utils, patterns.Parser, patterns.markdown, patterns.logger, tippy)
+ }
+//}(this, function($, Base, registry, Parser, logger, tippy, tippytheme) {
+}(this, function($, Base, registry, utils, Parser, Markdown, logger, tippy) {
+ 'use strict'
+
+ let start = 0
+ const log = logger.getLogger('pat-tooltip-ng'),
+ timelog = msg => { log.debug(`${Date.now() - start} ${msg}`) }
+
+ log.setLevel(20)
+ timelog('Initializing pat-tooltip-ng')
+
+ /* For logging, you can call log.debug, log.info, log.warn, log.error and log.fatal.
+ *
+ * For more information on how to use the logger and how to view log messages, please read:
+ * https://github.com/Patternslib/logging
+ */
+
+ let parser = new Parser('tooltip-ng')
+ /* If you'd like your pattern to be configurable via the
+ * data-pat-tooltip-ng attribute, then you need to
+ * specify the available arguments here, by calling parser.addArgument.
+ *
+ * The addArgument method takes the following parameters:
+ * - name: The required name of the pattern property which you want to make
+ * configurable.
+ * - default_value: An optional default string value of the property if the user
+ * doesn't provide one.
+ * - choices: An optional set (Array) of values that the property might take.
+ * If specified, values outside of this set will not be accepted.
+ * - multiple: An optional boolean value which specifies wether the
+ * property can be multivalued or not.
+ *
+ * For example:
+ * parser.addArgument('color', 'blue', ['red', 'green', 'blue'], false)
+ */
+ const all_positions = ['tl', 'tm', 'tr',
+ 'rt', 'rm', 'rb',
+ 'br', 'bm', 'bl',
+ 'lb', 'lm', 'lt']
+ parser.addArgument('position-list', [], all_positions, true)
+ parser.addArgument('position-policy', 'auto', ['auto', 'force'])
+ parser.addArgument('trigger', 'click', ['click', 'hover'])
+ parser.addArgument('source', 'title', ['auto', 'ajax', 'content', 'content-html', 'title'])
+ parser.addArgument('ajax-data-type', 'html', ['html', 'markdown'])
+ parser.addArgument('delay')
+ parser.addArgument('mark-inactive', true)
+ parser.addArgument('class')
+ parser.addArgument('target', 'body')
+
+
+ //return Base.extend({
+ let tooltip = {
+ /* The name is used to store the pattern in a registry and needs to be
+ * unique.
+ */
+ name: 'tooltip-ng',
+ /* The trigger specifies the selector (CSS or jQuery) which Patternslib
+ * will scan for in order to identifiy and initialize this pattern.
+ */
+ trigger: '.pat-tooltip-ng',
+
+ jquery_plugin: true,
+
+ tippy: tippy.default,
+
+ init($el, opts, debuglevel=20) {
+ log.setLevel(debuglevel)
+
+ return $el.each(function() {
+ this.defaults = {
+ 'allowHTML': true,
+ 'animation': 'fade',
+ 'arrow': true,
+ //'delay': [0, 1800],
+ //'duration': [325, 275],
+ 'flipOnUpdate': true,
+ 'ignoreAttributes': true,
+ 'interactive': false,
+ 'onHidden': tooltip._onHidden,
+ 'onHide': tooltip._onHide,
+ 'onMount': tooltip._onMount,
+ 'onShow': tooltip._onShow,
+ 'onShown': tooltip._onShown,
+ 'onTrigger': tooltip._onTrigger,
+ 'theme': 'light-border',
+ 'trigger': 'click'
+ }
+
+ start = Date.now()
+ const tippy = tooltip.tippy,
+ $trigger = $(this)
+
+ tippy.setDefaults(this.defaults)
+ this.options = parser.parse($trigger, opts)
+
+ /* this.options will now contain the configured pattern properties
+ * you've registered with the parser.addArgument method.
+ *
+ * If the user provided any values via the data-pat-tooltip-ng
+ * attribute, those values will already be set.
+ */
+
+ $trigger.data('patterns.tooltip-ng', tooltip._mutateOptions(this.options))
+ .on('destroy.pat-tooltip-ng', tooltip._onDestroy)
+
+ this.options = tooltip.parseOptionsForTippy(this.options, $trigger)
+ tippy($trigger[0], this.options)
+ tooltip.setupShowEvents($trigger)
+ })
+ },
+
+ parseOptionsForTippy(opts, $trigger) {
+ const notImplemented = (name) => { log.error(`${name} not implemented`) },
+
+ placement = (pos) => {
+ const primary = (pos) => ({
+ t: 'bottom',
+ r: 'left',
+ b: 'top',
+ l: 'right',
+ }[pos])
+
+ const secondary = (pos) => ({
+ l: '-start',
+ r: '-end',
+ m: '',
+ t: '-start',
+ b: '-end',
+ }[pos])
+
+ return `${primary(pos[0])}${secondary(pos[1])}`
+ },
+
+ flipBehavior = (pos) => placement(`${pos[0]}m`),
+
+ parsers = {
+ position() {
+ if (opts.hasOwnProperty('position')) {
+ const prefs = opts.position.list
+ if (prefs.length > 0) {
+ const pos = prefs[0]
+ opts.placement = placement(pos)
+
+ if (prefs.length > 1) {
+ opts.flipBehavior = prefs.map(flipBehavior)
+ opts.flip = true
+ opts.flipOnUpdate = true
+ }
+ }
+ if (opts.position.policy === 'force') {
+ opts.flip = false
+ opts.flipOnUpdate = false
+ }
+ delete opts.position
+ }
+ },
+
+ height: notImplemented,
+
+ trigger() {
+ if (opts.trigger === 'hover') {
+ opts.trigger = 'mouseenter focus'
+ }
+ },
+
+ closing: notImplemented,
+
+ source() {
+ if (opts.hasOwnProperty('source')) {
+ if (opts.source==='title') {
+ opts.content = $trigger.attr('title')
+ }
+ if (opts.source === 'auto') {
+ const href = $trigger.attr('href')
+ if (typeof(href) !== 'string') {
+ log.error(`href must be specified if 'source' is set to 'auto'`)
+ return
+ }
+ if (href.indexOf('#') === 0) {
+ tooltip._setSource(opts, 'content')
+ } else {
+ tooltip._setSource(opts, 'ajax')
+ }
+ }
+ if (opts.source==='content') {
+ const href = $trigger.attr('href'),
+ is_string = typeof(href) === 'string',
+ has_hash = href.indexOf('#') !== -1,
+ has_more = href.length > 1
+ let $content
+
+ if (is_string && has_hash && has_more) {
+ $content = $('#'+href.split('#')[1]).children().clone()
+ } else {
+ $content = $trigger.children().clone()
+ if (!$content.length) {
+ $content = $('').text($trigger.text())
+ }
+ }
+ opts.content = $content[0]
+ registry.scan($content[0])
+ }
+ if (opts.source==='ajax') {
+ const $p = $('')[0]
+
+ opts.content = $p
+ opts.onShow = tooltip._onAjax($trigger)
+ opts.onHidden = instance => {
+ timelog('ONAJAXHIDDEN')
+ instance.setContent($p)
+ instance.state.ajax.canFetch = true
+ }
+ }
+ delete opts.source
+ }
+ },
+
+ ajaxDataType() {
+ delete opts.ajaxDataType
+ },
+
+ delay() {
+ if (opts.hasOwnProperty('delay')) {
+ opts.delay = [utils.parseTime(opts.delay), 0]
+ }
+ },
+
+ markInactive() {
+ if (opts.markInactive) {
+ $trigger.addClass('inactive')
+ }
+ delete opts.markInactive
+ },
+
+ 'class': () => {
+ if (opts.hasOwnProperty('class')) {
+ const klass = opts.class,
+ handler = tooltip._addClassHandler(klass)
+
+ $trigger.on('pat-tippy-mount', handler)
+ delete opts.class
+ }
+ },
+
+ target() {
+ if (opts.hasOwnProperty('target')) {
+ if (opts.target === 'parent') {
+ opts.appendTo = 'parent'
+ } else if (opts.target !== 'body') {
+ opts.appendTo = $(opts.target)[0]
+ }
+ delete opts.target
+ }
+ }
+ }
+
+ for (let arg in opts) {
+ switch (arg) {
+ case 'ajax-data-type':
+ arg = 'ajaxDataType'
+ break
+ case 'mark-inactive':
+ arg = 'markInactive'
+ break
+ }
+ log.debug(arg)
+ parsers[arg](arg)
+ }
+
+ if ($trigger.attr('title')) {
+ $trigger.removeAttr('title')
+ }
+ return opts
+ },
+
+ setupShowEvents($trigger) {
+ $trigger.on('click.pat-tooltip-ng', tooltip.blockDefault)
+ },
+
+ removeShowEvents($trigger) {// jshint ignore:line
+ },
+
+ setupHideEvents($trigger) {
+ $trigger.on('click.pat-tooltip-ng', tooltip.blockdefault)
+ },
+
+ removeHideEvents($trigger) {// jshint ignore:line
+ },
+
+ blockDefault(event) {
+ if (event.preventDefault) {
+ event.preventDefault()
+ }
+ },
+
+ _mutateOptions(opts) {
+ // shallow copy
+ return Object.assign({}, opts)
+ },
+
+ _addClassHandler(klass) {
+ return (event, tooltip) => { $(tooltip).addClass(klass) }
+ },
+
+ _setSource(opts, source) {
+ opts.source = source
+ },
+
+ _onDestroy(event) {
+ timelog('ONDESTROY')
+ const $trigger = event.target
+ $trigger._tippy.destroy()
+ },
+
+ _onClick(instance, event) {
+ timelog('ONCLICK')
+ if (event.type === 'click') {
+ timelog(`it's click`)
+ event.stopPropagation()
+ event.preventDefault()
+ }
+ },
+
+ _onTrigger(instance, event) {// jshint ignore:line
+ timelog('ONTRIGGER')
+ },
+
+ _onMount(instance) {
+ timelog('ONMOUNT')
+ $(instance.reference).trigger('pat-tippy-mount', instance.popperChildren.tooltip)
+ },
+
+ _onShow(instance) {// jshint ignore:line
+ timelog('ONSHOW')
+ },
+
+ _onShown(instance) {
+ timelog('ONSHOWN')
+ const $trigger = $(instance.reference)
+ const options = $trigger.data('patterns.tooltip-ng')
+ tooltip.removeShowEvents($trigger)
+ tooltip.setupHideEvents($trigger)
+ if (options.markInactive) {
+ $trigger.removeClass('inactive').addClass('active')
+ }
+ },
+
+ _onHide(instance) {
+ timelog('ONHIDE')
+ const $trigger = $(instance.reference)
+ tooltip.removeHideEvents($trigger)
+ tooltip.setupShowEvents($trigger)
+ },
+
+ _onHidden(instance) { // jshint ignore:line
+ timelog('ONHIDDEN')
+ const $trigger = $(instance.reference)
+ const options = $trigger.data('patterns.tooltip-ng')
+ if (options.markInactive) {
+ $trigger.removeClass('active').addClass('inactive')
+ }
+ },
+
+ _onAjax($trigger) {
+ timelog('OnAJAX')
+ const source = $trigger.attr('href').split('#')
+ return instance => {
+ timelog('in ajax content function')
+ timelog(`instance.state.ajax ${JSON.stringify(instance.state.ajax)}`)
+ if (instance.state.ajax === undefined) {
+ instance.state.ajax = {
+ isFetching : false,
+ canFetch : true
+ }
+ }
+
+ if (instance.state.ajax.isFetching || !instance.state.ajax.canFetch) {
+ return tooltip._onAjaxBypass()
+ }
+
+ instance.state.ajax = {
+ isFetching: true,
+ canFetch: false
+ }
+ tooltip._onAjaxCallback(instance, source)
+ }
+ },
+
+ _onAjaxCallback(instance, src) {
+ timelog('AJAXCALLBACK')
+ const $trigger = $(instance.reference),
+ options = $trigger.data('patterns.tooltip-ng'),
+ handler = tooltip._ajaxDataTypeHandlers[options.ajaxDataType]
+ fetch(src[0]).then(response => {
+ return response.text().then(text => {
+ instance.setContent(handler(text, src))
+ }).finally(() => {
+ tooltip._onAjaxContentSet(instance)
+ })
+ })
+ },
+
+ _onAjaxBypass() {
+ timelog('AJAX BYPASSED')
+ return undefined
+ },
+
+ _onAjaxContentSet(instance) {
+ timelog('AJAXCONTENTSET')
+ instance.state.ajax.isFetching = false
+ },
+
+ _ajaxDataTypeHandlers: {
+ html(text, src) {
+ const $tmp = $('').append($.parseHTML(text))
+ return $tmp.find(`#${src[1]}`)[0]
+ },
+
+ markdown(text, src) {
+ const [url, source] = src,
+ cfg = { url, source: `#${source}` },
+ pat = Markdown.init($(''))
+ return pat.renderForInjection(cfg, text)[0]
+ }
+ }
+ }
+
+ registry.register(tooltip)
+ return tooltip
+}))
+/*global $, patterns, tippy */
diff --git a/src/pat/tooltip/documentation.md b/src/pat/tooltip/documentation.md
index beee77870..635cac897 100644
--- a/src/pat/tooltip/documentation.md
+++ b/src/pat/tooltip/documentation.md
@@ -15,14 +15,14 @@ Tooltips are intended to display contextual information and function about the t
### Display
-Tooltips are shown when the mouse hovers over the triggering element,
-and are hidden when the mouse leaves the triggering element.
+Tooltips are shown when clicking on the triggering element.
-The trigger can be changed to require a click on the triggering element
-by adding a `click` option.
+The trigger can be changed to require the mouse to hover over the triggering element,
+and to be hidden when the mouse leaves the triggering element
+by adding a `hover` option.
More information
+ class="pat-tooltip" data-pat-tooltip="hover">More information
### Positioning
@@ -38,16 +38,16 @@ The position of the tip within the tooltip can be specified with a
formatted as `[,preference]*`. The possible preferences are:
- `tl`: tip placed at the leftmost corner of the top side of the tooltip
-- `tm`: tip placed at the middle of the top side tooltip
+- `tm`: tip placed at the middle of the top side of the tooltip
- `tr`: tip placed at the rightmost corner of the top side of the tooltip
- `rt`: tip placed at the top corner of the right side of the tooltip
-- `rm`: tip placed at the middle of the right side tooltip
+- `rm`: tip placed at the middle of the right side of the tooltip
- `rb`: tip placed at the bottom corner of the right side of the tooltip
- `bl`: tip placed at the leftmost corner of the bottom side of the tooltip
-- `bm`: tip placed at the middle of the bottom side tooltip
+- `bm`: tip placed at the middle of the bottom side of the tooltip
- `br`: tip placed at the rightmost corner of the bottom side of the tooltip
- `lt`: tip placed at the top corner of the left side of the tooltip
-- `lm`: tip placed at the middle of the left side tooltip
+- `lm`: tip placed at the middle of the left side of the tooltip
- `lb`: tip placed at the bottom corner of the left side of the tooltip
An example:
@@ -60,7 +60,7 @@ This specifies that the preferred position of the tip is at the top left
side of the tooltip (placing the tooltip itself on the right side of the
link). Notice that the position is the first defined property so you can use
shorthand syntax directly. If the tooltip does not fit at that position
-then the left-middle position is tried, and then the the right-top or if all
+then the left-middle position is tried, and then the right-top or if all
previous options failed the middle of the right side is tried. If the tooltip
does not fit at any of the preferred positions, then it will be
positioned at the location that has the most space, even if this is not
@@ -94,7 +94,7 @@ For example, setting closing to `sticky`:
### AJAX tooltips
-The tooltip content can be loaded via an AJAX request by proving an ajax
+The tooltip content can be loaded via an AJAX request by providing an ajax
option:
@@ -137,7 +137,7 @@ for tooltips which fetch their content with an AJAX call the tooltip may
be temporarily shown with a progress indicator:
…