Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / ui / webui / resources / js / action_link.js
blobbd607f524b185ce8b25156c08804df38cca96d3c
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Action links are elements that are used to perform an in-page navigation or
6 // action (e.g. showing a dialog).
7 //
8 // They look like normal anchor (<a>) tags as their text color is blue. However,
9 // they're subtly different as they're not initially underlined (giving users a
10 // clue that underlined links navigate while action links don't).
12 // Action links look very similar to normal links when hovered (hand cursor,
13 // underlined). This gives the user an idea that clicking this link will do
14 // something similar to navigation but in the same page.
16 // They can be created in JavaScript like this:
18 //   var link = document.createElement('a', 'action-link');  // Note second arg.
20 // or with a constructor like this:
22 //   var link = new ActionLink();
24 // They can be used easily from HTML as well, like so:
26 //   <a is="action-link">Click me!</a>
28 // NOTE: <action-link> and document.createElement('action-link') don't work.
30 /**
31  * @constructor
32  * @extends {HTMLAnchorElement}
33  */
34 var ActionLink = document.registerElement('action-link', {
35   prototype: {
36     __proto__: HTMLAnchorElement.prototype,
38     /** @this {ActionLink} */
39     createdCallback: function() {
40       // Action links can start disabled (e.g. <a is="action-link" disabled>).
41       this.tabIndex = this.disabled ? -1 : 0;
43       if (!this.hasAttribute('role'))
44         this.setAttribute('role', 'link');
46       this.addEventListener('keydown', function(e) {
47         if (!this.disabled && e.keyIdentifier == 'Enter') {
48           // Schedule a click asynchronously because other 'keydown' handlers
49           // may still run later (e.g. document.addEventListener('keydown')).
50           // Specifically options dialogs break when this timeout isn't here.
51           // NOTE: this affects the "trusted" state of the ensuing click. I
52           // haven't found anything that breaks because of this (yet).
53           window.setTimeout(this.click.bind(this), 0);
54         }
55       });
57       function preventDefault(e) {
58         e.preventDefault();
59       }
61       function removePreventDefault() {
62         document.removeEventListener('selectstart', preventDefault);
63         document.removeEventListener('mouseup', removePreventDefault);
64       }
66       this.addEventListener('mousedown', function() {
67         // This handlers strives to match the behavior of <a href="...">.
69         // While the mouse is down, prevent text selection from dragging.
70         document.addEventListener('selectstart', preventDefault);
71         document.addEventListener('mouseup', removePreventDefault);
73         // If focus started via mouse press, don't show an outline.
74         if (document.activeElement != this)
75           this.classList.add('no-outline');
76       });
78       this.addEventListener('blur', function() {
79         this.classList.remove('no-outline');
80       });
81     },
83     /** @type {boolean} */
84     set disabled(disabled) {
85       if (disabled)
86         HTMLAnchorElement.prototype.setAttribute.call(this, 'disabled', '');
87       else
88         HTMLAnchorElement.prototype.removeAttribute.call(this, 'disabled');
89       this.tabIndex = disabled ? -1 : 0;
90     },
91     get disabled() {
92       return this.hasAttribute('disabled');
93     },
95     /** @override */
96     setAttribute: function(attr, val) {
97       if (attr.toLowerCase() == 'disabled')
98         this.disabled = true;
99       else
100         HTMLAnchorElement.prototype.setAttribute.apply(this, arguments);
101     },
103     /** @override */
104     removeAttribute: function(attr) {
105       if (attr.toLowerCase() == 'disabled')
106         this.disabled = false;
107       else
108         HTMLAnchorElement.prototype.removeAttribute.apply(this, arguments);
109     },
110   },
112   extends: 'a',