| OLD | NEW |
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * Prototype for first-run tutorial steps. | 6 * Prototype for first-run tutorial steps. |
| 7 */ | 7 */ |
| 8 | 8 |
| 9 cr.define('cr.FirstRun', function() { | 9 cr.define('cr.FirstRun', function() { |
| 10 var Step = cr.ui.define('div'); | 10 var Step = cr.ui.define('div'); |
| 11 | 11 |
| 12 Step.prototype = { | 12 Step.prototype = { |
| 13 __proto__: HTMLDivElement.prototype, | 13 __proto__: HTMLDivElement.prototype, |
| 14 | 14 |
| 15 // Name of step. | 15 // Name of step. |
| 16 name_: null, | 16 name_: null, |
| 17 | 17 |
| 18 // Button leading to next tutorial step. | 18 // Button leading to next tutorial step. |
| 19 nextButton_: null, | 19 nextButton_: null, |
| 20 | 20 |
| 21 // Default control for this step. | 21 // Default control for this step. |
| 22 defaultControl_: null, | 22 defaultControl_: null, |
| 23 | 23 |
| 24 decorate: function() { | 24 decorate: function() { |
| 25 this.name_ = this.getAttribute('id'); | 25 this.name_ = this.getAttribute('id'); |
| 26 var controlsContainer = this.getElementsByClassName('controls')[0]; | 26 var controlsContainer = this.getElementsByClassName('controls')[0]; |
| 27 if (!controlsContainer) | 27 if (!controlsContainer) |
| 28 throw Error('Controls not found.'); | 28 throw Error('Controls not found.'); |
| 29 this.nextButton_ = | 29 this.nextButton_ = |
| 30 controlsContainer.getElementsByClassName('next-button')[0]; | 30 controlsContainer.getElementsByClassName('next-button')[0]; |
| 31 if (!this.nextButton_) | 31 if (!this.nextButton_) |
| 32 throw Error('Next button not found.'); | 32 throw Error('Next button not found.'); |
| 33 this.nextButton_.addEventListener('click', (function(e) { | 33 this.nextButton_.addEventListener( |
| 34 chrome.send('nextButtonClicked', [this.getName()]); | 34 'click', (function(e) { |
| 35 e.stopPropagation(); | 35 chrome.send('nextButtonClicked', [this.getName()]); |
| 36 }).bind(this)); | 36 e.stopPropagation(); |
| 37 }).bind(this)); |
| 37 this.defaultControl_ = controlsContainer.children[0]; | 38 this.defaultControl_ = controlsContainer.children[0]; |
| 38 }, | 39 }, |
| 39 | 40 |
| 40 /** | 41 /** |
| 41 * Returns name of the string. | 42 * Returns name of the string. |
| 42 */ | 43 */ |
| 43 getName: function() { | 44 getName: function() { |
| 44 return this.name_; | 45 return this.name_; |
| 45 }, | 46 }, |
| 46 | 47 |
| 47 /** | 48 /** |
| 48 * Hides the step. | 49 * Hides the step. |
| 49 * @param {boolean} animated Whether transition should be animated. | 50 * @param {boolean} animated Whether transition should be animated. |
| 50 * @param {function()=} opt_onHidden Called after step has been hidden. | 51 * @param {function()=} opt_onHidden Called after step has been hidden. |
| 51 */ | 52 */ |
| 52 hide: function(animated, opt_onHidden) { | 53 hide: function(animated, opt_onHidden) { |
| 53 var transitionDuration = | 54 var transitionDuration = |
| 54 animated ? cr.FirstRun.getDefaultTransitionDuration() : 0; | 55 animated ? cr.FirstRun.getDefaultTransitionDuration() : 0; |
| 55 changeVisibility(this, | 56 changeVisibility(this, false, transitionDuration, function() { |
| 56 false, | 57 this.classList.add('hidden'); |
| 57 transitionDuration, | 58 if (opt_onHidden) |
| 58 function() { | 59 opt_onHidden(); |
| 59 this.classList.add('hidden'); | 60 }.bind(this)); |
| 60 if (opt_onHidden) | |
| 61 opt_onHidden(); | |
| 62 }.bind(this)); | |
| 63 }, | 61 }, |
| 64 | 62 |
| 65 /** | 63 /** |
| 66 * Shows the step. | 64 * Shows the step. |
| 67 * @param {boolean} animated Whether transition should be animated. | 65 * @param {boolean} animated Whether transition should be animated. |
| 68 * @param {function(Step)=} opt_onShown Called after step has been shown. | 66 * @param {function(Step)=} opt_onShown Called after step has been shown. |
| 69 */ | 67 */ |
| 70 show: function(animated, opt_onShown) { | 68 show: function(animated, opt_onShown) { |
| 71 var transitionDuration = | 69 var transitionDuration = |
| 72 animated ? cr.FirstRun.getDefaultTransitionDuration() : 0; | 70 animated ? cr.FirstRun.getDefaultTransitionDuration() : 0; |
| 73 this.classList.remove('hidden'); | 71 this.classList.remove('hidden'); |
| 74 changeVisibility(this, | 72 changeVisibility(this, true, transitionDuration, function() { |
| 75 true, | 73 if (opt_onShown) |
| 76 transitionDuration, | 74 opt_onShown(this); |
| 77 function() { | 75 }.bind(this)); |
| 78 if (opt_onShown) | |
| 79 opt_onShown(this); | |
| 80 }.bind(this)); | |
| 81 }, | 76 }, |
| 82 | 77 |
| 83 /** | 78 /** |
| 84 * Sets position of the step. | 79 * Sets position of the step. |
| 85 * @param {object} position Parameter with optional fields |top|, | 80 * @param {object} position Parameter with optional fields |top|, |
| 86 * |right|, |bottom|, |left| holding corresponding offsets. | 81 * |right|, |bottom|, |left| holding corresponding offsets. |
| 87 */ | 82 */ |
| 88 setPosition: function(position) { | 83 setPosition: function(position) { |
| 89 var style = this.style; | 84 var style = this.style; |
| 90 ['top', 'right', 'bottom', 'left'].forEach(function(property) { | 85 ['top', 'right', 'bottom', 'left'].forEach(function(property) { |
| (...skipping 14 matching lines...) Expand all Loading... |
| 105 var Bubble = cr.ui.define('div'); | 100 var Bubble = cr.ui.define('div'); |
| 106 | 101 |
| 107 // List of rules declaring bubble's arrow position depending on text direction | 102 // List of rules declaring bubble's arrow position depending on text direction |
| 108 // and shelf alignment. Every rule has required field |position| with list | 103 // and shelf alignment. Every rule has required field |position| with list |
| 109 // of classes that should be applied to arrow element if this rule choosen. | 104 // of classes that should be applied to arrow element if this rule choosen. |
| 110 // The rule is suitable if its |shelf| and |dir| fields are correspond | 105 // The rule is suitable if its |shelf| and |dir| fields are correspond |
| 111 // to current shelf alignment and text direction. Missing fields behaves like | 106 // to current shelf alignment and text direction. Missing fields behaves like |
| 112 // '*' wildcard. The last suitable rule in list is choosen for arrow style. | 107 // '*' wildcard. The last suitable rule in list is choosen for arrow style. |
| 113 var ARROW_POSITION = { | 108 var ARROW_POSITION = { |
| 114 'app-list': [ | 109 'app-list': [ |
| 115 { | 110 {position: ['points-down', 'left']}, |
| 116 position: ['points-down', 'left'] | 111 {dir: 'rtl', position: ['points-down', 'right']}, |
| 117 }, | 112 {shelf: 'left', position: ['points-left', 'top']}, |
| 118 { | 113 {shelf: 'right', position: ['points-right', 'top']} |
| 119 dir: 'rtl', | |
| 120 position: ['points-down', 'right'] | |
| 121 }, | |
| 122 { | |
| 123 shelf: 'left', | |
| 124 position: ['points-left', 'top'] | |
| 125 }, | |
| 126 { | |
| 127 shelf: 'right', | |
| 128 position: ['points-right', 'top'] | |
| 129 } | |
| 130 ], | 114 ], |
| 131 'tray': [ | 115 'tray': [ |
| 132 { | 116 {position: ['points-right', 'top']}, |
| 133 position: ['points-right', 'top'] | 117 {dir: 'rtl', shelf: 'bottom', position: ['points-left', 'top']}, |
| 134 }, | 118 {shelf: 'left', position: ['points-left', 'top']} |
| 135 { | |
| 136 dir: 'rtl', | |
| 137 shelf: 'bottom', | |
| 138 position: ['points-left', 'top'] | |
| 139 }, | |
| 140 { | |
| 141 shelf: 'left', | |
| 142 position: ['points-left', 'top'] | |
| 143 } | |
| 144 ], | 119 ], |
| 145 'help': [ | 120 'help': [ |
| 146 { | 121 {position: ['points-right', 'bottom']}, |
| 147 position: ['points-right', 'bottom'] | 122 {dir: 'rtl', shelf: 'bottom', position: ['points-left', 'bottom']}, |
| 148 }, | 123 {shelf: 'left', position: ['points-left', 'bottom']} |
| 149 { | |
| 150 dir: 'rtl', | |
| 151 shelf: 'bottom', | |
| 152 position: ['points-left', 'bottom'] | |
| 153 }, | |
| 154 { | |
| 155 shelf: 'left', | |
| 156 position: ['points-left', 'bottom'] | |
| 157 } | |
| 158 ] | 124 ] |
| 159 }; | 125 }; |
| 160 | 126 |
| 161 var DISTANCE_TO_POINTEE = 10; | 127 var DISTANCE_TO_POINTEE = 10; |
| 162 var MINIMAL_SCREEN_OFFSET = 10; | 128 var MINIMAL_SCREEN_OFFSET = 10; |
| 163 var ARROW_LENGTH = 6; // Keep synced with .arrow border-width. | 129 var ARROW_LENGTH = 6; // Keep synced with .arrow border-width. |
| 164 | 130 |
| 165 Bubble.prototype = { | 131 Bubble.prototype = { |
| 166 __proto__: Step.prototype, | 132 __proto__: Step.prototype, |
| 167 | 133 |
| 168 // Element displaying arrow. | 134 // Element displaying arrow. |
| 169 arrow_: null, | 135 arrow_: null, |
| 170 | 136 |
| 171 // Unit vector directed along the bubble arrow. | 137 // Unit vector directed along the bubble arrow. |
| 172 direction_: null, | 138 direction_: null, |
| 173 | 139 |
| 174 /** | 140 /** |
| 175 * In addition to base class 'decorate' this method creates arrow and | 141 * In addition to base class 'decorate' this method creates arrow and |
| 176 * sets some properties related to arrow. | 142 * sets some properties related to arrow. |
| 177 */ | 143 */ |
| 178 decorate: function() { | 144 decorate: function() { |
| 179 Step.prototype.decorate.call(this); | 145 Step.prototype.decorate.call(this); |
| 180 this.arrow_ = document.createElement('div'); | 146 this.arrow_ = document.createElement('div'); |
| 181 this.arrow_.classList.add('arrow'); | 147 this.arrow_.classList.add('arrow'); |
| 182 this.appendChild(this.arrow_); | 148 this.appendChild(this.arrow_); |
| 183 var inputDirection = document.documentElement.getAttribute('dir'); | 149 var inputDirection = document.documentElement.getAttribute('dir'); |
| 184 var shelfAlignment = document.documentElement.getAttribute('shelf'); | 150 var shelfAlignment = document.documentElement.getAttribute('shelf'); |
| 185 var isSuitable = function(rule) { | 151 var isSuitable = function(rule) { |
| 186 var inputDirectionMatch = !rule.hasOwnProperty('dir') || | 152 var inputDirectionMatch = |
| 187 rule.dir === inputDirection; | 153 !rule.hasOwnProperty('dir') || rule.dir === inputDirection; |
| 188 var shelfAlignmentMatch = !rule.hasOwnProperty('shelf') || | 154 var shelfAlignmentMatch = |
| 189 rule.shelf === shelfAlignment; | 155 !rule.hasOwnProperty('shelf') || rule.shelf === shelfAlignment; |
| 190 return inputDirectionMatch && shelfAlignmentMatch; | 156 return inputDirectionMatch && shelfAlignmentMatch; |
| 191 }; | 157 }; |
| 192 var lastSuitableRule = null; | 158 var lastSuitableRule = null; |
| 193 var rules = ARROW_POSITION[this.getName()]; | 159 var rules = ARROW_POSITION[this.getName()]; |
| 194 rules.forEach(function(rule) { | 160 rules.forEach(function(rule) { |
| 195 if (isSuitable(rule)) | 161 if (isSuitable(rule)) |
| 196 lastSuitableRule = rule; | 162 lastSuitableRule = rule; |
| 197 }); | 163 }); |
| 198 assert(lastSuitableRule); | 164 assert(lastSuitableRule); |
| 199 lastSuitableRule.position.forEach(function(cls) { | 165 lastSuitableRule.position.forEach(function(cls) { |
| 200 this.arrow_.classList.add(cls); | 166 this.arrow_.classList.add(cls); |
| 201 }.bind(this)); | 167 }.bind(this)); |
| 202 var list = this.arrow_.classList; | 168 var list = this.arrow_.classList; |
| 203 if (list.contains('points-up')) | 169 if (list.contains('points-up')) |
| 204 this.direction_ = [0, -1]; | 170 this.direction_ = [0, -1]; |
| 205 else if (list.contains('points-right')) | 171 else if (list.contains('points-right')) |
| 206 this.direction_ = [1, 0]; | 172 this.direction_ = [1, 0]; |
| 207 else if (list.contains('points-down')) | 173 else if (list.contains('points-down')) |
| 208 this.direction_ = [0, 1]; | 174 this.direction_ = [0, 1]; |
| 209 else // list.contains('points-left') | 175 else // list.contains('points-left') |
| 210 this.direction_ = [-1, 0]; | 176 this.direction_ = [-1, 0]; |
| 211 }, | 177 }, |
| 212 | 178 |
| 213 /** | 179 /** |
| 214 * Sets position of bubble in such a maner that bubble's arrow points to | 180 * Sets position of bubble in such a maner that bubble's arrow points to |
| 215 * given point. | 181 * given point. |
| 216 * @param {Array} point Bubble arrow should point to this point after | 182 * @param {Array} point Bubble arrow should point to this point after |
| 217 * positioning. |point| has format [x, y]. | 183 * positioning. |point| has format [x, y]. |
| 218 * @param {offset} number Additional offset from |point|. | 184 * @param {offset} number Additional offset from |point|. |
| 219 */ | 185 */ |
| 220 setPointsTo: function(point, offset) { | 186 setPointsTo: function(point, offset) { |
| 221 var shouldShowBefore = this.hidden; | 187 var shouldShowBefore = this.hidden; |
| 222 // "Showing" bubble in order to make offset* methods work. | 188 // "Showing" bubble in order to make offset* methods work. |
| 223 if (shouldShowBefore) { | 189 if (shouldShowBefore) { |
| 224 this.style.setProperty('opacity', '0'); | 190 this.style.setProperty('opacity', '0'); |
| 225 this.show(false); | 191 this.show(false); |
| 226 } | 192 } |
| 227 var arrow = [this.arrow_.offsetLeft + this.arrow_.offsetWidth / 2, | 193 var arrow = [ |
| 228 this.arrow_.offsetTop + this.arrow_.offsetHeight / 2]; | 194 this.arrow_.offsetLeft + this.arrow_.offsetWidth / 2, |
| 195 this.arrow_.offsetTop + this.arrow_.offsetHeight / 2 |
| 196 ]; |
| 229 var totalOffset = DISTANCE_TO_POINTEE + offset; | 197 var totalOffset = DISTANCE_TO_POINTEE + offset; |
| 230 var left = point[0] - totalOffset * this.direction_[0] - arrow[0]; | 198 var left = point[0] - totalOffset * this.direction_[0] - arrow[0]; |
| 231 var top = point[1] - totalOffset * this.direction_[1] - arrow[1]; | 199 var top = point[1] - totalOffset * this.direction_[1] - arrow[1]; |
| 232 // Force bubble to be inside screen. | 200 // Force bubble to be inside screen. |
| 233 if (this.arrow_.classList.contains('points-up') || | 201 if (this.arrow_.classList.contains('points-up') || |
| 234 this.arrow_.classList.contains('points-down')) { | 202 this.arrow_.classList.contains('points-down')) { |
| 235 left = Math.max(left, MINIMAL_SCREEN_OFFSET); | 203 left = Math.max(left, MINIMAL_SCREEN_OFFSET); |
| 236 left = Math.min(left, document.body.offsetWidth - this.offsetWidth - | 204 left = Math.min( |
| 237 MINIMAL_SCREEN_OFFSET); | 205 left, |
| 206 document.body.offsetWidth - this.offsetWidth - |
| 207 MINIMAL_SCREEN_OFFSET); |
| 238 } | 208 } |
| 239 if (this.arrow_.classList.contains('points-left') || | 209 if (this.arrow_.classList.contains('points-left') || |
| 240 this.arrow_.classList.contains('points-right')) { | 210 this.arrow_.classList.contains('points-right')) { |
| 241 top = Math.max(top, MINIMAL_SCREEN_OFFSET); | 211 top = Math.max(top, MINIMAL_SCREEN_OFFSET); |
| 242 top = Math.min(top, document.body.offsetHeight - this.offsetHeight - | 212 top = Math.min( |
| 243 MINIMAL_SCREEN_OFFSET); | 213 top, |
| 214 document.body.offsetHeight - this.offsetHeight - |
| 215 MINIMAL_SCREEN_OFFSET); |
| 244 } | 216 } |
| 245 this.style.setProperty('left', left + 'px'); | 217 this.style.setProperty('left', left + 'px'); |
| 246 this.style.setProperty('top', top + 'px'); | 218 this.style.setProperty('top', top + 'px'); |
| 247 if (shouldShowBefore) { | 219 if (shouldShowBefore) { |
| 248 this.hide(false); | 220 this.hide(false); |
| 249 this.style.removeProperty('opacity'); | 221 this.style.removeProperty('opacity'); |
| 250 } | 222 } |
| 251 }, | 223 }, |
| 252 | 224 |
| 253 /** | 225 /** |
| 254 * Sets position of bubble. Overrides Step.setPosition to adjust offsets | 226 * Sets position of bubble. Overrides Step.setPosition to adjust offsets |
| 255 * in case if its direction is the same as arrow's direction. | 227 * in case if its direction is the same as arrow's direction. |
| 256 * @param {object} position Parameter with optional fields |top|, | 228 * @param {object} position Parameter with optional fields |top|, |
| 257 * |right|, |bottom|, |left| holding corresponding offsets. | 229 * |right|, |bottom|, |left| holding corresponding offsets. |
| 258 */ | 230 */ |
| 259 setPosition: function(position) { | 231 setPosition: function(position) { |
| 260 var arrow = this.arrow_; | 232 var arrow = this.arrow_; |
| 261 // Increasing offset if it's from side where bubble points to. | 233 // Increasing offset if it's from side where bubble points to. |
| 262 [['top', 'points-up'], | 234 [['top', 'points-up'], ['right', 'points-right'], |
| 263 ['right', 'points-right'], | 235 ['bottom', 'points-down'], ['left', 'points-left']] |
| 264 ['bottom', 'points-down'], | 236 .forEach(function(mapping) { |
| 265 ['left', 'points-left']].forEach(function(mapping) { | 237 if (position.hasOwnProperty(mapping[0]) && |
| 266 if (position.hasOwnProperty(mapping[0]) && | 238 arrow.classList.contains(mapping[1])) { |
| 267 arrow.classList.contains(mapping[1])) { | 239 position[mapping[0]] += ARROW_LENGTH + DISTANCE_TO_POINTEE; |
| 268 position[mapping[0]] += ARROW_LENGTH + DISTANCE_TO_POINTEE; | 240 } |
| 269 } | 241 }); |
| 270 }); | |
| 271 Step.prototype.setPosition.call(this, position); | 242 Step.prototype.setPosition.call(this, position); |
| 272 }, | 243 }, |
| 273 }; | 244 }; |
| 274 | 245 |
| 275 var HelpStep = cr.ui.define('div'); | 246 var HelpStep = cr.ui.define('div'); |
| 276 | 247 |
| 277 HelpStep.prototype = { | 248 HelpStep.prototype = { |
| 278 __proto__: Bubble.prototype, | 249 __proto__: Bubble.prototype, |
| 279 | 250 |
| 280 decorate: function() { | 251 decorate: function() { |
| (...skipping 10 matching lines...) Expand all Loading... |
| 291 if (el.id == 'help') | 262 if (el.id == 'help') |
| 292 HelpStep.decorate(el); | 263 HelpStep.decorate(el); |
| 293 else if (el.classList.contains('bubble')) | 264 else if (el.classList.contains('bubble')) |
| 294 Bubble.decorate(el); | 265 Bubble.decorate(el); |
| 295 else | 266 else |
| 296 Step.decorate(el); | 267 Step.decorate(el); |
| 297 }; | 268 }; |
| 298 | 269 |
| 299 return {DecorateStep: DecorateStep}; | 270 return {DecorateStep: DecorateStep}; |
| 300 }); | 271 }); |
| OLD | NEW |