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 decorate: function() {
22 this.name_ = this.getAttribute('id');
23 this.nextButton_ = this.getElementsByClassName('next-button')[0];
24 if (!this.nextButton_)
25 throw Error('Next button not found.');
26 this.nextButton_.addEventListener('click', (function(e) {
27 chrome.send('nextButtonClicked', [this.getName()]);
33 * Returns name of the string.
41 * @param {boolean} animated Whether transition should be animated.
42 * @param {function()=} opt_onHidden Called after step has been hidden.
44 hide: function(animated, opt_onHidden) {
45 var transitionDuration =
46 animated ? cr.FirstRun.getDefaultTransitionDuration() : 0;
47 changeVisibility(this,
51 this.classList.add('hidden');
59 * @param {boolean} animated Whether transition should be animated.
60 * @param {function()=} opt_onShown Called after step has been shown.
62 show: function(animated, opt_onShown) {
63 var transitionDuration =
64 animated ? cr.FirstRun.getDefaultTransitionDuration() : 0;
65 this.classList.remove('hidden');
66 changeVisibility(this,
76 * Sets position of the step.
77 * @param {object} position Parameter with optional fields |top|,
78 * |right|, |bottom|, |left| holding corresponding offsets.
80 setPosition: function(position) {
81 var style = this.style;
82 ['top', 'right', 'bottom', 'left'].forEach(function(property) {
83 if (position.hasOwnProperty(property))
84 style.setProperty(property, position[property] + 'px');
89 var Bubble = cr.ui.define('div');
91 // Styles of .step which are used for arrow styling.
103 var DISTANCE_TO_POINTEE = 10;
104 var MINIMAL_SCREEN_OFFSET = 10;
105 var ARROW_LENGTH = 6; // Keep synced with .arrow border-width.
108 __proto__: Step.prototype,
110 // Element displaying arrow.
113 // Unit vector directed along the bubble arrow.
117 * In addition to base class 'decorate' this method creates arrow and
118 * sets some properties related to arrow.
120 decorate: function() {
121 Step.prototype.decorate.call(this);
122 this.arrow_ = document.createElement('div');
123 this.arrow_.classList.add('arrow');
124 this.appendChild(this.arrow_);
125 ARROW_STYLES.forEach(function(style) {
126 if (!this.classList.contains(style))
128 // Changing right to left in RTL case.
129 if (document.documentElement.getAttribute('dir') == 'rtl') {
130 style = style.replace(/right|left/, function(match) {
131 return (match == 'right') ? 'left' : 'right';
134 this.arrow_.classList.add(style);
136 var list = this.arrow_.classList;
137 if (list.contains('points-up'))
138 this.direction_ = [0, -1];
139 else if (list.contains('points-right'))
140 this.direction_ = [1, 0];
141 else if (list.contains('points-down'))
142 this.direction_ = [0, 1];
143 else // list.contains('points-left')
144 this.direction_ = [-1, 0];
148 * Sets position of bubble in such a maner that bubble's arrow points to
150 * @param {Array} point Bubble arrow should point to this point after
151 * positioning. |point| has format [x, y].
152 * @param {offset} number Additional offset from |point|.
154 setPointsTo: function(point, offset) {
155 var shouldShowBefore = this.hidden;
156 // "Showing" bubble in order to make offset* methods work.
157 if (shouldShowBefore) {
158 this.style.setProperty('opacity', '0');
161 var arrow = [this.arrow_.offsetLeft + this.arrow_.offsetWidth / 2,
162 this.arrow_.offsetTop + this.arrow_.offsetHeight / 2];
163 var totalOffset = DISTANCE_TO_POINTEE + offset;
164 var left = point[0] - totalOffset * this.direction_[0] - arrow[0];
165 var top = point[1] - totalOffset * this.direction_[1] - arrow[1];
166 // Force bubble to be inside screen.
167 if (this.arrow_.classList.contains('points-up') ||
168 this.arrow_.classList.contains('points-down')) {
169 left = Math.max(left, MINIMAL_SCREEN_OFFSET);
170 left = Math.min(left, document.body.offsetWidth - this.offsetWidth -
171 MINIMAL_SCREEN_OFFSET);
173 if (this.arrow_.classList.contains('points-left') ||
174 this.arrow_.classList.contains('points-right')) {
175 top = Math.max(top, MINIMAL_SCREEN_OFFSET);
176 top = Math.min(top, document.body.offsetHeight - this.offsetHeight -
177 MINIMAL_SCREEN_OFFSET);
179 this.style.setProperty('left', left + 'px');
180 this.style.setProperty('top', top + 'px');
181 if (shouldShowBefore) {
183 this.style.removeProperty('opacity');
188 * Sets position of bubble. Overrides Step.setPosition to adjust offsets
189 * in case if its direction is the same as arrow's direction.
190 * @param {object} position Parameter with optional fields |top|,
191 * |right|, |bottom|, |left| holding corresponding offsets.
193 setPosition: function(position) {
194 var arrow = this.arrow_;
195 // Increasing offset if it's from side where bubble points to.
196 [['top', 'points-up'],
197 ['right', 'points-right'],
198 ['bottom', 'points-down'],
199 ['left', 'points-left']].forEach(function(mapping) {
200 if (position.hasOwnProperty(mapping[0]) &&
201 arrow.classList.contains(mapping[1])) {
202 position[mapping[0]] += ARROW_LENGTH + DISTANCE_TO_POINTEE;
205 Step.prototype.setPosition.call(this, position);
209 var HelpStep = cr.ui.define('div');
211 HelpStep.prototype = {
212 __proto__: Bubble.prototype,
214 decorate: function() {
215 Bubble.prototype.decorate.call(this);
216 var helpButton = this.getElementsByClassName('help-button')[0];
217 helpButton.addEventListener('click', function(e) {
218 chrome.send('helpButtonClicked');
224 var DecorateStep = function(el) {
226 HelpStep.decorate(el);
227 else if (el.classList.contains('bubble'))
233 return {DecorateStep: DecorateStep};