1 // Copyright 2013 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.
6 * Prototype for first-run tutorial steps.
9 cr.define('cr.FirstRun', function() {
10 var Step = cr.ui.define('div');
13 __proto__: HTMLDivElement.prototype,
18 // Button leading to next tutorial step.
21 // Whether screen is shown.
24 decorate: function() {
25 this.name_ = this.getAttribute('id');
26 this.nextButton_ = this.getElementsByClassName('next-button')[0];
27 if (!this.nextButton_)
28 throw Error('Next button not found.');
29 this.nextButton_.addEventListener('click', (function(e) {
30 chrome.send('nextButtonClicked', [this.getName()]);
33 // Make close unfocusable by mouse.
34 var topBar = this.getElementsByClassName('topbutton-bar')[0];
36 var closeButton = topBar.getElementsByClassName('close-button')[0];
38 closeButton.addEventListener('mousedown', function(event) {
39 event.preventDefault();
46 * Returns name of the string.
56 this.style.setProperty('display', 'none');
57 this.isShown_ = false;
64 this.style.setProperty('display', 'inline-block');
69 * Sets position of the step.
70 * @param {object} position Parameter with optional fields |top|,
71 * |right|, |bottom|, |left| holding corresponding offsets.
73 setPosition: function(position) {
74 var style = this.style;
75 ['top', 'right', 'bottom', 'left'].forEach(function(property) {
76 if (position.hasOwnProperty(property))
77 style.setProperty(property, position[property] + 'px');
82 var Bubble = cr.ui.define('div');
84 // Styles of .step which are used for arrow styling.
96 var DISTANCE_TO_POINTEE = 10;
97 var MINIMAL_SCREEN_OFFSET = 10;
98 var ARROW_LENGTH = 6; // Keep synced with .arrow border-width.
101 __proto__: Step.prototype,
103 // Element displaying arrow.
106 // Unit vector directed along the bubble arrow.
110 * In addition to base class 'decorate' this method creates arrow and
111 * sets some properties related to arrow.
113 decorate: function() {
114 Step.prototype.decorate.call(this);
115 this.arrow_ = document.createElement('div');
116 this.arrow_.classList.add('arrow');
117 this.appendChild(this.arrow_);
118 ARROW_STYLES.forEach(function(style) {
119 if (!this.classList.contains(style))
121 // Changing right to left in RTL case.
122 if (document.documentElement.getAttribute('dir') == 'rtl') {
123 style = style.replace(/right|left/, function(match) {
124 return (match == 'right') ? 'left' : 'right';
127 this.arrow_.classList.add(style);
129 var list = this.arrow_.classList;
130 if (list.contains('points-up'))
131 this.direction_ = [0, -1];
132 else if (list.contains('points-right'))
133 this.direction_ = [1, 0];
134 else if (list.contains('points-down'))
135 this.direction_ = [0, 1];
136 else // list.contains('points-left')
137 this.direction_ = [-1, 0];
141 * Sets position of bubble in such a maner that bubble's arrow points to
143 * @param {Array} point Bubble arrow should point to this point after
144 * positioning. |point| has format [x, y].
145 * @param {offset} number Additional offset from |point|.
147 setPointsTo: function(point, offset) {
148 var shouldShowBefore = !this.isShown_;
149 // "Showing" bubble in order to make offset* methods work.
150 if (shouldShowBefore) {
151 this.style.setProperty('opacity', '0');
154 var arrow = [this.arrow_.offsetLeft + this.arrow_.offsetWidth / 2,
155 this.arrow_.offsetTop + this.arrow_.offsetHeight / 2];
156 var totalOffset = DISTANCE_TO_POINTEE + offset;
157 var left = point[0] - totalOffset * this.direction_[0] - arrow[0];
158 var top = point[1] - totalOffset * this.direction_[1] - arrow[1];
159 // Force bubble to be inside screen.
160 if (this.arrow_.classList.contains('points-up') ||
161 this.arrow_.classList.contains('points-down')) {
162 left = Math.max(left, MINIMAL_SCREEN_OFFSET);
163 left = Math.min(left, document.body.offsetWidth - this.offsetWidth -
164 MINIMAL_SCREEN_OFFSET);
166 if (this.arrow_.classList.contains('points-left') ||
167 this.arrow_.classList.contains('points-right')) {
168 top = Math.max(top, MINIMAL_SCREEN_OFFSET);
169 top = Math.min(top, document.body.offsetHeight - this.offsetHeight -
170 MINIMAL_SCREEN_OFFSET);
172 this.style.setProperty('left', left + 'px');
173 this.style.setProperty('top', top + 'px');
174 if (shouldShowBefore) {
175 this.style.setProperty('opacity', '1');
181 * Sets position of bubble. Overrides Step.setPosition to adjust offsets
182 * in case if its direction is the same as arrow's direction.
183 * @param {object} position Parameter with optional fields |top|,
184 * |right|, |bottom|, |left| holding corresponding offsets.
186 setPosition: function(position) {
187 var arrow = this.arrow_;
188 // Increasing offset if it's from side where bubble points to.
189 [['top', 'points-up'],
190 ['right', 'points-right'],
191 ['bottom', 'points-down'],
192 ['left', 'points-left']].forEach(function(mapping) {
193 if (position.hasOwnProperty(mapping[0]) &&
194 arrow.classList.contains(mapping[1])) {
195 position[mapping[0]] += ARROW_LENGTH + DISTANCE_TO_POINTEE;
198 Step.prototype.setPosition.call(this, position);
202 var DecorateStep = function(el) {
203 if (el.classList.contains('bubble'))
209 return {DecorateStep: DecorateStep};