forked from bookieio/Bookie
-
Notifications
You must be signed in to change notification settings - Fork 3
/
history.js
188 lines (174 loc) · 5.84 KB
/
history.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
/**
* History Module, helping us wrap/deal with history changes.
*
* @namespace bookie
* @module history
*
*/
YUI.add('bookie-history-module', function (Y) {
var ns = Y.namespace('bookie');
function trim(str, chars) {
return ltrim(rtrim(str, chars), chars);
}
function ltrim(str, chars) {
chars = chars || "\s";
return str.replace(new RegExp("^[" + chars + "]+", "g"), "");
}
function rtrim(str, chars) {
chars = chars || "\s";
return str.replace(new RegExp("[" + chars + "]+$", "g"), "");
}
/**
* Manage out interaction with the HTML5 history implementation.
*
* @class BmarkListHistory
* @extends Y.Base
*
*/
ns.BmarkListHistory = Y.Base.create('bookie-history', Y.Base, [], {
/**
* Watch for any changes that should effect the history.
*
* @method _bind_events
* @private
*
*/
_bind_events: function () {
// not sure how I can watch for any attr change on the pager as a
// whole
this.get('pager').after('countChange', this._update, this);
this.get('pager').after('pageChange', this._update, this);
this.after('termsChange', this._update, this);
// also watch for new tags being added
// this will trigger a terms update and should rebuild our
// url/state
Y.on('tag:changed', function (e) {
this.set('terms', e.tags);
}, this);
// watch the history for pop states such as when a user hits back,
// etc
Y.on('history:change', function (ev) {
// if we've got an empty history, then set the current passed
// info as the initial state (chrome popevent on page load for
// instance)
if (ev.src === Y.HistoryHTML5.SRC_POPSTATE) {
if (!this.history.get('pager') && !Y.Lang.isValue(ev.newval)) {
this.get('pager').set('page', 0);
} else {
// ok so now we need to replace the information based
// on the newVal page and terms list
if (ev.newVal.pager.page !== this.get('pager').get('page')) {
this.get('pager').set('page', ev.newVal.pager.page);
}
}
}
}, this);
},
/**
* Construct a new url based on our data and route specified for the
* current state of things.
*
* @method _build_url
* @private
*
*/
_build_url: function () {
var terms = this.get('terms').join('/');
var pager = this.get('pager');
var qs = Y.QueryString.stringify({
count: pager.get('count'),
page: pager.get('page')
});
var updated_url = rtrim(this.get('route'), '/') + '/' + terms + '?' + qs;
return '/' + ltrim(updated_url, '/');
},
/**
* Update the history based on the current state of the pager, terms
*
* @method _update
* @param {Event} ev
* @private
*
*/
_update: function (ev) {
// COMPLETE HACK ALERT @todo
// The issue is that when we catch pop events we often have to
// catch/update the pager's page manaully. This fires a change
// event and a pop state ends up running an add event because of
// the code below.
// Since most progressions are up pages/back pages, we're going to
// attempt to only add to the history if this was a valid add
// event, the new page is > prev page.
if (ev.newVal > ev.prevVal) {
var new_url = this._build_url();
this.history.add({
pager: this.get('pager').getAttrs(),
terms: this.get('terms')
}, {
title: 'Viewing page: ' + this.get('pager').get('page') + 1,
url : new_url
});
}
},
/**
* General initializer
*
* @method initializer
* @param {Object} cfg
*
*/
initializer: function (cfg) {
var pager = this.get('pager');
this.history = new Y.History({
pager: pager.getAttrs(),
terms: this.get('terms')
});
this._bind_events();
}
}, {
ATTRS: {
/**
* @attribute the pager object weneed to construct our current
* state from
*
* @default undefined
* @type PagerModel
* @required
*
*/
pager: {
required: true
},
/**
* The search terms/tags that have been used to generate the
* current view.
*
* @attribute terms
* @default []
* @type Array
*
*/
terms: {
value: [],
},
/**
* What is the http route we're building a history for? For
* instance, if we've loaded the /recent url, we'll need to append
* our history params to the /recent url to build:
*
* /recent/term1?count=50&page=2
*
* @attribute route
* @default undefined
* @type string
* @required
*
*/
route: {
required: true
}
}
});
}, '0.1', {
requires: ['history', 'querystring', 'base']
});