OLD | NEW |
---|---|
1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 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 // To avoid creating tons of unnecessary nodes. We assume we cannot fit more | 5 // To avoid creating tons of unnecessary nodes. We assume we cannot fit more |
6 // than this many items in the miniview. | 6 // than this many items in the miniview. |
7 var MAX_MINIVIEW_ITEMS = 15; | 7 var MAX_MINIVIEW_ITEMS = 15; |
8 | 8 |
9 var loading = true; | 9 var loading = true; |
10 | 10 |
11 function updateSimpleSection(id, section) { | 11 function updateSimpleSection(id, section) { |
12 if (shownSections & section) | 12 if (shownSections & section) |
13 $(id).classList.remove('hidden'); | 13 $(id).classList.remove('hidden'); |
14 else | 14 else |
15 $(id).classList.add('hidden'); | 15 $(id).classList.add('hidden'); |
16 } | 16 } |
17 | 17 |
18 function recentlyClosedTabs(data) { | 18 function recentlyClosedTabs(data) { |
19 logEvent('received recently closed tabs'); | 19 logEvent('received recently closed tabs'); |
20 // We need to store the recent items so we can update the layout on a resize. | 20 // We need to store the recent items so we can update the layout on a resize. |
21 recentItems = data; | 21 recentItems = data; |
22 renderRecentlyClosed(); | 22 renderRecentlyClosed(); |
23 layoutSections(); | |
23 } | 24 } |
24 | 25 |
25 var recentItems = []; | 26 var recentItems = []; |
26 | 27 |
27 function renderRecentlyClosed() { | 28 function renderRecentlyClosed() { |
28 // Remove all existing items and create new items. | 29 // Remove all existing items and create new items. |
29 var recentElement = $('recently-closed'); | 30 var recentElement = $('recently-closed'); |
30 var parentEl = recentElement.lastElementChild; | 31 var parentEl = recentElement.lastElementChild; |
31 parentEl.textContent = ''; | 32 parentEl.textContent = ''; |
32 | 33 |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
80 | 81 |
81 var oldLayoutMode = layoutMode; | 82 var oldLayoutMode = layoutMode; |
82 var b = useSmallGrid(); | 83 var b = useSmallGrid(); |
83 layoutMode = b ? LayoutMode.SMALL : LayoutMode.NORMAL | 84 layoutMode = b ? LayoutMode.SMALL : LayoutMode.NORMAL |
84 | 85 |
85 if (layoutMode != oldLayoutMode){ | 86 if (layoutMode != oldLayoutMode){ |
86 mostVisited.useSmallGrid = b; | 87 mostVisited.useSmallGrid = b; |
87 mostVisited.layout(); | 88 mostVisited.layout(); |
88 renderRecentlyClosed(); | 89 renderRecentlyClosed(); |
89 } | 90 } |
91 | |
92 layoutSections(); | |
93 } | |
94 | |
95 // Stores some information about each section necessary to layout. A new | |
96 // instance is constructed for each section on each layout. | |
97 function SectionLayoutInfo(section) { | |
98 this.section = section; | |
99 this.header = section.getElementsByTagName('h2')[0]; | |
100 this.miniview = section.getElementsByClassName('miniview')[0]; | |
101 this.maxiview = section.getElementsByClassName('maxiview')[0]; | |
102 this.expanded = !section.classList.contains('hidden'); | |
103 this.fixedHeight = this.header.offsetHeight; | |
104 this.scrollingHeight = 0; | |
105 | |
106 if (this.expanded) { | |
107 this.scrollingHeight = this.maxiview.offsetHeight; | |
108 } else if (this.miniview) { | |
109 this.fixedHeight += this.miniview.offsetHeight; | |
110 } | |
111 } | |
112 | |
113 // Get all sections to be layed out. | |
114 SectionLayoutInfo.getAll = function() { | |
115 var sections = document.querySelectorAll('.section:not(.disabled)'); | |
116 var result = []; | |
117 for (var i = 0, section; section = sections[i]; i++) { | |
118 result.push(new SectionLayoutInfo(section)); | |
119 } | |
120 return result; | |
121 }; | |
122 | |
123 // Layout the sections in a modified accordian. The header and miniview, if | |
arv (Not doing code reviews)
2010/08/31 21:13:59
accordion
| |
124 // visible are fixed within the viewport. If there is an expanded section, its | |
125 // it scrolls. | |
126 // | |
127 // ============================= | |
128 // | collapsed section | <- Any collapsed sections are fixed position. | |
129 // | and miniview | | |
130 // |---------------------------| | |
131 // | expanded section | | |
132 // | | <- There can be one expanded section and it | |
133 // | and maxiview | is absolutely positioned so that it can | |
134 // | | scroll "underneath" the fixed elements. | |
135 // | | | |
136 // |---------------------------| | |
137 // | another collapsed section | | |
138 // |---------------------------| | |
139 // | |
140 // We want the main frame scrollbar to be the one that scrolls the expanded | |
141 // region. To get this effect, we make the fixed elements position:fixed and the | |
142 // scrollable element position:absolute. We also artificially increase the | |
143 // height of the document so that it is possible to scroll down enough to | |
144 // display the end of the document, even with any fixed elements at the bottom | |
145 // of the viewport. | |
146 // | |
147 // There is a final twist: If the intrinsic height of the expanded section is | |
148 // less than the available height (because the window is tall), any collapsed | |
149 // sections sinch up and sit below the expanded section. This is so that we | |
150 // don't have a bunch of dead whitespace in the case of expanded sections that | |
151 // aren't very tall. | |
152 function layoutSections() { | |
153 var sections = SectionLayoutInfo.getAll(); | |
154 var expandedSection = null; | |
155 var headerHeight = 0; | |
156 var footerHeight = 0; | |
157 | |
158 // Calculate the height of the fixed elements above the expanded section. Also | |
159 // take note of the expanded section, if there is one. | |
160 var i; | |
161 var section; | |
162 for (i = 0; section = sections[i]; i++) { | |
163 headerHeight += section.fixedHeight; | |
164 if (section.expanded) { | |
165 expandedSection = section; | |
166 i++; | |
167 break; | |
168 } | |
169 } | |
170 | |
171 // Calculate the height of the fixed elements below the expanded section, if | |
172 // any. | |
173 for (; section = sections[i]; i++) { | |
174 footerHeight += section.fixedHeight; | |
175 } | |
176 | |
177 // Determine the height to use for the expanded section. If there isn't enough | |
178 // space to show the expanded section completely, this will be the available | |
179 // height. Otherwise, we use the intrinsic height of the expanded section. | |
180 var expandedSectionHeight; | |
181 if (expandedSection) { | |
182 var flexHeight = window.innerHeight - headerHeight - footerHeight; | |
183 if (flexHeight < expandedSection.scrollingHeight) { | |
184 expandedSectionHeight = flexHeight; | |
185 | |
186 // Also, artificially expand the height of the document so that we can see | |
187 // the entire expanded section. | |
188 // | |
189 // TODO(aa): Where does this come from? It is the difference between what | |
190 // we set document.body.style.height to and what | |
191 // document.body.scrollHeight measures afterward. I expect them to be the | |
192 // same if document.body has no margins. | |
arv (Not doing code reviews)
2010/08/31 21:13:59
I think we had a padding or margin on the body to
| |
193 var fudge = 44; | |
194 document.body.style.height = | |
195 headerHeight + | |
196 expandedSection.scrollingHeight + | |
197 footerHeight + | |
198 fudge + | |
199 'px'; | |
200 } else { | |
201 expandedSectionHeight = expandedSection.scrollingHeight; | |
202 document.body.style.height = ''; | |
203 } | |
204 } | |
205 | |
206 // Now position all the elements. | |
207 var y = 0; | |
208 for (i = 0, section; section = sections[i]; i++) { | |
209 section.header.style.top = y + 'px'; | |
210 y += section.header.offsetHeight; | |
211 | |
212 if (section.miniview) { | |
213 section.miniview.style.top = y + 'px'; | |
214 if (section != expandedSection) { | |
215 y += section.miniview.offsetHeight; | |
216 } | |
217 } | |
218 | |
219 if (section.maxiview) { | |
220 section.maxiview.style.top = y + 'px'; | |
221 if (section == expandedSection) { | |
222 y += expandedSectionHeight; | |
223 } | |
224 } | |
225 } | |
90 } | 226 } |
91 | 227 |
92 window.addEventListener('resize', handleWindowResize); | 228 window.addEventListener('resize', handleWindowResize); |
93 | 229 |
94 var sectionToElementMap; | 230 var sectionToElementMap; |
95 function getSectionElement(section) { | 231 function getSectionElement(section) { |
96 if (!sectionToElementMap) { | 232 if (!sectionToElementMap) { |
97 sectionToElementMap = {}; | 233 sectionToElementMap = {}; |
98 for (var key in Section) { | 234 for (var key in Section) { |
99 sectionToElementMap[Section[key]] = | 235 sectionToElementMap[Section[key]] = |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
140 * Callback when the shown sections changes in another NTP. | 276 * Callback when the shown sections changes in another NTP. |
141 * @param {number} newShownSections Bitmask of the shown sections. | 277 * @param {number} newShownSections Bitmask of the shown sections. |
142 */ | 278 */ |
143 function setShownSections(newShownSections) { | 279 function setShownSections(newShownSections) { |
144 for (var key in Section) { | 280 for (var key in Section) { |
145 if (newShownSections & Section[key]) | 281 if (newShownSections & Section[key]) |
146 showSection(Section[key]); | 282 showSection(Section[key]); |
147 else | 283 else |
148 hideSection(Section[key]); | 284 hideSection(Section[key]); |
149 } | 285 } |
286 layoutSections(); | |
150 } | 287 } |
151 | 288 |
152 // Recently closed | 289 // Recently closed |
153 | 290 |
154 function layoutRecentlyClosed() { | 291 function layoutRecentlyClosed() { |
155 var recentElement = $('recently-closed'); | 292 var recentElement = $('recently-closed'); |
156 // We cannot use clientWidth here since the width has a transition. | 293 // We cannot use clientWidth here since the width has a transition. |
157 var availWidth = useSmallGrid() ? 692 : 920; | 294 var availWidth = useSmallGrid() ? 692 : 920; |
158 var parentEl = recentElement.lastElementChild; | 295 var parentEl = recentElement.lastElementChild; |
159 | 296 |
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
296 var lines = []; | 433 var lines = []; |
297 var start = log[0][1]; | 434 var start = log[0][1]; |
298 | 435 |
299 for (var i = 0; i < log.length; i++) { | 436 for (var i = 0; i < log.length; i++) { |
300 lines.push((log[i][1] - start) + ': ' + log[i][0]); | 437 lines.push((log[i][1] - start) + ': ' + log[i][0]); |
301 } | 438 } |
302 | 439 |
303 console.log(lines.join('\n')); | 440 console.log(lines.join('\n')); |
304 } | 441 } |
305 | 442 |
306 // Updates the visibility of the menu items. | |
307 function updateOptionMenu() { | |
308 var menuItems = $('option-menu').children; | |
309 for (var i = 0; i < menuItems.length; i++) { | |
310 var item = menuItems[i]; | |
311 var command = item.getAttribute('command'); | |
312 if (command == 'show' || command == 'hide') { | |
313 var section = Section[item.getAttribute('section')]; | |
314 var visible = shownSections & section; | |
315 item.setAttribute('command', visible ? 'hide' : 'show'); | |
316 } | |
317 } | |
318 } | |
319 | |
320 // We apply the size class here so that we don't trigger layout animations | 443 // We apply the size class here so that we don't trigger layout animations |
321 // onload. | 444 // onload. |
322 | 445 |
323 handleWindowResize(); | 446 handleWindowResize(); |
324 | 447 |
325 var localStrings = new LocalStrings(); | 448 var localStrings = new LocalStrings(); |
326 | 449 |
327 /////////////////////////////////////////////////////////////////////////////// | 450 /////////////////////////////////////////////////////////////////////////////// |
328 // Things we know are not needed at startup go below here | 451 // Things we know are not needed at startup go below here |
329 | 452 |
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
409 | 532 |
410 /** | 533 /** |
411 * This handles the option menu. | 534 * This handles the option menu. |
412 * @param {Element} button The button element. | 535 * @param {Element} button The button element. |
413 * @param {Element} menu The menu element. | 536 * @param {Element} menu The menu element. |
414 * @constructor | 537 * @constructor |
415 */ | 538 */ |
416 function OptionMenu(button, menu) { | 539 function OptionMenu(button, menu) { |
417 this.button = button; | 540 this.button = button; |
418 this.menu = menu; | 541 this.menu = menu; |
542 this.button.onclick = bind(this.handleClick, this); | |
419 this.button.onmousedown = bind(this.handleMouseDown, this); | 543 this.button.onmousedown = bind(this.handleMouseDown, this); |
420 this.button.onkeydown = bind(this.handleKeyDown, this); | 544 this.button.onkeydown = bind(this.handleKeyDown, this); |
421 this.boundHideMenu_ = bind(this.hide, this); | 545 this.boundHideMenu_ = bind(this.hide, this); |
422 this.boundMaybeHide_ = bind(this.maybeHide_, this); | 546 this.boundMaybeHide_ = bind(this.maybeHide_, this); |
423 this.menu.onmouseover = bind(this.handleMouseOver, this); | 547 this.menu.onmouseover = bind(this.handleMouseOver, this); |
424 this.menu.onmouseout = bind(this.handleMouseOut, this); | 548 this.menu.onmouseout = bind(this.handleMouseOut, this); |
425 this.menu.onmouseup = bind(this.handleMouseUp, this); | 549 this.menu.onmouseup = bind(this.handleMouseUp, this); |
426 } | 550 } |
427 | 551 |
428 OptionMenu.prototype = { | 552 OptionMenu.prototype = { |
429 show: function() { | 553 show: function() { |
430 updateOptionMenu(); | |
431 this.positionMenu_(); | 554 this.positionMenu_(); |
432 this.menu.style.display = 'block'; | 555 this.menu.style.display = 'block'; |
433 this.button.classList.add('open'); | 556 this.button.classList.add('open'); |
434 this.button.focus(); | 557 this.button.focus(); |
435 | 558 |
436 // Listen to document and window events so that we hide the menu when the | 559 // Listen to document and window events so that we hide the menu when the |
437 // user clicks outside the menu or tabs away or the whole window is blurred. | 560 // user clicks outside the menu or tabs away or the whole window is blurred. |
438 document.addEventListener('focus', this.boundMaybeHide_, true); | 561 document.addEventListener('focus', this.boundMaybeHide_, true); |
439 document.addEventListener('mousedown', this.boundMaybeHide_, true); | 562 document.addEventListener('mousedown', this.boundMaybeHide_, true); |
440 }, | 563 }, |
441 | 564 |
442 positionMenu_: function() { | 565 positionMenu_: function() { |
443 this.menu.style.top = this.button.getBoundingClientRect().bottom + 'px'; | 566 var rect = this.button.getBoundingClientRect(); |
567 this.menu.style.top = rect.bottom + 'px'; | |
568 this.menu.style.right = (document.body.clientWidth - rect.right) + 'px' | |
444 }, | 569 }, |
445 | 570 |
446 hide: function() { | 571 hide: function() { |
447 this.menu.style.display = 'none'; | 572 this.menu.style.display = 'none'; |
448 this.button.classList.remove('open'); | 573 this.button.classList.remove('open'); |
449 this.setSelectedIndex(-1); | 574 this.setSelectedIndex(-1); |
450 | 575 |
451 document.removeEventListener('focus', this.boundMaybeHide_, true); | 576 document.removeEventListener('focus', this.boundMaybeHide_, true); |
452 document.removeEventListener('mousedown', this.boundMaybeHide_, true); | 577 document.removeEventListener('mousedown', this.boundMaybeHide_, true); |
453 }, | 578 }, |
(...skipping 15 matching lines...) Expand all Loading... | |
469 }, | 594 }, |
470 | 595 |
471 handleMouseDown: function(e) { | 596 handleMouseDown: function(e) { |
472 if (this.isShown()) { | 597 if (this.isShown()) { |
473 this.hide(); | 598 this.hide(); |
474 } else { | 599 } else { |
475 this.show(); | 600 this.show(); |
476 } | 601 } |
477 }, | 602 }, |
478 | 603 |
604 handleClick: function(e) { | |
605 e.stopPropagation(); | |
606 }, | |
607 | |
479 handleMouseOver: function(e) { | 608 handleMouseOver: function(e) { |
480 var el = e.target; | 609 var el = e.target; |
481 if (!el.hasAttribute('command')) { | 610 if (!el.hasAttribute('command')) { |
482 this.setSelectedIndex(-1); | 611 this.setSelectedIndex(-1); |
483 } else { | 612 } else { |
484 var index = Array.prototype.indexOf.call(this.menu.children, el); | 613 var index = Array.prototype.indexOf.call(this.menu.children, el); |
485 this.setSelectedIndex(index); | 614 this.setSelectedIndex(index); |
486 } | 615 } |
487 }, | 616 }, |
488 | 617 |
(...skipping 91 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
580 executeItem: function(item) { | 709 executeItem: function(item) { |
581 var command = item.getAttribute('command'); | 710 var command = item.getAttribute('command'); |
582 if (command in this.commands) { | 711 if (command in this.commands) { |
583 this.commands[command].call(this, item); | 712 this.commands[command].call(this, item); |
584 } | 713 } |
585 | 714 |
586 this.hide(); | 715 this.hide(); |
587 } | 716 } |
588 }; | 717 }; |
589 | 718 |
590 // TODO(aa): The 'clear-all-blacklisted' feature needs to move into a menu in | 719 var optionMenu = new OptionMenu( |
591 // the most visited section. | 720 document.querySelector('#most-visited-section h2 .settings'), |
592 /* | 721 $('option-menu')); |
593 var optionMenu = new OptionMenu($('option-button'), $('option-menu')); | |
594 optionMenu.commands = { | 722 optionMenu.commands = { |
595 'clear-all-blacklisted' : function() { | 723 'clear-all-blacklisted' : function() { |
596 mostVisited.clearAllBlacklisted(); | 724 mostVisited.clearAllBlacklisted(); |
597 chrome.send('getMostVisited'); | 725 chrome.send('getMostVisited'); |
598 }, | |
599 'show': function(item) { | |
600 var section = Section[item.getAttribute('section')]; | |
601 showSection(section); | |
602 saveShownSections(); | |
603 }, | |
604 'hide': function(item) { | |
605 var section = Section[item.getAttribute('section')]; | |
606 hideSection(section); | |
607 saveShownSections(); | |
608 } | 726 } |
609 }; | 727 }; |
610 */ | |
611 | 728 |
612 $('main').addEventListener('click', function(e) { | 729 $('main').addEventListener('click', function(e) { |
613 var p = e.target; | 730 var p = e.target; |
614 while (p && p.tagName != 'H2') { | 731 while (p && p.tagName != 'H2') { |
615 p = p.parentNode; | 732 p = p.parentNode; |
616 } | 733 } |
617 | 734 |
618 if (!p) { | 735 if (!p) { |
619 return; | 736 return; |
620 } | 737 } |
621 | 738 |
622 p = p.parentNode; | 739 p = p.parentNode; |
623 if (p.noexpand) { | 740 if (p.noexpand) { |
624 return; | 741 return; |
625 } | 742 } |
626 | 743 |
627 var section = p.getAttribute('section'); | 744 var section = p.getAttribute('section'); |
628 if (section) { | 745 if (section) { |
629 if (shownSections & Section[section]) | 746 if (shownSections & Section[section]) { |
630 hideSection(Section[section]); | 747 hideSection(Section[section]); |
631 else | 748 } else { |
632 showSection(Section[section]); | 749 for (var p in Section) { |
750 if (p == section) | |
751 showSection(Section[p]); | |
752 else | |
753 hideSection(Section[p]); | |
754 } | |
755 } | |
756 layoutSections(); | |
633 saveShownSections(); | 757 saveShownSections(); |
634 } | 758 } |
635 }); | 759 }); |
636 | 760 |
637 function handleIfEnterKey(f) { | 761 function handleIfEnterKey(f) { |
638 return function(e) { | 762 return function(e) { |
639 if (e.keyIdentifier == 'Enter') { | 763 if (e.keyIdentifier == 'Enter') { |
640 f(e); | 764 f(e); |
641 } | 765 } |
642 }; | 766 }; |
(...skipping 171 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
814 * make sure we don't send the initial sync message to the backend unless the | 938 * make sure we don't send the initial sync message to the backend unless the |
815 * backend told us that the sync code is present. | 939 * backend told us that the sync code is present. |
816 */ | 940 */ |
817 function callGetSyncMessageIfSyncIsPresent() { | 941 function callGetSyncMessageIfSyncIsPresent() { |
818 if (document.documentElement.getAttribute('syncispresent') == 'true') { | 942 if (document.documentElement.getAttribute('syncispresent') == 'true') { |
819 chrome.send('GetSyncMessage'); | 943 chrome.send('GetSyncMessage'); |
820 } | 944 } |
821 } | 945 } |
822 | 946 |
823 function hideAllMenus() { | 947 function hideAllMenus() { |
824 // TODO(aa): See comment in definition of optionMenu. | 948 optionMenu.hide(); |
825 //optionMenu.hide(); | |
826 } | 949 } |
827 | 950 |
828 window.addEventListener('blur', hideAllMenus); | 951 window.addEventListener('blur', hideAllMenus); |
829 window.addEventListener('keydown', function(e) { | 952 window.addEventListener('keydown', function(e) { |
830 if (e.keyIdentifier == 'Alt' || e.keyIdentifier == 'Meta') { | 953 if (e.keyIdentifier == 'Alt' || e.keyIdentifier == 'Meta') { |
831 hideAllMenus(); | 954 hideAllMenus(); |
832 } | 955 } |
833 }, true); | 956 }, true); |
834 | 957 |
835 // Tooltip for elements that have text that overflows. | 958 // Tooltip for elements that have text that overflows. |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
871 while (el.hasChildNodes()) { | 994 while (el.hasChildNodes()) { |
872 span.appendChild(el.firstChild); | 995 span.appendChild(el.firstChild); |
873 } | 996 } |
874 el.appendChild(span); | 997 el.appendChild(span); |
875 } | 998 } |
876 | 999 |
877 updateAttribution(); | 1000 updateAttribution(); |
878 | 1001 |
879 var mostVisited = new MostVisited( | 1002 var mostVisited = new MostVisited( |
880 $('most-visited'), | 1003 $('most-visited'), |
881 $('most-visited-section').getElementsByClassName('miniview')[0], | 1004 document.querySelector('#most-visited-section .miniview'), |
882 useSmallGrid(), | 1005 useSmallGrid(), |
883 shownSections & Section.THUMB); | 1006 shownSections & Section.THUMB); |
884 | 1007 |
885 function mostVisitedPages(data, firstRun) { | 1008 function mostVisitedPages(data, firstRun) { |
886 logEvent('received most visited pages'); | 1009 logEvent('received most visited pages'); |
887 | 1010 |
888 mostVisited.data = data; | 1011 mostVisited.data = data; |
889 mostVisited.layout(); | 1012 mostVisited.layout(); |
1013 layoutSections(); | |
890 | 1014 |
891 loading = false; | 1015 loading = false; |
892 | 1016 |
893 // Remove class name in a timeout so that changes done in this JS thread are | 1017 // Remove class name in a timeout so that changes done in this JS thread are |
894 // not animated. | 1018 // not animated. |
895 window.setTimeout(function() { | 1019 window.setTimeout(function() { |
896 mostVisited.ensureSmallGridCorrect(); | 1020 mostVisited.ensureSmallGridCorrect(); |
897 document.body.classList.remove('loading'); | 1021 document.body.classList.remove('loading'); |
898 }, 1); | 1022 }, 1); |
899 | 1023 |
900 // Only show the first run notification if first run. | 1024 // Only show the first run notification if first run. |
901 if (firstRun) { | 1025 if (firstRun) { |
902 showFirstRunNotification(); | 1026 showFirstRunNotification(); |
903 } | 1027 } |
904 } | 1028 } |
OLD | NEW |