├── .editorconfig
├── .gitignore
├── .npmignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── dist
├── hint.css
├── hint.js
├── hint.min.css
└── hint.min.js
├── gulpfile.js
├── package-lock.json
├── package.json
├── resources
└── hint.png
└── src
├── events.js
├── hint.js
├── hint.styl
└── tooltip.styl
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | charset = utf-8
9 | trim_trailing_whitespace = true
10 | insert_final_newline = true
11 |
12 | [*.md]
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .DS_Store
3 |
--------------------------------------------------------------------------------
/.npmignore:
--------------------------------------------------------------------------------
1 | resources
2 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # v3.0.0 Change of Name
2 |
3 | - Renamed to `@bevacqua/hint`
4 |
5 | # v2.0.0 Change of Stylus
6 |
7 | - Changed Stylus variables `ht-brown` and `ht-pink` into `ht-background` and `ht-border` respectively
8 |
9 | # v1.5.0 Boarding Pass
10 |
11 | - Remove `data-hint-before` in favor of reusing `data-hint` and the `hint-before` class
12 | - Remove `opacity` transitions in favor of `display: none` and `display: block`, fixing a content `overflow` bug
13 | - Introduced a JavaScript enhancement that allows hints to be constrained to the visible viewport
14 | - Moved `opacity` transition animation to JavaScript for better progressive enhancement
15 |
16 | # v1.3.1 Boring Panda
17 |
18 | - No delays on hide
19 |
20 | # v1.3.0 Obnoxious Boar
21 |
22 | - Introduced a `1s` delay before popping up hints
23 |
24 | # v1.2.2 Before Dawn
25 |
26 | - Introduce ability to add `:before` hints using `aria-label`
27 |
28 | # v1.2.1 Hot Pink
29 |
30 | - Changed prefix of color variables
31 |
32 | # v1.2.0 101 Dalmatians
33 |
34 | - Using `top: 101%` is better most of the time
35 |
36 | # v1.1.15 Nudity Is Bad
37 |
38 | - Include `nib` during stand-alone builds for cross-browser compatibility
39 |
40 | # v1.1.0 Accessible King
41 |
42 | - `aria-label` attributes also displayed as hints
43 | - Slight change to make responsiveness better around breakpoint
44 |
45 | # v1.0.2 Take a Hint
46 |
47 | - Initial Public Release
48 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright © 2014 Nicolas Bevacqua
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # @bevacqua/hint
2 |
3 | > Awesome pure CSS tooltips at your fingertips
4 |
5 | # Install
6 |
7 | Using Bower
8 |
9 | ```shell
10 | bower install -S @bevacqua/hint
11 | ```
12 |
13 | Using `npm`
14 |
15 | ```shell
16 | npm install -S @bevacqua/hint
17 | ```
18 |
19 | # Usage
20 |
21 | Just give your elements a nice tooltip in HTML. When hovered, the hint will appear.
22 |
23 | ```html
24 | Foo Bar
25 | ```
26 |
27 | You'll get a nice little tooltip. Remember to include the CSS in your styles!
28 |
29 | ![hint.png][1]
30 |
31 | By default, the `:after` pseudo-selector is used. This means you can use the `:before` pseudo-selector for your own purposes. If you want the `data-hint` to use `:before`, then you must use the `hint-before` class on the element as well.
32 |
33 | ```html
34 | Foo Bar
35 | ```
36 |
37 | You can also use the [`aria-label`][2] attribute.
38 |
39 | ```html
40 | Foo Bar
41 | ```
42 |
43 | If you want the `aria-label` hint to use `:before`, then you must use the `hint-before` class on the element as well.
44 |
45 | ```html
46 | Foo Bar
47 | ```
48 |
49 | Hints have a `z-index` of `5000`.
50 |
51 | # JavaScript
52 |
53 | The CSS will only get us so far, and we must add a tiny bit of JavaScript if we want a little functionality. This is not critical to `@bevacqua/hint`, and is therefore considered an optional feature. The JavaScript code enables the following features.
54 |
55 | - Hints are docked to the visible viewport so that they aren't cut off when they're near the edge
56 | - If hints are even wider than the viewport itself, then they are rendered in multi-line, setting the max width to the viewport width
57 | - You can define a maximum width to avoid hard-to-read long hints in wide viewports
58 | - When the JavaScript snippet is used, hints transition into view a second after the target element is hovered
59 |
60 | To include the JavaScript, just use the following snippet if you're using CommonJS, or refer to the `dist` directory for the compiled distributions.
61 |
62 | ```js
63 | require('@bevacqua/hint');
64 | ```
65 |
66 | To set the maximum hint width, do:
67 |
68 | ```js
69 | require('@bevacqua/hint').maximumWidth = 650;
70 | ```
71 |
72 | You can also set it to 'auto', which means the full viewport size will be used if the tooltip exceeds the viewport size in length. In practice, `'auto'` means `Infinity` will be used. The default `maximumWidth` value is `650` pixels wide.
73 |
74 | # License
75 |
76 | MIT
77 |
78 | [1]: http://i.imgur.com/EFP5j4E.png
79 | [2]: https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-label_attribute
80 |
--------------------------------------------------------------------------------
/dist/hint.css:
--------------------------------------------------------------------------------
1 | /**
2 | * @bevacqua/hint - Awesome tooltips at your fingertips
3 | * @version v3.0.3
4 | * @link https://github.com/bevacqua/hint
5 | * @license MIT
6 | */
7 | [aria-label],
8 | [data-hint] {
9 | position: relative;
10 | }
11 | [aria-label]:not(.hint-before):after,
12 | [data-hint]:not(.hint-before):after,
13 | [aria-label].hint-before:before,
14 | [data-hint].hint-before:before {
15 | display: none;
16 | position: absolute;
17 | top: 101%;
18 | left: 6px;
19 | z-index: 5000;
20 | pointer-events: none;
21 | padding: 8px 10px;
22 | line-height: 15px;
23 | white-space: nowrap;
24 | text-decoration: none;
25 | text-indent: 0;
26 | overflow: visible;
27 | font-size: 12px;
28 | font-weight: normal;
29 | color: #fff;
30 | text-shadow: 1px 0px 1px #888;
31 | background-color: #412917;
32 | border-left: 6px solid #d37092;
33 | border-radius: 2px;
34 | -webkit-box-shadow: 1px 2px 6px rgba(0,0,0,0.3);
35 | box-shadow: 1px 2px 6px rgba(0,0,0,0.3);
36 | }
37 | [aria-label]:not(.hint-before):hover:after,
38 | [data-hint]:not(.hint-before):hover:after,
39 | [aria-label].hint-before:hover:before,
40 | [data-hint].hint-before:hover:before {
41 | display: block;
42 | -webkit-transform: translateY(8px);
43 | -moz-transform: translateY(8px);
44 | -o-transform: translateY(8px);
45 | -ms-transform: translateY(8px);
46 | transform: translateY(8px);
47 | }
48 | [aria-label]:not(.hint-before):after,
49 | [aria-label].hint-before:before {
50 | content: attr(aria-label);
51 | }
52 | [data-hint]:not(.hint-before):after {
53 | content: attr(data-hint);
54 | }
55 | [data-hint].hint-before:before {
56 | content: attr(data-hint-before);
57 | }
58 | @media only print {
59 | [aria-label]:not(.hint-before):after,
60 | [data-hint]:not(.hint-before):after,
61 | [aria-label].hint-before:before,
62 | [data-hint].hint-before:before {
63 | display: none;
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/dist/hint.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @bevacqua/hint - Awesome tooltips at your fingertips
3 | * @version v3.0.3
4 | * @link https://github.com/bevacqua/hint
5 | * @license MIT
6 | */
7 | (function(f){if(typeof exports==="object"&&typeof module!=="undefined"){module.exports=f()}else if(typeof define==="function"&&define.amd){define([],f)}else{var g;if(typeof window!=="undefined"){g=window}else if(typeof global!=="undefined"){g=global}else if(typeof self!=="undefined"){g=self}else{g=this}g.hint = f()}})(function(){var define,module,exports;return (function(){function r(e,n,t){function o(i,f){if(!n[i]){if(!e[i]){var c="function"==typeof require&&require;if(!f&&c)return c(i,!0);if(u)return u(i,!0);var a=new Error("Cannot find module '"+i+"'");throw a.code="MODULE_NOT_FOUND",a}var p=n[i]={exports:{}};e[i][0].call(p.exports,function(r){var n=e[i][1][r];return o(n||r)},p,p.exports,r,e,n,t)}return n[i].exports}for(var u="function"==typeof require&&require,i=0;i>> 0;
18 | if (typeof fn !== 'function') {
19 | throw new TypeError();
20 | }
21 |
22 | var res = new Array(len);
23 | var ctx = arguments.length >= 2 ? arguments[1] : void 0;
24 | var i;
25 | for (i = 0; i < len; i++) {
26 | if (i in t) {
27 | res[i] = fn.call(ctx, t[i], i, t);
28 | }
29 | }
30 |
31 | return res;
32 | };
33 | }
34 |
35 | },{}],2:[function(require,module,exports){
36 | 'use strict';
37 |
38 | require('./object-keys');
39 | require('./array-map');
40 |
41 | var camel = /([a-z])([A-Z])/g;
42 | var hyphens = '$1-$2';
43 | var contexts = {};
44 |
45 | function parseStyles (styles) {
46 | if (typeof styles === 'string') {
47 | return styles;
48 | }
49 | if (Object.prototype.toString.call(styles) !== '[object Object]') {
50 | return '';
51 | }
52 | return Object.keys(styles).map(function (key) {
53 | var prop = key.replace(camel, hyphens).toLowerCase();
54 | return prop + ':' + styles[key];
55 | }).join(';');
56 | }
57 |
58 | function context (name) {
59 | if (contexts[name]) {
60 | return contexts[name];
61 | }
62 | var cache;
63 | var rules;
64 | var remove;
65 |
66 | function getStylesheet () {
67 | if (cache) {
68 | return cache;
69 | }
70 | var style = document.createElement('style');
71 | document.body.appendChild(style);
72 | style.setAttribute('data-context', name);
73 | cache = document.styleSheets[document.styleSheets.length - 1];
74 | rules = cache.cssRules ? 'cssRules' : 'rules';
75 | remove = cache.removeRule ? 'removeRule' : 'deleteRule';
76 | return cache;
77 | }
78 |
79 | function add (selector, styles) {
80 | var css = parseStyles(styles);
81 | var sheet = getStylesheet();
82 | var len = sheet[rules].length;
83 | if (sheet.insertRule) {
84 | sheet.insertRule(selector + '{' + css + '}', len);
85 | } else if (sheet.addRule) {
86 | sheet.addRule(selector, css, len);
87 | }
88 | }
89 |
90 | function remove (selector) {
91 | var sheet = getStylesheet();
92 | var length = sheet[rules].length;
93 | var i;
94 | for (i = length - 1; i >= 0; i--) {
95 | if (sheet[rules][i].selectorText === selector) {
96 | sheet[remove](i);
97 | }
98 | }
99 | }
100 |
101 | function clear () {
102 | var sheet = getStylesheet();
103 | while (sheet[rules].length) {
104 | sheet[remove](0);
105 | }
106 | }
107 |
108 | add.clear = clear;
109 | add.remove = remove;
110 | contexts[name] = add;
111 | return contexts[name];
112 | }
113 |
114 | var ctx = context('default');
115 | ctx.context = context;
116 | module.exports = ctx;
117 |
118 | },{"./array-map":1,"./object-keys":3}],3:[function(require,module,exports){
119 | if (!Object.keys) {
120 | Object.keys = (function () {
121 | 'use strict';
122 | var hasOwnProperty = Object.prototype.hasOwnProperty;
123 | var hasDontEnumBug = !({toString: null}).propertyIsEnumerable('toString');
124 | var dontEnums = [
125 | 'toString',
126 | 'toLocaleString',
127 | 'valueOf',
128 | 'hasOwnProperty',
129 | 'isPrototypeOf',
130 | 'propertyIsEnumerable',
131 | 'constructor'
132 | ];
133 | var dontEnumsLength = dontEnums.length;
134 |
135 | return function (obj) {
136 | if (typeof obj !== 'object' && (typeof obj !== 'function' || obj === null)) {
137 | throw new TypeError('Object.keys called on non-object');
138 | }
139 |
140 | var result = [];
141 | var prop;
142 | var i;
143 |
144 | for (prop in obj) {
145 | if (hasOwnProperty.call(obj, prop)) {
146 | result.push(prop);
147 | }
148 | }
149 |
150 | if (hasDontEnumBug) {
151 | for (i = 0; i < dontEnumsLength; i++) {
152 | if (hasOwnProperty.call(obj, dontEnums[i])) {
153 | result.push(dontEnums[i]);
154 | }
155 | }
156 | }
157 | return result;
158 | };
159 | }());
160 | }
161 |
162 | },{}],4:[function(require,module,exports){
163 | 'use strict';
164 |
165 | var addEvent = addEventEasy;
166 | var removeEvent = removeEventEasy;
167 |
168 | if (!window.addEventListener) {
169 | addEvent = addEventHard;
170 | }
171 |
172 | if (!window.removeEventListener) {
173 | removeEvent = removeEventHard;
174 | }
175 |
176 | function addEventEasy (element, evt, fn) {
177 | return element.addEventListener(evt, fn);
178 | }
179 |
180 | function addEventHard (element, evt, fn) {
181 | return element.attachEvent('on' + evt, function (e) {
182 | e = e || window.event;
183 | e.target = e.target || e.srcElement;
184 | e.preventDefault = e.preventDefault || function preventDefault () { e.returnValue = false; };
185 | e.stopPropagation = e.stopPropagation || function stopPropagation () { e.cancelBubble = true; };
186 | fn.call(element, e);
187 | });
188 | }
189 |
190 | function removeEventEasy (element, evt, fn) {
191 | return element.removeEventListener(evt, fn);
192 | }
193 |
194 | function removeEventHard (element, evt, fn) {
195 | return element.detachEvent('on' + evt, fn);
196 | }
197 |
198 | module.exports = {
199 | add: addEvent,
200 | remove: removeEvent
201 | };
202 |
203 | },{}],5:[function(require,module,exports){
204 | 'use strict';
205 |
206 | var insertRule = require('insert-rule').context('hint');
207 | var events = require('./events');
208 | var expando = 'hint-' + Date.now();
209 | var api = {
210 | maximumWidth: 650
211 | };
212 |
213 | events.add(document.body, 'mouseover', enter);
214 | events.add(document.body, 'mouseout', leave);
215 |
216 | function enter (e) {
217 | var prev = scan(e.fromElement);
218 | var elem = scan(e.target);
219 | if (elem && elem !== prev) {
220 | move(elem);
221 | }
222 | }
223 |
224 | function leave (e) {
225 | var next = scan(e.toElement);
226 | var elem = scan(e.target);
227 | if (elem && elem !== next) {
228 | clear(elem);
229 | }
230 | }
231 |
232 | function scan (elem) {
233 | while (elem) {
234 | if (probe(elem)) {
235 | return elem;
236 | }
237 | elem = elem.parentElement;
238 | }
239 | }
240 |
241 | function probe (elem) {
242 | return elem.getAttribute('aria-label') || elem.getAttribute('data-hint');
243 | }
244 |
245 | function expandex () {
246 | return expando + Date.now();
247 | }
248 |
249 | function i (px) {
250 | var raw = px.replace('px', '');
251 | return parseInt(raw, 10);
252 | }
253 |
254 | function getPseudo (elem) {
255 | return elem.className.indexOf('hint-before') !== -1 ? ':before' : ':after';
256 | }
257 |
258 | function move (elem) {
259 | var totalWidth = innerWidth;
260 | var pseudo = getPseudo(elem);
261 | var c = getComputedStyle(elem, pseudo);
262 | var existed = elem.getAttribute('id');
263 | var id = existed || expandex();
264 | if (id.indexOf(expando) === 0) {
265 | elem.setAttribute('id', id);
266 | }
267 |
268 | var selector = '#' + id + pseudo;
269 | var rect = elem.getBoundingClientRect();
270 | var paddings = i(c.paddingLeft) + i(c.paddingRight) + i(c.marginLeft) + i(c.marginRight);
271 | var width;
272 | var left;
273 | var right = rect.left + i(c.width) + i(c.left) + paddings;
274 | var collapse;
275 | var offset;
276 | var margin = 20;
277 | var max = api.maximumWidth === 'auto' ? Infinity : api.maximumWidth;
278 |
279 | if (i(c.width) + margin > totalWidth) {
280 | collapse = totalWidth - margin > max;
281 | width = collapse ? max : totalWidth - margin;
282 | offset = collapse ? totalWidth - max - margin : 0;
283 | left = -(rect.left + paddings) + offset;
284 |
285 | insertRule(selector, {
286 | width: width + 'px',
287 | left: left - 15 + 'px',
288 | whiteSpace: 'inherit'
289 | });
290 | } else if (right + margin > totalWidth) {
291 | insertRule(selector, {
292 | left: totalWidth - right + margin + 'px'
293 | });
294 | }
295 | transparent();
296 | setTimeout(opaque, 1000);
297 |
298 | function transparent () {
299 | insertRule(selector, { opacity: 0 });
300 | }
301 | function opaque () {
302 | insertRule(selector, transition({
303 | opacity: 1
304 | }));
305 | }
306 | function transition (css) {
307 | var rule = 'transition';
308 | var prefixes = ['', '-webkit-', '-moz-', '-o-', '-ms-'];
309 | function add (prefix) {
310 | css[prefix + rule] = 'opacity 0.3s ease-in-out';
311 | }
312 | prefixes.forEach(add);
313 | return css;
314 | }
315 | }
316 |
317 | function clear (elem) {
318 | var pseudo = getPseudo(elem);
319 | var id = elem.id;
320 | if (id.indexOf(expando) === 0) {
321 | elem.removeAttribute('id');
322 | }
323 | var selector = '#' + id + pseudo;
324 | insertRule.remove(selector);
325 | }
326 |
327 | module.exports = api;
328 |
329 | },{"./events":4,"insert-rule":2}]},{},[5])(5)
330 | });
331 |
332 | //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCJub2RlX21vZHVsZXMvaW5zZXJ0LXJ1bGUvc3JjL2FycmF5LW1hcC5qcyIsIm5vZGVfbW9kdWxlcy9pbnNlcnQtcnVsZS9zcmMvaW5zZXJ0UnVsZS5qcyIsIm5vZGVfbW9kdWxlcy9pbnNlcnQtcnVsZS9zcmMvb2JqZWN0LWtleXMuanMiLCJzcmMvZXZlbnRzLmpzIiwic3JjL2hpbnQuanMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7QUNBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDMUJBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQ2pGQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUMxQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FDdkNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EiLCJmaWxlIjoiZ2VuZXJhdGVkLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXNDb250ZW50IjpbIihmdW5jdGlvbigpe2Z1bmN0aW9uIHIoZSxuLHQpe2Z1bmN0aW9uIG8oaSxmKXtpZighbltpXSl7aWYoIWVbaV0pe3ZhciBjPVwiZnVuY3Rpb25cIj09dHlwZW9mIHJlcXVpcmUmJnJlcXVpcmU7aWYoIWYmJmMpcmV0dXJuIGMoaSwhMCk7aWYodSlyZXR1cm4gdShpLCEwKTt2YXIgYT1uZXcgRXJyb3IoXCJDYW5ub3QgZmluZCBtb2R1bGUgJ1wiK2krXCInXCIpO3Rocm93IGEuY29kZT1cIk1PRFVMRV9OT1RfRk9VTkRcIixhfXZhciBwPW5baV09e2V4cG9ydHM6e319O2VbaV1bMF0uY2FsbChwLmV4cG9ydHMsZnVuY3Rpb24ocil7dmFyIG49ZVtpXVsxXVtyXTtyZXR1cm4gbyhufHxyKX0scCxwLmV4cG9ydHMscixlLG4sdCl9cmV0dXJuIG5baV0uZXhwb3J0c31mb3IodmFyIHU9XCJmdW5jdGlvblwiPT10eXBlb2YgcmVxdWlyZSYmcmVxdWlyZSxpPTA7aTx0Lmxlbmd0aDtpKyspbyh0W2ldKTtyZXR1cm4gb31yZXR1cm4gcn0pKCkiLCJpZiAoIUFycmF5LnByb3RvdHlwZS5tYXApIHtcbiAgQXJyYXkucHJvdG90eXBlLm1hcCA9IGZ1bmN0aW9uIChmbikge1xuICAgICd1c2Ugc3RyaWN0JztcblxuICAgIGlmICh0aGlzID09PSB2b2lkIDAgfHwgdGhpcyA9PT0gbnVsbCkge1xuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcigpO1xuICAgIH1cblxuICAgIHZhciB0ID0gT2JqZWN0KHRoaXMpO1xuICAgIHZhciBsZW4gPSB0Lmxlbmd0aCA+Pj4gMDtcbiAgICBpZiAodHlwZW9mIGZuICE9PSAnZnVuY3Rpb24nKSB7XG4gICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCk7XG4gICAgfVxuXG4gICAgdmFyIHJlcyA9IG5ldyBBcnJheShsZW4pO1xuICAgIHZhciBjdHggPSBhcmd1bWVudHMubGVuZ3RoID49IDIgPyBhcmd1bWVudHNbMV0gOiB2b2lkIDA7XG4gICAgdmFyIGk7XG4gICAgZm9yIChpID0gMDsgaSA8IGxlbjsgaSsrKSB7XG4gICAgICBpZiAoaSBpbiB0KSB7XG4gICAgICAgIHJlc1tpXSA9IGZuLmNhbGwoY3R4LCB0W2ldLCBpLCB0KTtcbiAgICAgIH1cbiAgICB9XG5cbiAgICByZXR1cm4gcmVzO1xuICB9O1xufVxuIiwiJ3VzZSBzdHJpY3QnO1xuXG5yZXF1aXJlKCcuL29iamVjdC1rZXlzJyk7XG5yZXF1aXJlKCcuL2FycmF5LW1hcCcpO1xuXG52YXIgY2FtZWwgPSAvKFthLXpdKShbQS1aXSkvZztcbnZhciBoeXBoZW5zID0gJyQxLSQyJztcbnZhciBjb250ZXh0cyA9IHt9O1xuXG5mdW5jdGlvbiBwYXJzZVN0eWxlcyAoc3R5bGVzKSB7XG4gIGlmICh0eXBlb2Ygc3R5bGVzID09PSAnc3RyaW5nJykge1xuICAgIHJldHVybiBzdHlsZXM7XG4gIH1cbiAgaWYgKE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChzdHlsZXMpICE9PSAnW29iamVjdCBPYmplY3RdJykge1xuICAgIHJldHVybiAnJztcbiAgfVxuICByZXR1cm4gT2JqZWN0LmtleXMoc3R5bGVzKS5tYXAoZnVuY3Rpb24gKGtleSkge1xuICAgIHZhciBwcm9wID0ga2V5LnJlcGxhY2UoY2FtZWwsIGh5cGhlbnMpLnRvTG93ZXJDYXNlKCk7XG4gICAgcmV0dXJuIHByb3AgKyAnOicgKyBzdHlsZXNba2V5XTtcbiAgfSkuam9pbignOycpO1xufVxuXG5mdW5jdGlvbiBjb250ZXh0IChuYW1lKSB7XG4gIGlmIChjb250ZXh0c1tuYW1lXSkge1xuICAgIHJldHVybiBjb250ZXh0c1tuYW1lXTtcbiAgfVxuICB2YXIgY2FjaGU7XG4gIHZhciBydWxlcztcbiAgdmFyIHJlbW92ZTtcblxuICBmdW5jdGlvbiBnZXRTdHlsZXNoZWV0ICgpIHtcbiAgICBpZiAoY2FjaGUpIHtcbiAgICAgIHJldHVybiBjYWNoZTtcbiAgICB9XG4gICAgdmFyIHN0eWxlID0gZG9jdW1lbnQuY3JlYXRlRWxlbWVudCgnc3R5bGUnKTtcbiAgICBkb2N1bWVudC5ib2R5LmFwcGVuZENoaWxkKHN0eWxlKTtcbiAgICBzdHlsZS5zZXRBdHRyaWJ1dGUoJ2RhdGEtY29udGV4dCcsIG5hbWUpO1xuICAgIGNhY2hlID0gZG9jdW1lbnQuc3R5bGVTaGVldHNbZG9jdW1lbnQuc3R5bGVTaGVldHMubGVuZ3RoIC0gMV07XG4gICAgcnVsZXMgPSBjYWNoZS5jc3NSdWxlcyA/ICdjc3NSdWxlcycgOiAncnVsZXMnO1xuICAgIHJlbW92ZSA9IGNhY2hlLnJlbW92ZVJ1bGUgPyAncmVtb3ZlUnVsZScgOiAnZGVsZXRlUnVsZSc7XG4gICAgcmV0dXJuIGNhY2hlO1xuICB9XG5cbiAgZnVuY3Rpb24gYWRkIChzZWxlY3Rvciwgc3R5bGVzKSB7XG4gICAgdmFyIGNzcyA9IHBhcnNlU3R5bGVzKHN0eWxlcyk7XG4gICAgdmFyIHNoZWV0ID0gZ2V0U3R5bGVzaGVldCgpO1xuICAgIHZhciBsZW4gPSBzaGVldFtydWxlc10ubGVuZ3RoO1xuICAgIGlmIChzaGVldC5pbnNlcnRSdWxlKSB7XG4gICAgICBzaGVldC5pbnNlcnRSdWxlKHNlbGVjdG9yICsgJ3snICsgY3NzICsgJ30nLCBsZW4pO1xuICAgIH0gZWxzZSBpZiAoc2hlZXQuYWRkUnVsZSkge1xuICAgICAgc2hlZXQuYWRkUnVsZShzZWxlY3RvciwgY3NzLCBsZW4pO1xuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIHJlbW92ZSAoc2VsZWN0b3IpIHtcbiAgICB2YXIgc2hlZXQgPSBnZXRTdHlsZXNoZWV0KCk7XG4gICAgdmFyIGxlbmd0aCA9IHNoZWV0W3J1bGVzXS5sZW5ndGg7XG4gICAgdmFyIGk7XG4gICAgZm9yIChpID0gbGVuZ3RoIC0gMTsgaSA+PSAwOyBpLS0pIHtcbiAgICAgIGlmIChzaGVldFtydWxlc11baV0uc2VsZWN0b3JUZXh0ID09PSBzZWxlY3Rvcikge1xuICAgICAgICBzaGVldFtyZW1vdmVdKGkpO1xuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIGZ1bmN0aW9uIGNsZWFyICgpIHtcbiAgICB2YXIgc2hlZXQgPSBnZXRTdHlsZXNoZWV0KCk7XG4gICAgd2hpbGUgKHNoZWV0W3J1bGVzXS5sZW5ndGgpIHtcbiAgICAgIHNoZWV0W3JlbW92ZV0oMCk7XG4gICAgfVxuICB9XG5cbiAgYWRkLmNsZWFyID0gY2xlYXI7XG4gIGFkZC5yZW1vdmUgPSByZW1vdmU7XG4gIGNvbnRleHRzW25hbWVdID0gYWRkO1xuICByZXR1cm4gY29udGV4dHNbbmFtZV07XG59XG5cbnZhciBjdHggPSBjb250ZXh0KCdkZWZhdWx0Jyk7XG5jdHguY29udGV4dCA9IGNvbnRleHQ7XG5tb2R1bGUuZXhwb3J0cyA9IGN0eDtcbiIsImlmICghT2JqZWN0LmtleXMpIHtcbiAgT2JqZWN0LmtleXMgPSAoZnVuY3Rpb24gKCkge1xuICAgICd1c2Ugc3RyaWN0JztcbiAgICB2YXIgaGFzT3duUHJvcGVydHkgPSBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5O1xuICAgIHZhciBoYXNEb250RW51bUJ1ZyA9ICEoe3RvU3RyaW5nOiBudWxsfSkucHJvcGVydHlJc0VudW1lcmFibGUoJ3RvU3RyaW5nJyk7XG4gICAgdmFyIGRvbnRFbnVtcyA9IFtcbiAgICAgICd0b1N0cmluZycsXG4gICAgICAndG9Mb2NhbGVTdHJpbmcnLFxuICAgICAgJ3ZhbHVlT2YnLFxuICAgICAgJ2hhc093blByb3BlcnR5JyxcbiAgICAgICdpc1Byb3RvdHlwZU9mJyxcbiAgICAgICdwcm9wZXJ0eUlzRW51bWVyYWJsZScsXG4gICAgICAnY29uc3RydWN0b3InXG4gICAgXTtcbiAgICB2YXIgZG9udEVudW1zTGVuZ3RoID0gZG9udEVudW1zLmxlbmd0aDtcblxuICAgIHJldHVybiBmdW5jdGlvbiAob2JqKSB7XG4gICAgICBpZiAodHlwZW9mIG9iaiAhPT0gJ29iamVjdCcgJiYgKHR5cGVvZiBvYmogIT09ICdmdW5jdGlvbicgfHwgb2JqID09PSBudWxsKSkge1xuICAgICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdPYmplY3Qua2V5cyBjYWxsZWQgb24gbm9uLW9iamVjdCcpO1xuICAgICAgfVxuXG4gICAgICB2YXIgcmVzdWx0ID0gW107XG4gICAgICB2YXIgcHJvcDtcbiAgICAgIHZhciBpO1xuXG4gICAgICBmb3IgKHByb3AgaW4gb2JqKSB7XG4gICAgICAgIGlmIChoYXNPd25Qcm9wZXJ0eS5jYWxsKG9iaiwgcHJvcCkpIHtcbiAgICAgICAgICByZXN1bHQucHVzaChwcm9wKTtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICBpZiAoaGFzRG9udEVudW1CdWcpIHtcbiAgICAgICAgZm9yIChpID0gMDsgaSA8IGRvbnRFbnVtc0xlbmd0aDsgaSsrKSB7XG4gICAgICAgICAgaWYgKGhhc093blByb3BlcnR5LmNhbGwob2JqLCBkb250RW51bXNbaV0pKSB7XG4gICAgICAgICAgICByZXN1bHQucHVzaChkb250RW51bXNbaV0pO1xuICAgICAgICAgIH1cbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgcmV0dXJuIHJlc3VsdDtcbiAgICB9O1xuICB9KCkpO1xufVxuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgYWRkRXZlbnQgPSBhZGRFdmVudEVhc3k7XG52YXIgcmVtb3ZlRXZlbnQgPSByZW1vdmVFdmVudEVhc3k7XG5cbmlmICghd2luZG93LmFkZEV2ZW50TGlzdGVuZXIpIHtcbiAgYWRkRXZlbnQgPSBhZGRFdmVudEhhcmQ7XG59XG5cbmlmICghd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIpIHtcbiAgcmVtb3ZlRXZlbnQgPSByZW1vdmVFdmVudEhhcmQ7XG59XG5cbmZ1bmN0aW9uIGFkZEV2ZW50RWFzeSAoZWxlbWVudCwgZXZ0LCBmbikge1xuICByZXR1cm4gZWxlbWVudC5hZGRFdmVudExpc3RlbmVyKGV2dCwgZm4pO1xufVxuXG5mdW5jdGlvbiBhZGRFdmVudEhhcmQgKGVsZW1lbnQsIGV2dCwgZm4pIHtcbiAgcmV0dXJuIGVsZW1lbnQuYXR0YWNoRXZlbnQoJ29uJyArIGV2dCwgZnVuY3Rpb24gKGUpIHtcbiAgICBlID0gZSB8fCB3aW5kb3cuZXZlbnQ7XG4gICAgZS50YXJnZXQgPSBlLnRhcmdldCB8fCBlLnNyY0VsZW1lbnQ7XG4gICAgZS5wcmV2ZW50RGVmYXVsdCAgPSBlLnByZXZlbnREZWZhdWx0ICB8fCBmdW5jdGlvbiBwcmV2ZW50RGVmYXVsdCAoKSB7IGUucmV0dXJuVmFsdWUgPSBmYWxzZTsgfTtcbiAgICBlLnN0b3BQcm9wYWdhdGlvbiA9IGUuc3RvcFByb3BhZ2F0aW9uIHx8IGZ1bmN0aW9uIHN0b3BQcm9wYWdhdGlvbiAoKSB7IGUuY2FuY2VsQnViYmxlID0gdHJ1ZTsgfTtcbiAgICBmbi5jYWxsKGVsZW1lbnQsIGUpO1xuICB9KTtcbn1cblxuZnVuY3Rpb24gcmVtb3ZlRXZlbnRFYXN5IChlbGVtZW50LCBldnQsIGZuKSB7XG4gIHJldHVybiBlbGVtZW50LnJlbW92ZUV2ZW50TGlzdGVuZXIoZXZ0LCBmbik7XG59XG5cbmZ1bmN0aW9uIHJlbW92ZUV2ZW50SGFyZCAoZWxlbWVudCwgZXZ0LCBmbikge1xuICByZXR1cm4gZWxlbWVudC5kZXRhY2hFdmVudCgnb24nICsgZXZ0LCBmbik7XG59XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBhZGQ6IGFkZEV2ZW50LFxuICByZW1vdmU6IHJlbW92ZUV2ZW50XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgaW5zZXJ0UnVsZSA9IHJlcXVpcmUoJ2luc2VydC1ydWxlJykuY29udGV4dCgnaGludCcpO1xudmFyIGV2ZW50cyA9IHJlcXVpcmUoJy4vZXZlbnRzJyk7XG52YXIgZXhwYW5kbyA9ICdoaW50LScgKyBEYXRlLm5vdygpO1xudmFyIGFwaSA9IHtcbiAgbWF4aW11bVdpZHRoOiA2NTBcbn07XG5cbmV2ZW50cy5hZGQoZG9jdW1lbnQuYm9keSwgJ21vdXNlb3ZlcicsIGVudGVyKTtcbmV2ZW50cy5hZGQoZG9jdW1lbnQuYm9keSwgJ21vdXNlb3V0JywgbGVhdmUpO1xuXG5mdW5jdGlvbiBlbnRlciAoZSkge1xuICB2YXIgcHJldiA9IHNjYW4oZS5mcm9tRWxlbWVudCk7XG4gIHZhciBlbGVtID0gc2NhbihlLnRhcmdldCk7XG4gIGlmIChlbGVtICYmIGVsZW0gIT09IHByZXYpIHtcbiAgICBtb3ZlKGVsZW0pO1xuICB9XG59XG5cbmZ1bmN0aW9uIGxlYXZlIChlKSB7XG4gIHZhciBuZXh0ID0gc2NhbihlLnRvRWxlbWVudCk7XG4gIHZhciBlbGVtID0gc2NhbihlLnRhcmdldCk7XG4gIGlmIChlbGVtICYmIGVsZW0gIT09IG5leHQpIHtcbiAgICBjbGVhcihlbGVtKTtcbiAgfVxufVxuXG5mdW5jdGlvbiBzY2FuIChlbGVtKSB7XG4gIHdoaWxlIChlbGVtKSB7XG4gICAgaWYgKHByb2JlKGVsZW0pKSB7XG4gICAgICByZXR1cm4gZWxlbTtcbiAgICB9XG4gICAgZWxlbSA9IGVsZW0ucGFyZW50RWxlbWVudDtcbiAgfVxufVxuXG5mdW5jdGlvbiBwcm9iZSAoZWxlbSkge1xuICByZXR1cm4gZWxlbS5nZXRBdHRyaWJ1dGUoJ2FyaWEtbGFiZWwnKSB8fCBlbGVtLmdldEF0dHJpYnV0ZSgnZGF0YS1oaW50Jyk7XG59XG5cbmZ1bmN0aW9uIGV4cGFuZGV4ICgpIHtcbiAgcmV0dXJuIGV4cGFuZG8gKyBEYXRlLm5vdygpO1xufVxuXG5mdW5jdGlvbiBpIChweCkge1xuICB2YXIgcmF3ID0gcHgucmVwbGFjZSgncHgnLCAnJyk7XG4gIHJldHVybiBwYXJzZUludChyYXcsIDEwKTtcbn1cblxuZnVuY3Rpb24gZ2V0UHNldWRvIChlbGVtKSB7XG4gIHJldHVybiBlbGVtLmNsYXNzTmFtZS5pbmRleE9mKCdoaW50LWJlZm9yZScpICE9PSAtMSA/ICc6YmVmb3JlJyA6ICc6YWZ0ZXInO1xufVxuXG5mdW5jdGlvbiBtb3ZlIChlbGVtKSB7XG4gIHZhciB0b3RhbFdpZHRoID0gaW5uZXJXaWR0aDtcbiAgdmFyIHBzZXVkbyA9IGdldFBzZXVkbyhlbGVtKTtcbiAgdmFyIGMgPSBnZXRDb21wdXRlZFN0eWxlKGVsZW0sIHBzZXVkbyk7XG4gIHZhciBleGlzdGVkID0gZWxlbS5nZXRBdHRyaWJ1dGUoJ2lkJyk7XG4gIHZhciBpZCA9IGV4aXN0ZWQgfHwgZXhwYW5kZXgoKTtcbiAgaWYgKGlkLmluZGV4T2YoZXhwYW5kbykgPT09IDApIHtcbiAgICBlbGVtLnNldEF0dHJpYnV0ZSgnaWQnLCBpZCk7XG4gIH1cblxuICB2YXIgc2VsZWN0b3IgPSAnIycgKyBpZCArIHBzZXVkbztcbiAgdmFyIHJlY3QgPSBlbGVtLmdldEJvdW5kaW5nQ2xpZW50UmVjdCgpO1xuICB2YXIgcGFkZGluZ3MgPSBpKGMucGFkZGluZ0xlZnQpICsgaShjLnBhZGRpbmdSaWdodCkgKyBpKGMubWFyZ2luTGVmdCkgKyBpKGMubWFyZ2luUmlnaHQpO1xuICB2YXIgd2lkdGg7XG4gIHZhciBsZWZ0O1xuICB2YXIgcmlnaHQgPSByZWN0LmxlZnQgKyBpKGMud2lkdGgpICsgaShjLmxlZnQpICsgcGFkZGluZ3M7XG4gIHZhciBjb2xsYXBzZTtcbiAgdmFyIG9mZnNldDtcbiAgdmFyIG1hcmdpbiA9IDIwO1xuICB2YXIgbWF4ID0gYXBpLm1heGltdW1XaWR0aCA9PT0gJ2F1dG8nID8gSW5maW5pdHkgOiBhcGkubWF4aW11bVdpZHRoO1xuXG4gIGlmIChpKGMud2lkdGgpICsgbWFyZ2luID4gdG90YWxXaWR0aCkge1xuICAgIGNvbGxhcHNlID0gdG90YWxXaWR0aCAtIG1hcmdpbiA+IG1heDtcbiAgICB3aWR0aCA9IGNvbGxhcHNlID8gbWF4IDogdG90YWxXaWR0aCAtIG1hcmdpbjtcbiAgICBvZmZzZXQgPSBjb2xsYXBzZSA/IHRvdGFsV2lkdGggLSBtYXggLSBtYXJnaW4gOiAwO1xuICAgIGxlZnQgPSAtKHJlY3QubGVmdCArIHBhZGRpbmdzKSArIG9mZnNldDtcblxuICAgIGluc2VydFJ1bGUoc2VsZWN0b3IsIHtcbiAgICAgIHdpZHRoOiB3aWR0aCArICdweCcsXG4gICAgICBsZWZ0OiBsZWZ0IC0gMTUgKyAncHgnLFxuICAgICAgd2hpdGVTcGFjZTogJ2luaGVyaXQnXG4gICAgfSk7XG4gIH0gZWxzZSBpZiAocmlnaHQgKyBtYXJnaW4gPiB0b3RhbFdpZHRoKSB7XG4gICAgaW5zZXJ0UnVsZShzZWxlY3Rvciwge1xuICAgICAgbGVmdDogdG90YWxXaWR0aCAtIHJpZ2h0ICsgbWFyZ2luICsgJ3B4J1xuICAgIH0pO1xuICB9XG4gIHRyYW5zcGFyZW50KCk7XG4gIHNldFRpbWVvdXQob3BhcXVlLCAxMDAwKTtcblxuICBmdW5jdGlvbiB0cmFuc3BhcmVudCAoKSB7XG4gICAgaW5zZXJ0UnVsZShzZWxlY3RvciwgeyBvcGFjaXR5OiAwIH0pO1xuICB9XG4gIGZ1bmN0aW9uIG9wYXF1ZSAoKSB7XG4gICAgaW5zZXJ0UnVsZShzZWxlY3RvciwgdHJhbnNpdGlvbih7XG4gICAgICBvcGFjaXR5OiAxXG4gICAgfSkpO1xuICB9XG4gIGZ1bmN0aW9uIHRyYW5zaXRpb24gKGNzcykge1xuICAgIHZhciBydWxlID0gJ3RyYW5zaXRpb24nO1xuICAgIHZhciBwcmVmaXhlcyA9IFsnJywgJy13ZWJraXQtJywgJy1tb3otJywgJy1vLScsICctbXMtJ107XG4gICAgZnVuY3Rpb24gYWRkIChwcmVmaXgpIHtcbiAgICAgIGNzc1twcmVmaXggKyBydWxlXSA9ICdvcGFjaXR5IDAuM3MgZWFzZS1pbi1vdXQnO1xuICAgIH1cbiAgICBwcmVmaXhlcy5mb3JFYWNoKGFkZCk7XG4gICAgcmV0dXJuIGNzcztcbiAgfVxufVxuXG5mdW5jdGlvbiBjbGVhciAoZWxlbSkge1xuICB2YXIgcHNldWRvID0gZ2V0UHNldWRvKGVsZW0pO1xuICB2YXIgaWQgPSBlbGVtLmlkO1xuICBpZiAoaWQuaW5kZXhPZihleHBhbmRvKSA9PT0gMCkge1xuICAgIGVsZW0ucmVtb3ZlQXR0cmlidXRlKCdpZCcpO1xuICB9XG4gIHZhciBzZWxlY3RvciA9ICcjJyArIGlkICsgcHNldWRvO1xuICBpbnNlcnRSdWxlLnJlbW92ZShzZWxlY3Rvcik7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gYXBpO1xuIl19
333 |
--------------------------------------------------------------------------------
/dist/hint.min.css:
--------------------------------------------------------------------------------
1 | /* @bevacqua/hint@v3.0.3, MIT licensed. https://github.com/bevacqua/hint */
2 | [aria-label],[data-hint]{position:relative}[aria-label].hint-before:before,[aria-label]:not(.hint-before):after,[data-hint].hint-before:before,[data-hint]:not(.hint-before):after{display:none;position:absolute;top:101%;left:6px;z-index:5000;pointer-events:none;padding:8px 10px;line-height:15px;white-space:nowrap;text-decoration:none;text-indent:0;overflow:visible;font-size:12px;font-weight:400;color:#fff;text-shadow:1px 0 1px #888;background-color:#412917;border-left:6px solid #d37092;border-radius:2px;-webkit-box-shadow:1px 2px 6px rgba(0,0,0,.3);box-shadow:1px 2px 6px rgba(0,0,0,.3)}[aria-label].hint-before:hover:before,[aria-label]:not(.hint-before):hover:after,[data-hint].hint-before:hover:before,[data-hint]:not(.hint-before):hover:after{display:block;-webkit-transform:translateY(8px);-moz-transform:translateY(8px);-o-transform:translateY(8px);-ms-transform:translateY(8px);transform:translateY(8px)}[aria-label].hint-before:before,[aria-label]:not(.hint-before):after{content:attr(aria-label)}[data-hint]:not(.hint-before):after{content:attr(data-hint)}[data-hint].hint-before:before{content:attr(data-hint-before)}@media only print{[aria-label].hint-before:before,[aria-label]:not(.hint-before):after,[data-hint].hint-before:before,[data-hint]:not(.hint-before):after{display:none}}
--------------------------------------------------------------------------------
/dist/hint.min.js:
--------------------------------------------------------------------------------
1 | // @bevacqua/hint@v3.0.3, MIT licensed. https://github.com/bevacqua/hint
2 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{("undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this).hint=e()}}(function(){return function i(u,a,f){function c(t,e){if(!a[t]){if(!u[t]){var n="function"==typeof require&&require;if(!e&&n)return n(t,!0);if(l)return l(t,!0);var r=new Error("Cannot find module '"+t+"'");throw r.code="MODULE_NOT_FOUND",r}var o=a[t]={exports:{}};u[t][0].call(o.exports,function(e){return c(u[t][1][e]||e)},o,o.exports,i,u,a,f)}return a[t].exports}for(var l="function"==typeof require&&require,e=0;e>>0;if("function"!=typeof e)throw new TypeError;var r,o=new Array(n),i=2<=arguments.length?arguments[1]:void 0;for(r=0;rt?(i=(a=p - <%= pkg.description %>',
20 | ' * @version v<%= pkg.version %>',
21 | ' * @link <%= pkg.homepage %>',
22 | ' * @license <%= pkg.license %>',
23 | ' */',
24 | ''
25 | ].join('\n');
26 |
27 | var succint = ' <%= pkg.name %>@v<%= pkg.version %>, <%= pkg.license %> licensed. <%= pkg.homepage %>';
28 | var succjs = '//' + succint + '\n';
29 | var succss = '/*' + succint + ' */\n';
30 |
31 | gulp.task('clean', function () {
32 | gulp.src('./dist', { read: false })
33 | .pipe(clean());
34 | });
35 |
36 | gulp.task('build', ['styles'], function () {
37 | var pkg = require('./package.json');
38 |
39 | return browserify('./src/hint.js', { debug: true, standalone: 'hint' })
40 | .bundle()
41 | .pipe(source('hint.js'))
42 | .pipe(streamify(header(extended, { pkg : pkg } )))
43 | .pipe(gulp.dest('./dist'))
44 | .pipe(streamify(rename('hint.min.js')))
45 | .pipe(streamify(uglify()))
46 | .pipe(streamify(header(succjs, { pkg : pkg } )))
47 | .pipe(streamify(size()))
48 | .pipe(gulp.dest('./dist'));
49 | });
50 |
51 | gulp.task('styles', ['clean', 'bump'], function () {
52 | var pkg = require('./package.json');
53 |
54 | return gulp.src('./src/hint.styl')
55 | .pipe(stylus({
56 | import: path.resolve('node_modules/nib/index')
57 | }))
58 | .pipe(header(extended, { pkg : pkg } ))
59 | .pipe(gulp.dest('./dist'))
60 | .pipe(rename('hint.min.css'))
61 | .pipe(minifyCSS())
62 | .pipe(size())
63 | .pipe(header(succss, { pkg : pkg } ))
64 | .pipe(gulp.dest('./dist'));
65 | });
66 |
67 | gulp.task('bump', function () {
68 | var bumpType = process.env.BUMP || 'patch'; // major.minor.patch
69 |
70 | return gulp.src(['./package.json'])
71 | .pipe(bump({ type: bumpType }))
72 | .pipe(gulp.dest('./'));
73 | });
74 |
75 | gulp.task('tag', ['build'], function (done) {
76 | var pkg = require('./package.json');
77 | var v = 'v' + pkg.version;
78 | var message = 'Release ' + v;
79 |
80 | gulp.src('./')
81 | .pipe(git.commit(message))
82 | .pipe(gulp.dest('./'))
83 | .on('end', tag);
84 |
85 | function tag () {
86 | git.tag(v, message);
87 | git.push('origin', 'master', { args: '--tags' }).end();
88 | done();
89 | }
90 | });
91 |
92 | gulp.task('npm', ['tag'], function (done) {
93 | var child = require('child_process').exec('npm publish', {}, function () {
94 | done();
95 | });
96 |
97 | child.stdout.pipe(process.stdout);
98 | child.stderr.pipe(process.stderr);
99 | child.on('error', function () {
100 | throw new Error('unable to publish');
101 | });
102 | });
103 |
104 | gulp.task('release', ['npm']);
105 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@bevacqua/hint",
3 | "description": "Awesome tooltips at your fingertips",
4 | "version": "3.0.3",
5 | "homepage": "https://github.com/bevacqua/hint",
6 | "author": {
7 | "name": "Nicolas Bevacqua",
8 | "email": "hello@ponyfoo.com",
9 | "url": "https://ponyfoo.com"
10 | },
11 | "license": "MIT",
12 | "repository": {
13 | "type": "git",
14 | "url": "git://github.com/bevacqua/hint.git"
15 | },
16 | "main": "src/hint.js",
17 | "devDependencies": {
18 | "browserify": "16.2.2",
19 | "gulp": "^3.6.2",
20 | "gulp-bump": "^0.1.8",
21 | "gulp-clean": "^0.2.4",
22 | "gulp-git": "^0.4.2",
23 | "gulp-header": "^1.0.2",
24 | "gulp-minify-css": "^0.3.4",
25 | "gulp-rename": "^1.2.0",
26 | "gulp-size": "^0.3.1",
27 | "gulp-streamify": "1.0.2",
28 | "gulp-stylus": "^1.0.0",
29 | "gulp-uglify": "3.0.0",
30 | "vinyl-source-stream": "2.0.0"
31 | },
32 | "dependencies": {
33 | "insert-rule": "^2.1.0",
34 | "nib": "^1.0.3"
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/resources/hint.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/bevacqua/hint/82081c9d6367a2900d98a167ef47d0fe270580cb/resources/hint.png
--------------------------------------------------------------------------------
/src/events.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var addEvent = addEventEasy;
4 | var removeEvent = removeEventEasy;
5 |
6 | if (!window.addEventListener) {
7 | addEvent = addEventHard;
8 | }
9 |
10 | if (!window.removeEventListener) {
11 | removeEvent = removeEventHard;
12 | }
13 |
14 | function addEventEasy (element, evt, fn) {
15 | return element.addEventListener(evt, fn);
16 | }
17 |
18 | function addEventHard (element, evt, fn) {
19 | return element.attachEvent('on' + evt, function (e) {
20 | e = e || window.event;
21 | e.target = e.target || e.srcElement;
22 | e.preventDefault = e.preventDefault || function preventDefault () { e.returnValue = false; };
23 | e.stopPropagation = e.stopPropagation || function stopPropagation () { e.cancelBubble = true; };
24 | fn.call(element, e);
25 | });
26 | }
27 |
28 | function removeEventEasy (element, evt, fn) {
29 | return element.removeEventListener(evt, fn);
30 | }
31 |
32 | function removeEventHard (element, evt, fn) {
33 | return element.detachEvent('on' + evt, fn);
34 | }
35 |
36 | module.exports = {
37 | add: addEvent,
38 | remove: removeEvent
39 | };
40 |
--------------------------------------------------------------------------------
/src/hint.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | var insertRule = require('insert-rule').context('hint');
4 | var events = require('./events');
5 | var expando = 'hint-' + Date.now();
6 | var api = {
7 | maximumWidth: 650
8 | };
9 |
10 | events.add(document.body, 'mouseover', enter);
11 | events.add(document.body, 'mouseout', leave);
12 |
13 | function enter (e) {
14 | var prev = scan(e.fromElement);
15 | var elem = scan(e.target);
16 | if (elem && elem !== prev) {
17 | move(elem);
18 | }
19 | }
20 |
21 | function leave (e) {
22 | var next = scan(e.toElement);
23 | var elem = scan(e.target);
24 | if (elem && elem !== next) {
25 | clear(elem);
26 | }
27 | }
28 |
29 | function scan (elem) {
30 | while (elem) {
31 | if (probe(elem)) {
32 | return elem;
33 | }
34 | elem = elem.parentElement;
35 | }
36 | }
37 |
38 | function probe (elem) {
39 | return elem.getAttribute('aria-label') || elem.getAttribute('data-hint');
40 | }
41 |
42 | function expandex () {
43 | return expando + Date.now();
44 | }
45 |
46 | function i (px) {
47 | var raw = px.replace('px', '');
48 | return parseInt(raw, 10);
49 | }
50 |
51 | function getPseudo (elem) {
52 | return elem.className.indexOf('hint-before') !== -1 ? ':before' : ':after';
53 | }
54 |
55 | function move (elem) {
56 | var totalWidth = innerWidth;
57 | var pseudo = getPseudo(elem);
58 | var c = getComputedStyle(elem, pseudo);
59 | var existed = elem.getAttribute('id');
60 | var id = existed || expandex();
61 | if (id.indexOf(expando) === 0) {
62 | elem.setAttribute('id', id);
63 | }
64 |
65 | var selector = '#' + id + pseudo;
66 | var rect = elem.getBoundingClientRect();
67 | var paddings = i(c.paddingLeft) + i(c.paddingRight) + i(c.marginLeft) + i(c.marginRight);
68 | var width;
69 | var left;
70 | var right = rect.left + i(c.width) + i(c.left) + paddings;
71 | var collapse;
72 | var offset;
73 | var margin = 20;
74 | var max = api.maximumWidth === 'auto' ? Infinity : api.maximumWidth;
75 |
76 | if (i(c.width) + margin > totalWidth) {
77 | collapse = totalWidth - margin > max;
78 | width = collapse ? max : totalWidth - margin;
79 | offset = collapse ? totalWidth - max - margin : 0;
80 | left = -(rect.left + paddings) + offset;
81 |
82 | insertRule(selector, {
83 | width: width + 'px',
84 | left: left - 15 + 'px',
85 | whiteSpace: 'inherit'
86 | });
87 | } else if (right + margin > totalWidth) {
88 | insertRule(selector, {
89 | left: totalWidth - right + margin + 'px'
90 | });
91 | }
92 | transparent();
93 | setTimeout(opaque, 1000);
94 |
95 | function transparent () {
96 | insertRule(selector, { opacity: 0 });
97 | }
98 | function opaque () {
99 | insertRule(selector, transition({
100 | opacity: 1
101 | }));
102 | }
103 | function transition (css) {
104 | var rule = 'transition';
105 | var prefixes = ['', '-webkit-', '-moz-', '-o-', '-ms-'];
106 | function add (prefix) {
107 | css[prefix + rule] = 'opacity 0.3s ease-in-out';
108 | }
109 | prefixes.forEach(add);
110 | return css;
111 | }
112 | }
113 |
114 | function clear (elem) {
115 | var pseudo = getPseudo(elem);
116 | var id = elem.id;
117 | if (id.indexOf(expando) === 0) {
118 | elem.removeAttribute('id');
119 | }
120 | var selector = '#' + id + pseudo;
121 | insertRule.remove(selector);
122 | }
123 |
124 | module.exports = api;
125 |
--------------------------------------------------------------------------------
/src/hint.styl:
--------------------------------------------------------------------------------
1 | @import 'tooltip'
2 |
3 | z-hint = 5000
4 |
5 | [aria-label],
6 | [data-hint]
7 | position relative
8 |
9 | [aria-label]:not(.hint-before):after,
10 | [data-hint]:not(.hint-before):after,
11 | [aria-label].hint-before:before,
12 | [data-hint].hint-before:before
13 | display none
14 | position absolute
15 | top 101%
16 | left 6px
17 |
18 | z-index z-hint
19 | pointer-events none
20 |
21 | tooltip()
22 |
23 | [aria-label]:not(.hint-before):hover:after,
24 | [data-hint]:not(.hint-before):hover:after,
25 | [aria-label].hint-before:hover:before,
26 | [data-hint].hint-before:hover:before
27 | display block
28 | transform translateY(8px)
29 |
30 | [aria-label]:not(.hint-before):after,
31 | [aria-label].hint-before:before
32 | content attr(aria-label)
33 |
34 | [data-hint]:not(.hint-before):after
35 | content attr(data-hint)
36 |
37 | [data-hint].hint-before:before
38 | content attr(data-hint-before)
39 |
40 | @media only print
41 | [aria-label]:not(.hint-before):after,
42 | [data-hint]:not(.hint-before):after,
43 | [aria-label].hint-before:before,
44 | [data-hint].hint-before:before
45 | display none
46 |
--------------------------------------------------------------------------------
/src/tooltip.styl:
--------------------------------------------------------------------------------
1 | ht-background = #412917
2 | ht-border = #d37092
3 |
4 | tooltip()
5 | padding 8px 10px
6 | line-height 15px
7 | white-space nowrap
8 | text-decoration none
9 | text-indent 0
10 | overflow visible
11 |
12 | font-size 12px
13 | font-weight normal
14 | color #fff
15 | text-shadow 1px 0px 1px #888
16 | background-color ht-background
17 | border-left 6px solid ht-border
18 | border-radius 2px
19 |
20 | box-shadow 1px 2px 6px rgba(#000, 0.3)
21 |
--------------------------------------------------------------------------------