1/*
2 * Copyright (C) 2014 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31/**
32 * @interface
33 */
34WebInspector.HistoryEntry = function() { }
35
36WebInspector.HistoryEntry.prototype = {
37    /**
38     * @return {boolean}
39     */
40    valid: function() { },
41
42    reveal: function() { }
43};
44
45/**
46 * @constructor
47 * @param {number} historyDepth
48 */
49WebInspector.SimpleHistoryManager = function(historyDepth)
50{
51    this._entries = [];
52    this._activeEntryIndex = -1;
53    this._coalescingReadonly = 0;
54    this._historyDepth = historyDepth;
55}
56
57WebInspector.SimpleHistoryManager.prototype = {
58    readOnlyLock: function()
59    {
60        ++this._coalescingReadonly;
61    },
62
63    releaseReadOnlyLock: function()
64    {
65        --this._coalescingReadonly;
66    },
67
68    /**
69     * @return {boolean}
70     */
71    readOnly: function()
72    {
73        return !!this._coalescingReadonly;
74    },
75
76    /**
77     * @param {!function(!WebInspector.HistoryEntry):boolean} filterOutCallback
78     */
79    filterOut: function(filterOutCallback)
80    {
81        if (this.readOnly())
82            return;
83        var filteredEntries = [];
84        var removedBeforeActiveEntry = 0;
85        for (var i = 0; i < this._entries.length; ++i) {
86            if (!filterOutCallback(this._entries[i])) {
87                filteredEntries.push(this._entries[i]);
88            } else if (i <= this._activeEntryIndex)
89                ++removedBeforeActiveEntry;
90        }
91        this._entries = filteredEntries;
92        this._activeEntryIndex = Math.max(0, this._activeEntryIndex - removedBeforeActiveEntry);
93    },
94
95    /**
96     * @return {boolean}
97     */
98    empty: function()
99    {
100        return !this._entries.length;
101    },
102
103    /**
104     * @return {?WebInspector.HistoryEntry}
105     */
106    active: function()
107    {
108        return this.empty() ? null : this._entries[this._activeEntryIndex];
109    },
110
111    /**
112     * @param {!WebInspector.HistoryEntry} entry
113     */
114    push: function(entry)
115    {
116        if (this.readOnly())
117            return;
118        if (!this.empty())
119            this._entries.splice(this._activeEntryIndex + 1);
120        this._entries.push(entry);
121        if (this._entries.length > this._historyDepth)
122            this._entries.shift();
123        this._activeEntryIndex = this._entries.length - 1;
124    },
125
126    /**
127     * @return {boolean}
128     */
129    rollback: function()
130    {
131        if (this.empty())
132            return false;
133
134        var revealIndex = this._activeEntryIndex - 1;
135        while (revealIndex >= 0 && !this._entries[revealIndex].valid())
136            --revealIndex;
137        if (revealIndex < 0)
138            return false;
139
140        this.readOnlyLock();
141        this._entries[revealIndex].reveal();
142        this.releaseReadOnlyLock();
143
144        this._activeEntryIndex = revealIndex;
145        return true;
146    },
147
148    /**
149     * @return {boolean}
150     */
151    rollover: function()
152    {
153        var revealIndex = this._activeEntryIndex + 1;
154
155        while (revealIndex < this._entries.length && !this._entries[revealIndex].valid())
156            ++revealIndex;
157        if (revealIndex >= this._entries.length)
158            return false;
159
160        this.readOnlyLock();
161        this._entries[revealIndex].reveal();
162        this.releaseReadOnlyLock();
163
164        this._activeEntryIndex = revealIndex;
165        return true;
166    },
167};
168