Commit b8e3e44b authored by S Anand's avatar S Anand

ENH: add path change event to urlchange. Fixes #128

parent d546223f
Pipeline #72679 passed with stage
in 2 minutes and 10 seconds
# Change log
- `0.12.0`:
- [$.urlchange](#urlchange) fires `#/` on path changes.
**Breaking change**: it fires `#` instead of `#?`.
- `0.11.0`: 8 Dec 2018
- [$.urlchange](#urlchange) acts as an event listener for URL hash changes
triggered by [$.urlfilter](#urlfilter) -- making bookmarkable pages easier
......
......@@ -39,24 +39,36 @@ $(window)
Examples:
- `#?x=1` triggers 2 events on `window`:
- `.on('#?', function(e) { e.hash.searchKey.x == '1' }`
- `.on('#?x', function(e, val) { val == '1' && e.vals == ['1'] })`
- `.on('#', function(e) { e.hash.searchKey.x == '1' }`
- `.on('#?x', function(e, val) { val == '1' && && e.old == [] })`
- When the URL changes from `#?x=1` to `#?x=1&y=2`, it triggers 2 events on `window`:
- `.on('#?', function(e) { e.hash.searchKey == {x: '1', y: '2'} }`
- `.on('#?y', function(e, val) { val == '2' && e.vals == ['2'] })`
- `.on('#', function(e) { e.hash.searchKey == {x: '1', y: '2'} }`
- `.on('#?y', function(e, val) { val == '2' && e.old == [] })`
- No `#?x` event is fired, since x has not changed
- When the URL changes from `#?x=1` to `#?x=1&x=2`, it triggers 2 events on `window`:
- `.on('#?', function(e) { e.hash.searchKey == {x: '1', y: '2'} }`
- `.on('#?x', function(e, val) { val == '1' && e.vals == ['1', '2'] })`
- `.on('#', function(e) { e.hash.searchKey == {x: '1', y: '2'} }`
- `.on('#?x', function(e, val) { val == '1' && && e.old == ['1'] })`
- The `#?x` event is fired since x has a new value
- When the URL changes from `#a` to `#b`, it triggers 2 events on `window`:
- `.on('#', function(e) { e.hash.pathname == 'b' }`
- `.on('#/', function(e, val) { val == 'b' && e.old == 'a' }`
## $.urlchange events
- `#?` is fired when the URL hash changes. Event attributes are:
- `hash`: a [g1.url.parse](#urlparse) object that holds the parsed URL hash
- `#?<key>` is fired for *each* key that is changed. Event attributes are:
- `hash`: a [g1.url.parse](#urlparse) object that holds the parsed URL hash
- `vals`: a list of values in matching `#?<key>`.
For example, `#?x=1&x=2` triggers `#?x` with `vals=['1', '2']`
- `val`: the first value of `vals`.
- `.on('#', function(e, val) {}` is fired when any part of the URL hash changes.
- `val` is the changed hash as a [URL](#urlparse) object.
- `e.change` is a dict with the changed key-values
For example, if the hash changes from `#a?x=1&y=1` to `#b?x=1&y=2`,
then `change={'/':'b', 'y': ['2']}`
- `e.hash` is the [parsed URL hash](#urlparse) object
- `e.old` is the previous [parsed URL hash](#urlparse) object
- `.on('#/', function(e, val) {}` is fired when the URL hash **path** changes.
- `val` is the changed path
- `e.hash` is the [parsed URL hash](#urlparse) object
- `e.old` is the previous [parsed URL hash](#urlparse) object
- `.on('#?<key>', function(e, val) {}` is fired when *any* URL hash **key** changes.
- `val` is the changed hash value
- `e.vals` is the list of new hash values -- same as `e.hash.searchList[<key>]`
- `e.hash` is the [parsed URL hash](#urlparse) object
- `e.old` is the previous [parsed URL hash](#urlparse) object
......@@ -8,24 +8,29 @@ export function urlchange() {
var $self = this
// $self / this is typically a window, but could also be an iframe. Use its location
var loc = $self.get(0).location
var oldhash = {}
return $self.on('hashchange.urlchange', function() {
var oldurl, oldpath, oldhash = {}
return $self.on('hashchange.urlchange', function () {
var url = parse(loc.hash.replace(/^#/, ''))
var change = {}
// Parse the URL hash and trigger "#?<key>" if a key has changed
for (var key in url.searchList) {
var vals = url.searchList[key]
if (!array_eq(vals, oldhash[key])) {
oldhash[key] = vals
// Parse keys in old & new URL hash. Trigger "#?<key>" if a key has changed
for (var key in $.extend({}, url.searchList, oldhash)) {
var vals = url.searchList[key] || []
var old = oldhash[key] || []
if (!array_eq(vals, old)) {
var val = vals.length > 0 ? vals[0] : ''
var e = { type: '#?' + key, hash: url, vals: vals, val: val }
change[key] = vals
$self.trigger(e, val)
$self.trigger({ type: '#?' + key, hash: url, vals: vals, old: oldurl }, val)
oldhash[key] = change[key] = vals
}
}
// If the path has changed, trigger a "#/" event
if (oldpath != url.pathname) {
$self.trigger({ type: '#/', hash: url, old: oldurl }, url.pathname)
change['/'] = oldpath = url.pathname
}
// If any part of the hash has changed, trigger a "#?"" event with all info
if (!$.isEmptyObject(change)) {
$self.trigger({ type: '#?', hash: url, change: change }, url)
$self.trigger({ type: '#', hash: url, change: change, old: oldurl }, url)
}
oldurl = url
}).trigger('hashchange')
}
......@@ -26,47 +26,65 @@
</script>
<script>
tape('#?高 triggers #? and #?高 events', function(t) {
tape('#?高 triggers # and #?高 events', function(t) {
reset()
t.plan(4)
$(window).one('#?.test', function(e, hash) {
t.deepEqual(hash.searchKey, { '': '' })
t.deepEqual(e.hash.searchKey, { '': '' })
t.plan(9)
$(window).one('#.test', function(e, hash) {
t.deepEqual(hash, e.hash)
t.deepEqual(hash.searchList, { '': [''] })
t.equal(e.old.pathname, '')
t.deepEqual(e.old.searchList, {})
t.deepEqual(e.change, { '': [''] })
})
$(window).one('#?高.test', function(e, val) {
t.equal(val, '')
t.deepEqual(e.hash.searchKey, { '': '' })
t.deepEqual(e.vals, [''])
t.deepEqual(e.hash.searchList, { '': [''] })
t.deepEqual(e.old.searchList, {})
})
window.location.hash = '#?高'
})
tape('#?高=λ&►=σ trigges #?, #?高 and #?► events', function(t) {
tape('#?高=λ&►=σ trigges #, #?高 and #?► events', function(t) {
reset()
t.plan(6)
$(window).one('#?.test', function (e, hash) {
t.deepEqual(hash.searchKey, { '': 'λ', '': 'σ' })
t.deepEqual(e.hash.searchKey, { '': 'λ', '': 'σ' })
t.plan(13)
$(window).one('#.test', function (e, hash) {
t.deepEqual(hash, e.hash)
t.deepEqual(hash.searchList, { '': ['λ'], '': ['σ'] })
t.equal(e.old.pathname, '')
t.deepEqual(e.old.searchList, {})
t.deepEqual(e.change, { '': ['λ'], '': ['σ'] })
})
$(window).one('#?高.test', function (e, val) {
t.equal(val, 'λ')
t.deepEqual(e.hash.searchKey, { '': 'λ', '': 'σ' })
t.deepEqual(e.vals, ['λ'])
t.deepEqual(e.hash.searchList, { '': ['λ'], '': ['σ'] })
t.deepEqual(e.old.searchList, {})
})
$(window).one('#?►.test', function (e, val) {
t.equal(val, 'σ')
t.deepEqual(e.hash.searchKey, { '': 'λ', '': 'σ' })
t.deepEqual(e.vals, ['σ'])
t.deepEqual(e.hash.searchList, { '': ['λ'], '': ['σ'] })
t.deepEqual(e.old.searchList, {})
})
window.location.hash = '#?高=λ&►=σ'
})
tape('Adding #?y=2 on triggers #?y events, not #?x', function(t) {
reset('#?x=1')
t.plan(3)
$(window).one('#?.test', function(e, hash) {
t.plan(9) // One less than no. of tests. #?x.test should not be fired
$(window).one('#.test', function (e, hash) {
t.deepEqual(hash, e.hash)
t.deepEqual(e.hash.searchList, { 'x': ['1'], 'y': ['2'] })
t.equal(e.old.pathname, '')
t.deepEqual(e.old.searchList, { 'x': ['1'] })
t.deepEqual(e.change, { 'y': ['2'] })
})
$(window).one('#?y.test', function (e, val) {
t.equal(val, '2')
t.deepEqual(e.vals, ['2'])
t.deepEqual(e.hash.searchList, { 'x': ['1'], 'y': ['2'] })
t.deepEqual(e.old.searchList, { 'x': ['1'] })
})
$(window).one('#?x.test', function (e, val) {
t.fail('#?x should not be triggered')
......@@ -74,6 +92,37 @@
window.location.hash = '#?x=1&y=2'
})
tape('#a?x=1&y=2&z=3 -> #b?x=1&x=2&y=3', function(t) {
reset('#a?x=1&y=2&z=3')
t.plan(18)
$(window).one('#.test', function (e, hash) {
t.deepEqual(hash, e.hash)
t.equal(e.old.pathname, 'a')
t.equal(hash.pathname, 'b')
t.deepEqual(e.old.searchList, { 'x': ['1'], 'y': ['2'], 'z': ['3'] })
t.deepEqual(hash.searchList, { 'x': ['1', '2'], 'y': ['3'] })
t.deepEqual(e.change, { 'x': ['1', '2'], 'y': ['3'], 'z': [], '/': 'b' })
})
$(window).one('#?x.test', function (e, val) {
t.equal(val, '1')
t.deepEqual(e.vals, ['1', '2'])
t.deepEqual(e.hash.searchList['x'], ['1', '2'])
t.deepEqual(e.old.searchList['x'], ['1'])
})
$(window).one('#?y.test', function (e, val) {
t.equal(val, '3')
t.deepEqual(e.vals, ['3'])
t.deepEqual(e.hash.searchList['y'], ['3'])
t.deepEqual(e.old.searchList['y'], ['2'])
})
$(window).one('#?z.test', function (e, val) {
t.equal(val, '')
t.deepEqual(e.vals, [])
t.deepEqual(e.hash.searchList['z'], undefined)
t.deepEqual(e.old.searchList['z'], ['3'])
})
window.location.hash = '#b?x=1&x=2&y=3'
})
</script>
</body>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment