Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
Y
yii2
Project
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
PSDI Army
yii2
Commits
04f381eb
Commit
04f381eb
authored
Jan 30, 2014
by
Paul Klimov
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' of github.com:yiisoft/yii2
parents
518954da
afefcd6e
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
574 additions
and
565 deletions
+574
-565
jquery.pjax.js
framework/assets/pjax/jquery.pjax.js
+565
-564
Pjax.php
framework/widgets/Pjax.php
+9
-1
No files found.
framework/assets/pjax/jquery.pjax.js
View file @
04f381eb
...
...
@@ -27,15 +27,15 @@
// the options object.
//
// Returns the jQuery object
function
fnPjax
(
selector
,
container
,
options
)
{
var
context
=
this
return
this
.
on
(
'click.pjax'
,
selector
,
function
(
event
)
{
var
opts
=
$
.
extend
({},
optionsFor
(
container
,
options
))
if
(
!
opts
.
container
)
opts
.
container
=
$
(
this
).
attr
(
'data-pjax'
)
||
context
handleClick
(
event
,
opts
)
})
}
function
fnPjax
(
selector
,
container
,
options
)
{
var
context
=
this
return
this
.
on
(
'click.pjax'
,
selector
,
function
(
event
)
{
var
opts
=
$
.
extend
({},
optionsFor
(
container
,
options
))
if
(
!
opts
.
container
)
opts
.
container
=
$
(
this
).
attr
(
'data-pjax'
)
||
context
handleClick
(
event
,
opts
)
})
}
// Public: pjax on click handler
//
...
...
@@ -56,47 +56,48 @@ function fnPjax(selector, container, options) {
// })
//
// Returns nothing.
function
handleClick
(
event
,
container
,
options
)
{
options
=
optionsFor
(
container
,
options
)
function
handleClick
(
event
,
container
,
options
)
{
options
=
optionsFor
(
container
,
options
)
var
link
=
event
.
currentTarget
var
link
=
event
.
currentTarget
if
(
link
.
tagName
.
toUpperCase
()
!==
'A'
)
throw
"$.fn.pjax or $.pjax.click requires an anchor element"
if
(
link
.
tagName
.
toUpperCase
()
!==
'A'
)
throw
"$.fn.pjax or $.pjax.click requires an anchor element"
// Middle click, cmd click, and ctrl click should open
// links in a new tab as normal.
if
(
event
.
which
>
1
||
event
.
metaKey
||
event
.
ctrlKey
||
event
.
shiftKey
||
event
.
altKey
)
return
// Middle click, cmd click, and ctrl click should open
// links in a new tab as normal.
if
(
event
.
which
>
1
||
event
.
metaKey
||
event
.
ctrlKey
||
event
.
shiftKey
||
event
.
altKey
)
return
// Ignore cross origin links
if
(
location
.
protocol
!==
link
.
protocol
||
location
.
hostname
!==
link
.
hostname
)
return
// Ignore cross origin links
if
(
location
.
protocol
!==
link
.
protocol
||
location
.
hostname
!==
link
.
hostname
)
return
// Ignore anchors on the same page
if
(
link
.
hash
&&
link
.
href
.
replace
(
link
.
hash
,
''
)
===
location
.
href
.
replace
(
location
.
hash
,
''
))
return
// Ignore anchors on the same page
if
(
link
.
hash
&&
link
.
href
.
replace
(
link
.
hash
,
''
)
===
location
.
href
.
replace
(
location
.
hash
,
''
))
return
// Ignore empty anchor "foo.html#"
if
(
link
.
href
===
location
.
href
+
'#'
)
return
// Ignore empty anchor "foo.html#"
if
(
link
.
href
===
location
.
href
+
'#'
)
return
var
defaults
=
{
url
:
link
.
href
,
container
:
$
(
link
).
attr
(
'data-pjax'
),
target
:
link
}
var
defaults
=
{
url
:
link
.
href
,
container
:
$
(
link
).
attr
(
'data-pjax'
),
target
:
link
}
var
opts
=
$
.
extend
({},
defaults
,
options
)
var
clickEvent
=
$
.
Event
(
'pjax:click'
)
$
(
link
).
trigger
(
clickEvent
,
[
opts
])
var
opts
=
$
.
extend
({},
defaults
,
options
)
var
clickEvent
=
$
.
Event
(
'pjax:click'
)
$
(
link
).
trigger
(
clickEvent
,
[
opts
])
if
(
!
clickEvent
.
isDefaultPrevented
())
{
pjax
(
opts
)
event
.
preventDefault
()
}
}
if
(
!
clickEvent
.
isDefaultPrevented
())
{
pjax
(
opts
)
event
.
preventDefault
()
$
(
link
).
trigger
(
'pjax:clicked'
,
[
opts
])
}
}
// Public: pjax on form submit handler
//
...
...
@@ -113,26 +114,26 @@ function handleClick(event, container, options) {
// })
//
// Returns nothing.
function
handleSubmit
(
event
,
container
,
options
)
{
options
=
optionsFor
(
container
,
options
)
function
handleSubmit
(
event
,
container
,
options
)
{
options
=
optionsFor
(
container
,
options
)
var
form
=
event
.
currentTarget
var
form
=
event
.
currentTarget
if
(
form
.
tagName
.
toUpperCase
()
!==
'FORM'
)
throw
"$.pjax.submit requires a form element"
if
(
form
.
tagName
.
toUpperCase
()
!==
'FORM'
)
throw
"$.pjax.submit requires a form element"
var
defaults
=
{
type
:
form
.
method
.
toUpperCase
(),
url
:
form
.
action
,
data
:
$
(
form
).
serializeArray
(),
container
:
$
(
form
).
attr
(
'data-pjax'
),
target
:
form
}
var
defaults
=
{
type
:
form
.
method
.
toUpperCase
(),
url
:
form
.
action
,
data
:
$
(
form
).
serializeArray
(),
container
:
$
(
form
).
attr
(
'data-pjax'
),
target
:
form
}
pjax
(
$
.
extend
({},
defaults
,
options
))
pjax
(
$
.
extend
({},
defaults
,
options
))
event
.
preventDefault
()
}
event
.
preventDefault
()
}
// Loads a URL with ajax, puts the response body inside a container,
// then pushState()'s the loaded URL.
...
...
@@ -153,212 +154,212 @@ function handleSubmit(event, container, options) {
// console.log( xhr.readyState )
//
// Returns whatever $.ajax returns.
function
pjax
(
options
)
{
options
=
$
.
extend
(
true
,
{},
$
.
ajaxSettings
,
pjax
.
defaults
,
options
)
if
(
$
.
isFunction
(
options
.
url
))
{
options
.
url
=
options
.
url
()
}
var
target
=
options
.
target
var
hash
=
parseURL
(
options
.
url
).
hash
function
pjax
(
options
)
{
options
=
$
.
extend
(
true
,
{},
$
.
ajaxSettings
,
pjax
.
defaults
,
options
)
if
(
$
.
isFunction
(
options
.
url
))
{
options
.
url
=
options
.
url
()
}
var
target
=
options
.
target
var
hash
=
parseURL
(
options
.
url
).
hash
var
context
=
options
.
context
=
findContainerFor
(
options
.
container
)
// We want the browser to maintain two separate internal caches: one
// for pjax'd partial page loads and one for normal page loads.
// Without adding this secret parameter, some browsers will often
// confuse the two.
if
(
!
options
.
data
)
options
.
data
=
{}
options
.
data
.
_pjax
=
context
.
selector
function
fire
(
type
,
args
)
{
var
event
=
$
.
Event
(
type
,
{
relatedTarget
:
target
})
context
.
trigger
(
event
,
args
)
return
!
event
.
isDefaultPrevented
()
}
var
timeoutTimer
options
.
beforeSend
=
function
(
xhr
,
settings
)
{
// No timeout for non-GET requests
// Its not safe to request the resource again with a fallback method.
if
(
settings
.
type
!==
'GET'
)
{
settings
.
timeout
=
0
}
xhr
.
setRequestHeader
(
'X-PJAX'
,
'true'
)
xhr
.
setRequestHeader
(
'X-PJAX-Container'
,
context
.
selector
)
if
(
!
fire
(
'pjax:beforeSend'
,
[
xhr
,
settings
]))
return
false
if
(
settings
.
timeout
>
0
)
{
timeoutTimer
=
setTimeout
(
function
()
{
if
(
fire
(
'pjax:timeout'
,
[
xhr
,
options
]))
xhr
.
abort
(
'timeout'
)
},
settings
.
timeout
)
// Clear timeout setting so jquerys internal timeout isn't invoked
settings
.
timeout
=
0
}
options
.
requestUrl
=
parseURL
(
settings
.
url
).
href
}
options
.
complete
=
function
(
xhr
,
textStatus
)
{
if
(
timeoutTimer
)
clearTimeout
(
timeoutTimer
)
fire
(
'pjax:complete'
,
[
xhr
,
textStatus
,
options
])
fire
(
'pjax:end'
,
[
xhr
,
options
])
}
options
.
error
=
function
(
xhr
,
textStatus
,
errorThrown
)
{
var
container
=
extractContainer
(
""
,
xhr
,
options
)
var
allowed
=
fire
(
'pjax:error'
,
[
xhr
,
textStatus
,
errorThrown
,
options
])
if
(
options
.
type
==
'GET'
&&
textStatus
!==
'abort'
&&
allowed
)
{
locationReplace
(
container
.
url
)
}
}
options
.
success
=
function
(
data
,
status
,
xhr
)
{
// If $.pjax.defaults.version is a function, invoke it first.
// Otherwise it can be a static string.
var
currentVersion
=
(
typeof
$
.
pjax
.
defaults
.
version
===
'function'
)
?
$
.
pjax
.
defaults
.
version
()
:
$
.
pjax
.
defaults
.
version
var
latestVersion
=
xhr
.
getResponseHeader
(
'X-PJAX-Version'
)
var
container
=
extractContainer
(
data
,
xhr
,
options
)
// If there is a layout version mismatch, hard load the new url
if
(
currentVersion
&&
latestVersion
&&
currentVersion
!==
latestVersion
)
{
locationReplace
(
container
.
url
)
return
}
// If the new response is missing a body, hard load the page
if
(
!
container
.
contents
)
{
locationReplace
(
container
.
url
)
return
}
pjax
.
state
=
{
id
:
options
.
id
||
uniqueId
(),
url
:
container
.
url
,
title
:
container
.
title
,
container
:
context
.
selector
,
fragment
:
options
.
fragment
,
timeout
:
options
.
timeout
}
if
(
options
.
push
||
options
.
replace
)
{
window
.
history
.
replaceState
(
pjax
.
state
,
container
.
title
,
container
.
url
)
}
// Clear out any focused controls before inserting new page contents.
document
.
activeElement
.
blur
()
if
(
container
.
title
)
document
.
title
=
container
.
title
context
.
html
(
container
.
contents
)
// FF bug: Won't autofocus fields that are inserted via JS.
// This behavior is incorrect. So if theres no current focus, autofocus
// the last field.
//
// http://www.w3.org/html/wg/drafts/html/master/forms.html
var
autofocusEl
=
context
.
find
(
'input[autofocus], textarea[autofocus]'
).
last
()[
0
]
if
(
autofocusEl
&&
document
.
activeElement
!==
autofocusEl
)
{
autofocusEl
.
focus
();
}
executeScriptTags
(
container
.
scripts
)
// Scroll to top by default
if
(
typeof
options
.
scrollTo
===
'number'
)
$
(
window
).
scrollTop
(
options
.
scrollTo
)
// If the URL has a hash in it, make sure the browser
// knows to navigate to the hash.
if
(
hash
!==
''
)
{
// Avoid using simple hash set here. Will add another history
// entry. Replace the url with replaceState and scroll to target
// by hand.
//
// window.location.hash = hash
var
url
=
parseURL
(
container
.
url
)
url
.
hash
=
hash
pjax
.
state
.
url
=
url
.
href
window
.
history
.
replaceState
(
pjax
.
state
,
container
.
title
,
url
.
href
)
var
target
=
$
(
url
.
hash
)
if
(
target
.
length
)
$
(
window
).
scrollTop
(
target
.
offset
().
top
)
}
fire
(
'pjax:success'
,
[
data
,
status
,
xhr
,
options
])
}
// Initialize pjax.state for the initial page load. Assume we're
// using the container and options of the link we're loading for the
// back button to the initial page. This ensures good back button
// behavior.
if
(
!
pjax
.
state
)
{
pjax
.
state
=
{
id
:
uniqueId
(),
url
:
window
.
location
.
href
,
title
:
document
.
title
,
container
:
context
.
selector
,
fragment
:
options
.
fragment
,
timeout
:
options
.
timeout
}
window
.
history
.
replaceState
(
pjax
.
state
,
document
.
title
)
}
// Cancel the current request if we're already pjaxing
var
xhr
=
pjax
.
xhr
if
(
xhr
&&
xhr
.
readyState
<
4
)
{
xhr
.
onreadystatechange
=
$
.
noop
xhr
.
abort
()
}
pjax
.
options
=
options
var
xhr
=
pjax
.
xhr
=
$
.
ajax
(
options
)
if
(
xhr
.
readyState
>
0
)
{
if
(
options
.
push
&&
!
options
.
replace
)
{
// Cache current container element before replacing it
cachePush
(
pjax
.
state
.
id
,
context
.
clone
().
contents
())
window
.
history
.
pushState
(
null
,
""
,
stripPjaxParam
(
options
.
requestUrl
))
}
fire
(
'pjax:start'
,
[
xhr
,
options
])
fire
(
'pjax:send'
,
[
xhr
,
options
])
}
return
pjax
.
xhr
}
var
context
=
options
.
context
=
findContainerFor
(
options
.
container
)
// We want the browser to maintain two separate internal caches: one
// for pjax'd partial page loads and one for normal page loads.
// Without adding this secret parameter, some browsers will often
// confuse the two.
if
(
!
options
.
data
)
options
.
data
=
{}
options
.
data
.
_pjax
=
context
.
selector
function
fire
(
type
,
args
)
{
var
event
=
$
.
Event
(
type
,
{
relatedTarget
:
target
})
context
.
trigger
(
event
,
args
)
return
!
event
.
isDefaultPrevented
()
}
var
timeoutTimer
options
.
beforeSend
=
function
(
xhr
,
settings
)
{
// No timeout for non-GET requests
// Its not safe to request the resource again with a fallback method.
if
(
settings
.
type
!==
'GET'
)
{
settings
.
timeout
=
0
}
xhr
.
setRequestHeader
(
'X-PJAX'
,
'true'
)
xhr
.
setRequestHeader
(
'X-PJAX-Container'
,
context
.
selector
)
if
(
!
fire
(
'pjax:beforeSend'
,
[
xhr
,
settings
]))
return
false
if
(
settings
.
timeout
>
0
)
{
timeoutTimer
=
setTimeout
(
function
()
{
if
(
fire
(
'pjax:timeout'
,
[
xhr
,
options
]))
xhr
.
abort
(
'timeout'
)
},
settings
.
timeout
)
// Clear timeout setting so jquerys internal timeout isn't invoked
settings
.
timeout
=
0
}
options
.
requestUrl
=
parseURL
(
settings
.
url
).
href
}
options
.
complete
=
function
(
xhr
,
textStatus
)
{
if
(
timeoutTimer
)
clearTimeout
(
timeoutTimer
)
fire
(
'pjax:complete'
,
[
xhr
,
textStatus
,
options
])
fire
(
'pjax:end'
,
[
xhr
,
options
])
}
options
.
error
=
function
(
xhr
,
textStatus
,
errorThrown
)
{
var
container
=
extractContainer
(
""
,
xhr
,
options
)
var
allowed
=
fire
(
'pjax:error'
,
[
xhr
,
textStatus
,
errorThrown
,
options
])
if
(
options
.
type
==
'GET'
&&
textStatus
!==
'abort'
&&
allowed
)
{
locationReplace
(
container
.
url
)
}
}
options
.
success
=
function
(
data
,
status
,
xhr
)
{
// If $.pjax.defaults.version is a function, invoke it first.
// Otherwise it can be a static string.
var
currentVersion
=
(
typeof
$
.
pjax
.
defaults
.
version
===
'function'
)
?
$
.
pjax
.
defaults
.
version
()
:
$
.
pjax
.
defaults
.
version
var
latestVersion
=
xhr
.
getResponseHeader
(
'X-PJAX-Version'
)
var
container
=
extractContainer
(
data
,
xhr
,
options
)
// If there is a layout version mismatch, hard load the new url
if
(
currentVersion
&&
latestVersion
&&
currentVersion
!==
latestVersion
)
{
locationReplace
(
container
.
url
)
return
}
// If the new response is missing a body, hard load the page
if
(
!
container
.
contents
)
{
locationReplace
(
container
.
url
)
return
}
pjax
.
state
=
{
id
:
options
.
id
||
uniqueId
(),
url
:
container
.
url
,
title
:
container
.
title
,
container
:
context
.
selector
,
fragment
:
options
.
fragment
,
timeout
:
options
.
timeout
}
if
(
options
.
push
||
options
.
replace
)
{
window
.
history
.
replaceState
(
pjax
.
state
,
container
.
title
,
container
.
url
)
}
// Clear out any focused controls before inserting new page contents.
document
.
activeElement
.
blur
()
if
(
container
.
title
)
document
.
title
=
container
.
title
context
.
html
(
container
.
contents
)
// FF bug: Won't autofocus fields that are inserted via JS.
// This behavior is incorrect. So if theres no current focus, autofocus
// the last field.
//
// http://www.w3.org/html/wg/drafts/html/master/forms.html
var
autofocusEl
=
context
.
find
(
'input[autofocus], textarea[autofocus]'
).
last
()[
0
]
if
(
autofocusEl
&&
document
.
activeElement
!==
autofocusEl
)
{
autofocusEl
.
focus
();
}
executeScriptTags
(
container
.
scripts
)
// Scroll to top by default
if
(
typeof
options
.
scrollTo
===
'number'
)
$
(
window
).
scrollTop
(
options
.
scrollTo
)
// If the URL has a hash in it, make sure the browser
// knows to navigate to the hash.
if
(
hash
!==
''
)
{
// Avoid using simple hash set here. Will add another history
// entry. Replace the url with replaceState and scroll to target
// by hand.
//
// window.location.hash = hash
var
url
=
parseURL
(
container
.
url
)
url
.
hash
=
hash
pjax
.
state
.
url
=
url
.
href
window
.
history
.
replaceState
(
pjax
.
state
,
container
.
title
,
url
.
href
)
var
target
=
$
(
url
.
hash
)
if
(
target
.
length
)
$
(
window
).
scrollTop
(
target
.
offset
().
top
)
}
fire
(
'pjax:success'
,
[
data
,
status
,
xhr
,
options
])
}
// Initialize pjax.state for the initial page load. Assume we're
// using the container and options of the link we're loading for the
// back button to the initial page. This ensures good back button
// behavior.
if
(
!
pjax
.
state
)
{
pjax
.
state
=
{
id
:
uniqueId
(),
url
:
window
.
location
.
href
,
title
:
document
.
title
,
container
:
context
.
selector
,
fragment
:
options
.
fragment
,
timeout
:
options
.
timeout
}
window
.
history
.
replaceState
(
pjax
.
state
,
document
.
title
)
}
// Cancel the current request if we're already pjaxing
var
xhr
=
pjax
.
xhr
if
(
xhr
&&
xhr
.
readyState
<
4
)
{
xhr
.
onreadystatechange
=
$
.
noop
xhr
.
abort
()
}
pjax
.
options
=
options
var
xhr
=
pjax
.
xhr
=
$
.
ajax
(
options
)
if
(
xhr
.
readyState
>
0
)
{
if
(
options
.
push
&&
!
options
.
replace
)
{
// Cache current container element before replacing it
cachePush
(
pjax
.
state
.
id
,
context
.
clone
().
contents
())
window
.
history
.
pushState
(
null
,
""
,
stripPjaxParam
(
options
.
requestUrl
))
}
fire
(
'pjax:start'
,
[
xhr
,
options
])
fire
(
'pjax:send'
,
[
xhr
,
options
])
}
return
pjax
.
xhr
}
// Public: Reload current page with pjax.
//
// Returns whatever $.pjax returns.
function
pjaxReload
(
container
,
options
)
{
var
defaults
=
{
url
:
window
.
location
.
href
,
push
:
false
,
replace
:
true
,
scrollTo
:
false
}
function
pjaxReload
(
container
,
options
)
{
var
defaults
=
{
url
:
window
.
location
.
href
,
push
:
false
,
replace
:
true
,
scrollTo
:
false
}
return
pjax
(
$
.
extend
(
defaults
,
optionsFor
(
container
,
options
)))
}
return
pjax
(
$
.
extend
(
defaults
,
optionsFor
(
container
,
options
)))
}
// Internal: Hard replace current state with url.
//
...
...
@@ -366,133 +367,133 @@ function pjaxReload(container, options) {
// https://bugs.webkit.org/show_bug.cgi?id=93506
//
// Returns nothing.
function
locationReplace
(
url
)
{
window
.
history
.
replaceState
(
null
,
""
,
"#"
)
window
.
location
.
replace
(
url
)
}
function
locationReplace
(
url
)
{
window
.
history
.
replaceState
(
null
,
""
,
"#"
)
window
.
location
.
replace
(
url
)
}
var
initialPop
=
true
var
initialURL
=
window
.
location
.
href
var
initialState
=
window
.
history
.
state
var
initialPop
=
true
var
initialURL
=
window
.
location
.
href
var
initialState
=
window
.
history
.
state
// Initialize $.pjax.state if possible
// Happens when reloading a page and coming forward from a different
// session history.
if
(
initialState
&&
initialState
.
container
)
{
pjax
.
state
=
initialState
}
if
(
initialState
&&
initialState
.
container
)
{
pjax
.
state
=
initialState
}
// Non-webkit browsers don't fire an initial popstate event
if
(
'state'
in
window
.
history
)
{
initialPop
=
false
}
if
(
'state'
in
window
.
history
)
{
initialPop
=
false
}
// popstate handler takes care of the back and forward buttons
//
// You probably shouldn't use pjax on pages with other pushState
// stuff yet.
function
onPjaxPopstate
(
event
)
{
var
state
=
event
.
state
if
(
state
&&
state
.
container
)
{
// When coming forward from a separate history session, will get an
// initial pop with a state we are already at. Skip reloading the current
// page.
if
(
initialPop
&&
initialURL
==
state
.
url
)
return
// If popping back to the same state, just skip.
// Could be clicking back from hashchange rather than a pushState.
if
(
pjax
.
state
.
id
===
state
.
id
)
return
var
container
=
$
(
state
.
container
)
if
(
container
.
length
)
{
var
direction
,
contents
=
cacheMapping
[
state
.
id
]
if
(
pjax
.
state
)
{
// Since state ids always increase, we can deduce the history
// direction from the previous state.
direction
=
pjax
.
state
.
id
<
state
.
id
?
'forward'
:
'back'
// Cache current container before replacement and inform the
// cache which direction the history shifted.
cachePop
(
direction
,
pjax
.
state
.
id
,
container
.
clone
().
contents
())
}
var
popstateEvent
=
$
.
Event
(
'pjax:popstate'
,
{
state
:
state
,
direction
:
direction
})
container
.
trigger
(
popstateEvent
)
var
options
=
{
id
:
state
.
id
,
url
:
state
.
url
,
container
:
container
,
push
:
false
,
fragment
:
state
.
fragment
,
timeout
:
state
.
timeout
,
scrollTo
:
false
}
if
(
contents
)
{
container
.
trigger
(
'pjax:start'
,
[
null
,
options
])
if
(
state
.
title
)
document
.
title
=
state
.
title
container
.
html
(
contents
)
pjax
.
state
=
state
container
.
trigger
(
'pjax:end'
,
[
null
,
options
])
}
else
{
pjax
(
options
)
}
// Force reflow/relayout before the browser tries to restore the
// scroll position.
container
[
0
].
offsetHeight
}
else
{
locationReplace
(
location
.
href
)
}
}
initialPop
=
false
}
function
onPjaxPopstate
(
event
)
{
var
state
=
event
.
state
if
(
state
&&
state
.
container
)
{
// When coming forward from a separate history session, will get an
// initial pop with a state we are already at. Skip reloading the current
// page.
if
(
initialPop
&&
initialURL
==
state
.
url
)
return
// If popping back to the same state, just skip.
// Could be clicking back from hashchange rather than a pushState.
if
(
pjax
.
state
.
id
===
state
.
id
)
return
var
container
=
$
(
state
.
container
)
if
(
container
.
length
)
{
var
direction
,
contents
=
cacheMapping
[
state
.
id
]
if
(
pjax
.
state
)
{
// Since state ids always increase, we can deduce the history
// direction from the previous state.
direction
=
pjax
.
state
.
id
<
state
.
id
?
'forward'
:
'back'
// Cache current container before replacement and inform the
// cache which direction the history shifted.
cachePop
(
direction
,
pjax
.
state
.
id
,
container
.
clone
().
contents
())
}
var
popstateEvent
=
$
.
Event
(
'pjax:popstate'
,
{
state
:
state
,
direction
:
direction
})
container
.
trigger
(
popstateEvent
)
var
options
=
{
id
:
state
.
id
,
url
:
state
.
url
,
container
:
container
,
push
:
false
,
fragment
:
state
.
fragment
,
timeout
:
state
.
timeout
,
scrollTo
:
false
}
if
(
contents
)
{
container
.
trigger
(
'pjax:start'
,
[
null
,
options
])
if
(
state
.
title
)
document
.
title
=
state
.
title
container
.
html
(
contents
)
pjax
.
state
=
state
container
.
trigger
(
'pjax:end'
,
[
null
,
options
])
}
else
{
pjax
(
options
)
}
// Force reflow/relayout before the browser tries to restore the
// scroll position.
container
[
0
].
offsetHeight
}
else
{
locationReplace
(
location
.
href
)
}
}
initialPop
=
false
}
// Fallback version of main pjax function for browsers that don't
// support pushState.
//
// Returns nothing since it retriggers a hard form submission.
function
fallbackPjax
(
options
)
{
var
url
=
$
.
isFunction
(
options
.
url
)
?
options
.
url
()
:
options
.
url
,
method
=
options
.
type
?
options
.
type
.
toUpperCase
()
:
'GET'
var
form
=
$
(
'<form>'
,
{
method
:
method
===
'GET'
?
'GET'
:
'POST'
,
action
:
url
,
style
:
'display:none'
})
if
(
method
!==
'GET'
&&
method
!==
'POST'
)
{
form
.
append
(
$
(
'<input>'
,
{
type
:
'hidden'
,
name
:
'_method'
,
value
:
method
.
toLowerCase
()
}))
}
var
data
=
options
.
data
if
(
typeof
data
===
'string'
)
{
$
.
each
(
data
.
split
(
'&'
),
function
(
index
,
value
)
{
var
pair
=
value
.
split
(
'='
)
form
.
append
(
$
(
'<input>'
,
{
type
:
'hidden'
,
name
:
pair
[
0
],
value
:
pair
[
1
]}))
})
}
else
if
(
typeof
data
===
'object'
)
{
for
(
key
in
data
)
form
.
append
(
$
(
'<input>'
,
{
type
:
'hidden'
,
name
:
key
,
value
:
data
[
key
]}))
}
$
(
document
.
body
).
append
(
form
)
form
.
submit
()
}
function
fallbackPjax
(
options
)
{
var
url
=
$
.
isFunction
(
options
.
url
)
?
options
.
url
()
:
options
.
url
,
method
=
options
.
type
?
options
.
type
.
toUpperCase
()
:
'GET'
var
form
=
$
(
'<form>'
,
{
method
:
method
===
'GET'
?
'GET'
:
'POST'
,
action
:
url
,
style
:
'display:none'
})
if
(
method
!==
'GET'
&&
method
!==
'POST'
)
{
form
.
append
(
$
(
'<input>'
,
{
type
:
'hidden'
,
name
:
'_method'
,
value
:
method
.
toLowerCase
()
}))
}
var
data
=
options
.
data
if
(
typeof
data
===
'string'
)
{
$
.
each
(
data
.
split
(
'&'
),
function
(
index
,
value
)
{
var
pair
=
value
.
split
(
'='
)
form
.
append
(
$
(
'<input>'
,
{
type
:
'hidden'
,
name
:
pair
[
0
],
value
:
pair
[
1
]}))
})
}
else
if
(
typeof
data
===
'object'
)
{
for
(
key
in
data
)
form
.
append
(
$
(
'<input>'
,
{
type
:
'hidden'
,
name
:
key
,
value
:
data
[
key
]}))
}
$
(
document
.
body
).
append
(
form
)
form
.
submit
()
}
// Internal: Generate unique id for state object.
//
...
...
@@ -500,32 +501,32 @@ function fallbackPjax(options) {
// unique across page loads.
//
// Returns Number.
function
uniqueId
()
{
return
(
new
Date
).
getTime
()
}
function
uniqueId
()
{
return
(
new
Date
).
getTime
()
}
// Internal: Strips _pjax param from url
//
// url - String
//
// Returns String.
function
stripPjaxParam
(
url
)
{
return
url
.
replace
(
/
\?
_pjax=
[^
&
]
+&
?
/
,
'?'
)
.
replace
(
/_pjax=
[^
&
]
+&
?
/
,
''
)
.
replace
(
/
[\?
&
]
$/
,
''
)
}
function
stripPjaxParam
(
url
)
{
return
url
.
replace
(
/
\?
_pjax=
[^
&
]
+&
?
/
,
'?'
)
.
replace
(
/_pjax=
[^
&
]
+&
?
/
,
''
)
.
replace
(
/
[\?
&
]
$/
,
''
)
}
// Internal: Parse URL components and returns a Locationish object.
//
// url - String URL
//
// Returns HTMLAnchorElement that acts like Location.
function
parseURL
(
url
)
{
var
a
=
document
.
createElement
(
'a'
)
a
.
href
=
url
return
a
}
function
parseURL
(
url
)
{
var
a
=
document
.
createElement
(
'a'
)
a
.
href
=
url
return
a
}
// Internal: Build options Object for arguments.
//
...
...
@@ -544,25 +545,25 @@ function parseURL(url) {
// // => {container: '#container', push: true}
//
// Returns options Object.
function
optionsFor
(
container
,
options
)
{
// Both container and options
if
(
container
&&
options
)
options
.
container
=
container
function
optionsFor
(
container
,
options
)
{
// Both container and options
if
(
container
&&
options
)
options
.
container
=
container
// First argument is options Object
else
if
(
$
.
isPlainObject
(
container
)
)
options
=
container
// First argument is options Object
else
if
(
$
.
isPlainObject
(
container
)
)
options
=
container
// Only container
else
options
=
{
container
:
container
}
// Only container
else
options
=
{
container
:
container
}
// Find and validate container
if
(
options
.
container
)
options
.
container
=
findContainerFor
(
options
.
container
)
// Find and validate container
if
(
options
.
container
)
options
.
container
=
findContainerFor
(
options
.
container
)
return
options
}
return
options
}
// Internal: Find container element for a variety of inputs.
//
...
...
@@ -572,19 +573,19 @@ function optionsFor(container, options) {
// container - A selector String, jQuery object, or DOM Element.
//
// Returns a jQuery object whose context is `document` and has a selector.
function
findContainerFor
(
container
)
{
container
=
$
(
container
)
if
(
!
container
.
length
)
{
throw
"no pjax container for "
+
container
.
selector
}
else
if
(
container
.
selector
!==
''
&&
container
.
context
===
document
)
{
return
container
}
else
if
(
container
.
attr
(
'id'
)
)
{
return
$
(
'#'
+
container
.
attr
(
'id'
))
}
else
{
throw
"cant get selector for pjax container!"
}
}
function
findContainerFor
(
container
)
{
container
=
$
(
container
)
if
(
!
container
.
length
)
{
throw
"no pjax container for "
+
container
.
selector
}
else
if
(
container
.
selector
!==
''
&&
container
.
context
===
document
)
{
return
container
}
else
if
(
container
.
attr
(
'id'
)
)
{
return
$
(
'#'
+
container
.
attr
(
'id'
))
}
else
{
throw
"cant get selector for pjax container!"
}
}
// Internal: Filter and find all elements matching the selector.
//
...
...
@@ -595,13 +596,13 @@ function findContainerFor(container) {
// selector - String selector to match
//
// Returns a jQuery object.
function
findAll
(
elems
,
selector
)
{
return
elems
.
filter
(
selector
).
add
(
elems
.
find
(
selector
));
}
function
findAll
(
elems
,
selector
)
{
return
elems
.
filter
(
selector
).
add
(
elems
.
find
(
selector
));
}
function
parseHTML
(
html
)
{
return
$
.
parseHTML
(
html
,
document
,
true
)
}
function
parseHTML
(
html
)
{
return
$
.
parseHTML
(
html
,
document
,
true
)
}
// Internal: Extracts container and metadata from response.
//
...
...
@@ -614,69 +615,69 @@ function parseHTML(html) {
// options - pjax options Object
//
// Returns an Object with url, title, and contents keys.
function
extractContainer
(
data
,
xhr
,
options
)
{
var
obj
=
{}
// Prefer X-PJAX-URL header if it was set, otherwise fallback to
// using the original requested url.
obj
.
url
=
stripPjaxParam
(
xhr
.
getResponseHeader
(
'X-PJAX-URL'
)
||
options
.
requestUrl
)
// Attempt to parse response html into elements
if
(
/<html/i
.
test
(
data
))
{
var
$head
=
$
(
parseHTML
(
data
.
match
(
/<head
[^
>
]
*>
([\s\S
.
]
*
)
<
\/
head>/i
)[
0
]))
var
$body
=
$
(
parseHTML
(
data
.
match
(
/<body
[^
>
]
*>
([\s\S
.
]
*
)
<
\/
body>/i
)[
0
]))
}
else
{
var
$head
=
$body
=
$
(
parseHTML
(
data
))
}
// If response data is empty, return fast
if
(
$body
.
length
===
0
)
return
obj
// If there's a <title> tag in the header, use it as
// the page's title.
obj
.
title
=
findAll
(
$head
,
'title'
).
last
().
text
()
if
(
options
.
fragment
)
{
// If they specified a fragment, look for it in the response
// and pull it out.
if
(
options
.
fragment
===
'body'
)
{
var
$fragment
=
$body
}
else
{
var
$fragment
=
findAll
(
$body
,
options
.
fragment
).
first
()
}
if
(
$fragment
.
length
)
{
obj
.
contents
=
$fragment
.
contents
()
// If there's no title, look for data-title and title attributes
// on the fragment
if
(
!
obj
.
title
)
obj
.
title
=
$fragment
.
attr
(
'title'
)
||
$fragment
.
data
(
'title'
)
}
}
else
if
(
!
/<html/i
.
test
(
data
))
{
obj
.
contents
=
$body
}
// Clean up any <title> tags
if
(
obj
.
contents
)
{
// Remove any parent title elements
obj
.
contents
=
obj
.
contents
.
not
(
function
()
{
return
$
(
this
).
is
(
'title'
)
})
// Then scrub any titles from their descendants
obj
.
contents
.
find
(
'title'
).
remove
()
// Gather all script[src] elements
obj
.
scripts
=
findAll
(
obj
.
contents
,
'script[src]'
).
remove
()
obj
.
contents
=
obj
.
contents
.
not
(
obj
.
scripts
)
}
// Trim any whitespace off the title
if
(
obj
.
title
)
obj
.
title
=
$
.
trim
(
obj
.
title
)
return
obj
}
function
extractContainer
(
data
,
xhr
,
options
)
{
var
obj
=
{}
// Prefer X-PJAX-URL header if it was set, otherwise fallback to
// using the original requested url.
obj
.
url
=
stripPjaxParam
(
xhr
.
getResponseHeader
(
'X-PJAX-URL'
)
||
options
.
requestUrl
)
// Attempt to parse response html into elements
if
(
/<html/i
.
test
(
data
))
{
var
$head
=
$
(
parseHTML
(
data
.
match
(
/<head
[^
>
]
*>
([\s\S
.
]
*
)
<
\/
head>/i
)[
0
]))
var
$body
=
$
(
parseHTML
(
data
.
match
(
/<body
[^
>
]
*>
([\s\S
.
]
*
)
<
\/
body>/i
)[
0
]))
}
else
{
var
$head
=
$body
=
$
(
parseHTML
(
data
))
}
// If response data is empty, return fast
if
(
$body
.
length
===
0
)
return
obj
// If there's a <title> tag in the header, use it as
// the page's title.
obj
.
title
=
findAll
(
$head
,
'title'
).
last
().
text
()
if
(
options
.
fragment
)
{
// If they specified a fragment, look for it in the response
// and pull it out.
if
(
options
.
fragment
===
'body'
)
{
var
$fragment
=
$body
}
else
{
var
$fragment
=
findAll
(
$body
,
options
.
fragment
).
first
()
}
if
(
$fragment
.
length
)
{
obj
.
contents
=
$fragment
.
contents
()
// If there's no title, look for data-title and title attributes
// on the fragment
if
(
!
obj
.
title
)
obj
.
title
=
$fragment
.
attr
(
'title'
)
||
$fragment
.
data
(
'title'
)
}
}
else
if
(
!
/<html/i
.
test
(
data
))
{
obj
.
contents
=
$body
}
// Clean up any <title> tags
if
(
obj
.
contents
)
{
// Remove any parent title elements
obj
.
contents
=
obj
.
contents
.
not
(
function
()
{
return
$
(
this
).
is
(
'title'
)
})
// Then scrub any titles from their descendants
obj
.
contents
.
find
(
'title'
).
remove
()
// Gather all script[src] elements
obj
.
scripts
=
findAll
(
obj
.
contents
,
'script[src]'
).
remove
()
obj
.
contents
=
obj
.
contents
.
not
(
obj
.
scripts
)
}
// Trim any whitespace off the title
if
(
obj
.
title
)
obj
.
title
=
$
.
trim
(
obj
.
title
)
return
obj
}
// Load an execute scripts using standard script request.
//
...
...
@@ -686,29 +687,29 @@ function extractContainer(data, xhr, options) {
// scripts - jQuery object of script Elements
//
// Returns nothing.
function
executeScriptTags
(
scripts
)
{
if
(
!
scripts
)
return
function
executeScriptTags
(
scripts
)
{
if
(
!
scripts
)
return
var
existingScripts
=
$
(
'script[src]'
)
var
existingScripts
=
$
(
'script[src]'
)
scripts
.
each
(
function
()
{
var
src
=
this
.
src
var
matchedScripts
=
existingScripts
.
filter
(
function
()
{
return
this
.
src
===
src
})
if
(
matchedScripts
.
length
)
return
scripts
.
each
(
function
()
{
var
src
=
this
.
src
var
matchedScripts
=
existingScripts
.
filter
(
function
()
{
return
this
.
src
===
src
})
if
(
matchedScripts
.
length
)
return
var
script
=
document
.
createElement
(
'script'
)
script
.
type
=
$
(
this
).
attr
(
'type'
)
script
.
src
=
$
(
this
).
attr
(
'src'
)
document
.
head
.
appendChild
(
script
)
})
}
var
script
=
document
.
createElement
(
'script'
)
script
.
type
=
$
(
this
).
attr
(
'type'
)
script
.
src
=
$
(
this
).
attr
(
'src'
)
document
.
head
.
appendChild
(
script
)
})
}
// Internal: History DOM caching class.
var
cacheMapping
=
{}
var
cacheForwardStack
=
[]
var
cacheBackStack
=
[]
var
cacheMapping
=
{}
var
cacheForwardStack
=
[]
var
cacheBackStack
=
[]
// Push previous state id and container contents into the history
// cache. Should be called in conjunction with `pushState` to save the
...
...
@@ -718,19 +719,19 @@ var cacheBackStack = []
// value - DOM Element to cache
//
// Returns nothing.
function
cachePush
(
id
,
value
)
{
cacheMapping
[
id
]
=
value
cacheBackStack
.
push
(
id
)
function
cachePush
(
id
,
value
)
{
cacheMapping
[
id
]
=
value
cacheBackStack
.
push
(
id
)
// Remove all entires in forward history stack after pushing
// a new page.
while
(
cacheForwardStack
.
length
)
delete
cacheMapping
[
cacheForwardStack
.
shift
()]
// Remove all entires in forward history stack after pushing
// a new page.
while
(
cacheForwardStack
.
length
)
delete
cacheMapping
[
cacheForwardStack
.
shift
()]
// Trim back history stack to max cache length.
while
(
cacheBackStack
.
length
>
pjax
.
defaults
.
maxCacheLength
)
delete
cacheMapping
[
cacheBackStack
.
shift
()]
}
// Trim back history stack to max cache length.
while
(
cacheBackStack
.
length
>
pjax
.
defaults
.
maxCacheLength
)
delete
cacheMapping
[
cacheBackStack
.
shift
()]
}
// Shifts cache from directional history cache. Should be
// called on `popstate` with the previous state id and container
...
...
@@ -741,32 +742,32 @@ function cachePush(id, value) {
// value - DOM Element to cache
//
// Returns nothing.
function
cachePop
(
direction
,
id
,
value
)
{
var
pushStack
,
popStack
cacheMapping
[
id
]
=
value
if
(
direction
===
'forward'
)
{
pushStack
=
cacheBackStack
popStack
=
cacheForwardStack
}
else
{
pushStack
=
cacheForwardStack
popStack
=
cacheBackStack
}
pushStack
.
push
(
id
)
if
(
id
=
popStack
.
pop
())
delete
cacheMapping
[
id
]
}
function
cachePop
(
direction
,
id
,
value
)
{
var
pushStack
,
popStack
cacheMapping
[
id
]
=
value
if
(
direction
===
'forward'
)
{
pushStack
=
cacheBackStack
popStack
=
cacheForwardStack
}
else
{
pushStack
=
cacheForwardStack
popStack
=
cacheBackStack
}
pushStack
.
push
(
id
)
if
(
id
=
popStack
.
pop
())
delete
cacheMapping
[
id
]
}
// Public: Find version identifier for the initial page load.
//
// Returns String version or undefined.
function
findVersion
()
{
return
$
(
'meta'
).
filter
(
function
()
{
var
name
=
$
(
this
).
attr
(
'http-equiv'
)
return
name
&&
name
.
toUpperCase
()
===
'X-PJAX-VERSION'
}).
attr
(
'content'
)
}
function
findVersion
()
{
return
$
(
'meta'
).
filter
(
function
()
{
var
name
=
$
(
this
).
attr
(
'http-equiv'
)
return
name
&&
name
.
toUpperCase
()
===
'X-PJAX-VERSION'
}).
attr
(
'content'
)
}
// Install pjax functions on $.pjax to enable pushState behavior.
//
...
...
@@ -777,26 +778,26 @@ function findVersion() {
// $.pjax.enable()
//
// Returns nothing.
function
enable
()
{
$
.
fn
.
pjax
=
fnPjax
$
.
pjax
=
pjax
$
.
pjax
.
enable
=
$
.
noop
$
.
pjax
.
disable
=
disable
$
.
pjax
.
click
=
handleClick
$
.
pjax
.
submit
=
handleSubmit
$
.
pjax
.
reload
=
pjaxReload
$
.
pjax
.
defaults
=
{
timeout
:
650
,
push
:
true
,
replace
:
false
,
type
:
'GET'
,
dataType
:
'html'
,
scrollTo
:
0
,
maxCacheLength
:
20
,
version
:
findVersion
}
$
(
window
).
on
(
'popstate.pjax'
,
onPjaxPopstate
)
}
function
enable
()
{
$
.
fn
.
pjax
=
fnPjax
$
.
pjax
=
pjax
$
.
pjax
.
enable
=
$
.
noop
$
.
pjax
.
disable
=
disable
$
.
pjax
.
click
=
handleClick
$
.
pjax
.
submit
=
handleSubmit
$
.
pjax
.
reload
=
pjaxReload
$
.
pjax
.
defaults
=
{
timeout
:
650
,
push
:
true
,
replace
:
false
,
type
:
'GET'
,
dataType
:
'html'
,
scrollTo
:
0
,
maxCacheLength
:
20
,
version
:
findVersion
}
$
(
window
).
on
(
'popstate.pjax'
,
onPjaxPopstate
)
}
// Disable pushState behavior.
//
...
...
@@ -809,30 +810,30 @@ function enable() {
// $.pjax.disable()
//
// Returns nothing.
function
disable
()
{
$
.
fn
.
pjax
=
function
()
{
return
this
}
$
.
pjax
=
fallbackPjax
$
.
pjax
.
enable
=
enable
$
.
pjax
.
disable
=
$
.
noop
$
.
pjax
.
click
=
$
.
noop
$
.
pjax
.
submit
=
$
.
noop
$
.
pjax
.
reload
=
function
()
{
window
.
location
.
reload
()
}
function
disable
()
{
$
.
fn
.
pjax
=
function
()
{
return
this
}
$
.
pjax
=
fallbackPjax
$
.
pjax
.
enable
=
enable
$
.
pjax
.
disable
=
$
.
noop
$
.
pjax
.
click
=
$
.
noop
$
.
pjax
.
submit
=
$
.
noop
$
.
pjax
.
reload
=
function
()
{
window
.
location
.
reload
()
}
$
(
window
).
off
(
'popstate.pjax'
,
onPjaxPopstate
)
}
$
(
window
).
off
(
'popstate.pjax'
,
onPjaxPopstate
)
}
// Add the state property to jQuery's event object so we can use it in
// $(window).bind('popstate')
if
(
$
.
inArray
(
'state'
,
$
.
event
.
props
)
<
0
)
$
.
event
.
props
.
push
(
'state'
)
if
(
$
.
inArray
(
'state'
,
$
.
event
.
props
)
<
0
)
$
.
event
.
props
.
push
(
'state'
)
// Is pjax supported by this browser?
$
.
support
.
pjax
=
window
.
history
&&
window
.
history
.
pushState
&&
window
.
history
.
replaceState
&&
// pushState isn't reliable on iOS until 5.
!
navigator
.
userAgent
.
match
(
/
((
iPod|iPhone|iPad
)
.+
\b
OS
\s
+
[
1-4
]
|WebApps
\/
.+CFNetwork
)
/
)
$
.
support
.
pjax
=
window
.
history
&&
window
.
history
.
pushState
&&
window
.
history
.
replaceState
&&
// pushState isn't reliable on iOS until 5.
!
navigator
.
userAgent
.
match
(
/
((
iPod|iPhone|iPad
)
.+
\b
OS
\s
+
[
1-4
]
|WebApps
\/
.+CFNetwork
)
/
)
$
.
support
.
pjax
?
enable
()
:
disable
()
$
.
support
.
pjax
?
enable
()
:
disable
()
})(
jQuery
);
framework/widgets/Pjax.php
View file @
04f381eb
...
...
@@ -50,10 +50,16 @@ class Pjax extends Widget
/**
* @var string the jQuery selector of the links that should trigger pjax requests.
* If not set, all links within the enclosed content of Pjax will trigger pjax requests.
* Note that
the pjax response to a link
is a full page, a normal request will be sent again.
* Note that
if the response to the pjax request
is a full page, a normal request will be sent again.
*/
public
$linkSelector
;
/**
* @var string the jQuery selector of the forms whose submissions should trigger pjax requests.
* If not set, all forms with `data-pjax` attribute within the enclosed content of Pjax will trigger pjax requests.
* Note that if the response to the pjax request is a full page, a normal request will be sent again.
*/
public
$formSelector
;
/**
* @var boolean whether to enable push state.
*/
public
$enablePushState
=
true
;
...
...
@@ -148,9 +154,11 @@ class Pjax extends Widget
$this
->
clientOptions
[
'timeout'
]
=
$this
->
timeout
;
$options
=
Json
::
encode
(
$this
->
clientOptions
);
$linkSelector
=
Json
::
encode
(
$this
->
linkSelector
!==
null
?
$this
->
linkSelector
:
'#'
.
$id
.
' a'
);
$formSelector
=
Json
::
encode
(
$this
->
formSelector
!==
null
?
$this
->
formSelector
:
'#'
.
$id
.
' form[data-pjax]'
);
$view
=
$this
->
getView
();
PjaxAsset
::
register
(
$view
);
$js
=
"jQuery(document).pjax(
$linkSelector
,
\"
#
$id
\"
,
$options
);"
;
$js
.=
"jQuery(document).on('submit',
$formSelector
, function (event) {jQuery.pjax.submit(event, '#
$id
');});"
;
$view
->
registerJs
(
$js
);
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment