2017-02-18 01:56:23 -07:00
'use strict' ;
2017-02-17 21:55:19 -07:00
2015-07-21 11:23:16 -04:00
2019-06-22 21:23:19 -04:00
ajaxify = window . ajaxify || { } ;
2015-07-21 11:23:16 -04:00
2020-09-04 20:53:21 -04:00
( function ( ) {
2016-03-10 19:07:42 +02:00
var apiXHR = null ;
2016-10-23 10:49:44 -04:00
var ajaxifyTimer ;
2015-07-21 11:23:16 -04:00
2016-03-10 19:07:42 +02:00
var retry = true ;
2016-08-05 16:32:44 +03:00
var previousBodyClass = '' ;
2015-07-21 11:23:16 -04:00
2017-12-06 12:31:55 -05:00
ajaxify . count = 0 ;
2015-07-21 11:23:16 -04:00
ajaxify . currentPage = null ;
2015-08-10 17:57:51 -04:00
ajaxify . go = function ( url , callback , quiet ) {
2020-12-30 13:51:45 -05:00
// Automatically reconnect to socket and re-ajaxify on success
2015-07-21 11:23:16 -04:00
if ( ! socket . connected ) {
2020-12-30 13:51:45 -05:00
app . reconnect ( ) ;
2015-07-21 11:23:16 -04:00
if ( ajaxify . reconnectAction ) {
$ ( window ) . off ( 'action:reconnected' , ajaxify . reconnectAction ) ;
}
2016-10-13 11:43:39 +02:00
ajaxify . reconnectAction = function ( e ) {
2015-08-10 17:57:51 -04:00
ajaxify . go ( url , callback , quiet ) ;
2015-07-21 11:23:16 -04:00
$ ( window ) . off ( e ) ;
2015-08-10 17:57:51 -04:00
} ;
2015-07-21 11:23:16 -04:00
$ ( window ) . on ( 'action:reconnected' , ajaxify . reconnectAction ) ;
}
2016-10-23 10:49:44 -04:00
// Abort subsequent requests if clicked multiple times within a short window of time
if ( ajaxifyTimer && ( Date . now ( ) - ajaxifyTimer ) < 500 ) {
return true ;
}
ajaxifyTimer = Date . now ( ) ;
2015-07-21 11:23:16 -04:00
if ( ajaxify . handleRedirects ( url ) ) {
return true ;
}
2015-10-20 17:53:44 -04:00
app . leaveCurrentRoom ( ) ;
2015-07-21 11:23:16 -04:00
$ ( window ) . off ( 'scroll' ) ;
if ( $ ( '#content' ) . hasClass ( 'ajaxifying' ) && apiXHR ) {
apiXHR . abort ( ) ;
}
2019-05-22 13:15:53 -04:00
app . previousUrl = ! [ 'reset' ] . includes ( ajaxify . currentPage ) ? window . location . pathname . slice ( config . relative _path . length ) : app . previousUrl ;
2016-05-03 19:13:10 +03:00
2016-06-20 13:39:08 +03:00
url = ajaxify . start ( url ) ;
2016-06-08 13:34:41 +03:00
2016-08-10 12:29:25 -04:00
// If any listeners alter url and set it to an empty string, abort the ajaxification
2016-08-10 12:50:41 -04:00
if ( url === null ) {
2021-01-26 14:32:50 -05:00
require ( [ 'hooks' ] , function ( hooks ) {
hooks . fire ( 'action:ajaxify.end' , { url : url , tpl _url : ajaxify . data . template . name , title : ajaxify . data . title } ) ;
} ) ;
2016-08-10 12:29:25 -04:00
return false ;
}
2016-08-05 16:32:44 +03:00
previousBodyClass = ajaxify . data . bodyClass ;
2015-07-21 11:23:16 -04:00
$ ( '#footer, #content' ) . removeClass ( 'hide' ) . addClass ( 'ajaxifying' ) ;
2016-10-13 11:43:39 +02:00
ajaxify . loadData ( url , function ( err , data ) {
2016-06-20 14:55:50 +03:00
if ( ! err || ( err && err . data && ( parseInt ( err . data . status , 10 ) !== 302 && parseInt ( err . data . status , 10 ) !== 308 ) ) ) {
ajaxify . updateHistory ( url , quiet ) ;
}
2015-07-21 11:23:16 -04:00
if ( err ) {
return onAjaxError ( err , url , callback , quiet ) ;
}
2016-04-13 11:58:14 -04:00
2016-03-10 19:07:42 +02:00
retry = true ;
2015-07-21 11:23:16 -04:00
2018-06-11 12:17:24 -04:00
renderTemplate ( url , data . templateToRender || data . template . name , data , callback ) ;
2015-07-21 11:23:16 -04:00
} ) ;
return true ;
} ;
2020-09-04 20:53:21 -04:00
// this function is called just once from footer on page load
ajaxify . coldLoad = function ( ) {
var url = ajaxify . start ( window . location . pathname . slice ( 1 ) + window . location . search + window . location . hash ) ;
ajaxify . updateHistory ( url , true ) ;
2020-09-06 22:07:39 -04:00
ajaxify . end ( url , ajaxify . data . template . name ) ;
2020-09-04 20:53:21 -04:00
$ ( window ) . trigger ( 'action:ajaxify.coldLoad' ) ;
} ;
2017-12-06 12:31:55 -05:00
ajaxify . isCold = function ( ) {
return ajaxify . count <= 1 ;
} ;
2016-10-13 11:43:39 +02:00
ajaxify . handleRedirects = function ( url ) {
2017-03-07 16:13:09 +03:00
url = ajaxify . removeRelativePath ( url . replace ( /^\/|\/$/g , '' ) ) . toLowerCase ( ) ;
2018-06-02 15:56:23 -04:00
var isClientToAdmin = url . startsWith ( 'admin' ) && window . location . pathname . indexOf ( config . relative _path + '/admin' ) !== 0 ;
var isAdminToClient = ! url . startsWith ( 'admin' ) && window . location . pathname . indexOf ( config . relative _path + '/admin' ) === 0 ;
2017-03-07 13:38:31 +03:00
2018-04-19 14:24:01 -04:00
if ( isClientToAdmin || isAdminToClient ) {
2018-06-02 15:56:23 -04:00
window . open ( config . relative _path + '/' + url , '_top' ) ;
2015-07-21 11:23:16 -04:00
return true ;
}
return false ;
} ;
2016-10-13 11:43:39 +02:00
ajaxify . start = function ( url ) {
2015-07-21 11:23:16 -04:00
url = ajaxify . removeRelativePath ( url . replace ( /^\/|\/$/g , '' ) ) ;
2016-08-10 12:29:25 -04:00
var payload = {
2017-02-17 19:31:21 -07:00
url : url ,
2016-10-25 21:34:47 +02:00
} ;
2016-08-10 12:29:25 -04:00
$ ( window ) . trigger ( 'action:ajaxify.start' , payload ) ;
2015-07-21 11:23:16 -04:00
2017-12-06 12:31:55 -05:00
ajaxify . count += 1 ;
2016-08-10 12:29:25 -04:00
return payload . url ;
2016-06-20 13:39:08 +03:00
} ;
2016-10-13 11:43:39 +02:00
ajaxify . updateHistory = function ( url , quiet ) {
2016-03-24 13:21:05 +02:00
ajaxify . currentPage = url . split ( /[?#]/ ) [ 0 ] ;
2016-04-21 22:59:42 +03:00
if ( window . history && window . history . pushState ) {
window . history [ ! quiet ? 'pushState' : 'replaceState' ] ( {
2017-02-17 19:31:21 -07:00
url : url ,
2018-06-02 15:56:23 -04:00
} , url , config . relative _path + '/' + url ) ;
2016-04-21 22:59:42 +03:00
}
2015-07-21 11:23:16 -04:00
} ;
function onAjaxError ( err , url , callback , quiet ) {
2016-03-10 19:07:42 +02:00
var data = err . data ;
var textStatus = err . textStatus ;
2015-07-21 11:23:16 -04:00
if ( data ) {
var status = parseInt ( data . status , 10 ) ;
2015-08-28 15:08:21 -04:00
if ( status === 403 || status === 404 || status === 500 || status === 502 || status === 503 ) {
2016-03-10 19:07:42 +02:00
if ( status === 502 && retry ) {
retry = false ;
2016-10-23 17:58:49 -04:00
ajaxifyTimer = undefined ;
2016-03-10 19:07:42 +02:00
return ajaxify . go ( url , callback , quiet ) ;
}
2015-07-21 11:23:16 -04:00
if ( status === 502 ) {
status = 500 ;
}
2015-09-11 18:36:19 -04:00
if ( data . responseJSON ) {
data . responseJSON . config = config ;
}
2016-03-10 19:07:42 +02:00
2015-07-21 11:23:16 -04:00
$ ( '#footer, #content' ) . removeClass ( 'hide' ) . addClass ( 'ajaxifying' ) ;
2016-03-06 00:45:32 +02:00
return renderTemplate ( url , status . toString ( ) , data . responseJSON || { } , callback ) ;
2015-07-21 11:23:16 -04:00
} else if ( status === 401 ) {
app . alertError ( '[[global:please_log_in]]' ) ;
app . previousUrl = url ;
2016-05-03 19:13:10 +03:00
window . location . href = config . relative _path + '/login' ;
2020-12-03 10:29:18 -05:00
} else if ( status === 302 || status === 308 ) {
if ( data . responseJSON && data . responseJSON . external ) {
// this is used by sso plugins to redirect to the auth route
// cant use ajaxify.go for /auth/sso routes
window . location . href = data . responseJSON . external ;
} else if ( typeof data . responseJSON === 'string' ) {
ajaxifyTimer = undefined ;
if ( data . responseJSON . startsWith ( 'http://' ) || data . responseJSON . startsWith ( 'https://' ) ) {
window . location . href = data . responseJSON ;
} else {
ajaxify . go ( data . responseJSON . slice ( 1 ) , callback , quiet ) ;
}
2015-07-21 11:23:16 -04:00
}
}
} else if ( textStatus !== 'abort' ) {
app . alertError ( data . responseJSON . error ) ;
}
}
function renderTemplate ( url , tpl _url , data , callback ) {
$ ( window ) . trigger ( 'action:ajaxify.loadingTemplates' , { } ) ;
2020-12-02 14:14:56 -05:00
require ( [ 'translator' , 'benchpress' ] , function ( translator , Benchpress ) {
Benchpress . render ( tpl _url , data )
. then ( rendered => translator . translate ( rendered ) )
. then ( function ( translated ) {
translated = translator . unescape ( translated ) ;
$ ( 'body' ) . removeClass ( previousBodyClass ) . addClass ( data . bodyClass ) ;
$ ( '#content' ) . html ( translated ) ;
ajaxify . end ( url , tpl _url ) ;
if ( typeof callback === 'function' ) {
callback ( ) ;
}
$ ( '#content, #footer' ) . removeClass ( 'ajaxifying' ) ;
// Only executed on ajaxify. Otherwise these'd be in ajaxify.end()
updateTitle ( data . title ) ;
updateTags ( ) ;
} ) ;
} ) ;
2015-07-21 11:23:16 -04:00
}
2020-09-06 22:36:09 -04:00
function updateTitle ( title ) {
if ( ! title ) {
return ;
}
require ( [ 'translator' ] , function ( translator ) {
title = config . titleLayout . replace ( /{/g , '{' ) . replace ( /}/g , '}' )
. replace ( '{pageTitle}' , function ( ) { return title ; } )
. replace ( '{browserTitle}' , function ( ) { return config . browserTitle ; } ) ;
// Allow translation strings in title on ajaxify (#5927)
title = translator . unescape ( title ) ;
2020-12-02 20:46:57 -05:00
var data = { title : title } ;
$ ( window ) . trigger ( 'action:ajaxify.updateTitle' , data ) ;
translator . translate ( data . title , function ( translated ) {
2020-09-06 22:36:09 -04:00
window . document . title = $ ( '<div></div>' ) . html ( translated ) . text ( ) ;
} ) ;
} ) ;
}
function updateTags ( ) {
var metaWhitelist = [ 'title' , 'description' , /og:.+/ , /article:.+/ ] . map ( function ( val ) {
return new RegExp ( val ) ;
} ) ;
var linkWhitelist = [ 'canonical' , 'alternate' , 'up' ] ;
// Delete the old meta tags
Array . prototype . slice
. call ( document . querySelectorAll ( 'head meta' ) )
. filter ( function ( el ) {
var name = el . getAttribute ( 'property' ) || el . getAttribute ( 'name' ) ;
return metaWhitelist . some ( function ( exp ) {
return ! ! exp . test ( name ) ;
} ) ;
} )
. forEach ( function ( el ) {
document . head . removeChild ( el ) ;
} ) ;
// Add new meta tags
ajaxify . data . _header . tags . meta
. filter ( function ( tagObj ) {
var name = tagObj . name || tagObj . property ;
return metaWhitelist . some ( function ( exp ) {
return ! ! exp . test ( name ) ;
} ) ;
} )
. forEach ( function ( tagObj ) {
var metaEl = document . createElement ( 'meta' ) ;
Object . keys ( tagObj ) . forEach ( function ( prop ) {
metaEl . setAttribute ( prop , tagObj [ prop ] ) ;
} ) ;
document . head . appendChild ( metaEl ) ;
} ) ;
// Delete the old link tags
Array . prototype . slice
. call ( document . querySelectorAll ( 'head link' ) )
. filter ( function ( el ) {
var name = el . getAttribute ( 'rel' ) ;
return linkWhitelist . some ( function ( item ) {
return item === name ;
} ) ;
} )
. forEach ( function ( el ) {
document . head . removeChild ( el ) ;
} ) ;
// Add new link tags
ajaxify . data . _header . tags . link
. filter ( function ( tagObj ) {
return linkWhitelist . some ( function ( item ) {
return item === tagObj . rel ;
} ) ;
} )
. forEach ( function ( tagObj ) {
var linkEl = document . createElement ( 'link' ) ;
Object . keys ( tagObj ) . forEach ( function ( prop ) {
linkEl . setAttribute ( prop , tagObj [ prop ] ) ;
} ) ;
document . head . appendChild ( linkEl ) ;
} ) ;
}
2016-10-13 11:43:39 +02:00
ajaxify . end = function ( url , tpl _url ) {
2020-10-18 01:33:03 -04:00
// Scroll back to top of page
if ( ! ajaxify . isCold ( ) ) {
window . scrollTo ( 0 , 0 ) ;
}
2018-11-17 14:07:48 -05:00
ajaxify . loadScript ( tpl _url , function done ( ) {
2021-01-26 14:32:50 -05:00
require ( [ 'hooks' ] , function ( hooks ) {
hooks . fire ( 'action:ajaxify.end' , { url : url , tpl _url : tpl _url , title : ajaxify . data . title } ) ;
} ) ;
2018-11-17 14:07:48 -05:00
} ) ;
ajaxify . widgets . render ( tpl _url ) ;
2015-07-21 11:23:16 -04:00
2017-02-18 12:30:49 -07:00
$ ( window ) . trigger ( 'action:ajaxify.contentLoaded' , { url : url , tpl : tpl _url } ) ;
2015-07-21 11:23:16 -04:00
app . processPage ( ) ;
} ;
2016-10-13 11:43:39 +02:00
ajaxify . parseData = function ( ) {
2016-08-26 16:39:03 +03:00
var dataEl = $ ( '#ajaxify-data' ) ;
if ( dataEl . length ) {
ajaxify . data = JSON . parse ( dataEl . text ( ) ) ;
dataEl . remove ( ) ;
}
} ;
2016-10-13 11:43:39 +02:00
ajaxify . removeRelativePath = function ( url ) {
2018-06-02 15:56:23 -04:00
if ( url . startsWith ( config . relative _path . slice ( 1 ) ) ) {
url = url . slice ( config . relative _path . length ) ;
2015-07-21 11:23:16 -04:00
}
return url ;
} ;
2016-10-13 11:43:39 +02:00
ajaxify . refresh = function ( callback ) {
2016-03-24 13:21:05 +02:00
ajaxify . go ( ajaxify . currentPage + window . location . search + window . location . hash , callback , true ) ;
2015-07-21 11:23:16 -04:00
} ;
2016-10-13 11:43:39 +02:00
ajaxify . loadScript = function ( tpl _url , callback ) {
2015-07-21 11:23:16 -04:00
var location = ! app . inAdmin ? 'forum/' : '' ;
2016-02-03 18:21:34 +02:00
if ( tpl _url . startsWith ( 'admin' ) ) {
location = '' ;
}
2021-01-27 12:05:26 -05:00
2021-01-27 14:32:00 -05:00
const proceed = ( hooks , module ) => {
2021-01-27 12:05:26 -05:00
if ( module && module . init ) {
module . init ( ) ;
}
hooks . fire ( 'static:script.init' , { tpl _url } ) . then ( ajaxify . loadExtraScripts . bind ( null , tpl _url , callback ) ) ;
2021-01-27 14:32:00 -05:00
} ;
require ( [ 'hooks' , location + tpl _url ] , proceed , ( ) => {
// Module doesn't exist or didn't load, proceed without it
require ( [ 'hooks' ] , proceed ) ;
2021-01-27 12:05:26 -05:00
} ) ;
} ;
2021-01-27 16:10:00 -05:00
// TODO: Deprecate in v1.18.0
2021-01-27 12:05:26 -05:00
ajaxify . loadExtraScripts = ( tpl _url , callback ) => {
2016-01-15 22:06:30 +02:00
var data = {
tpl _url : tpl _url ,
2021-01-27 12:05:26 -05:00
scripts : [ ] ,
2016-01-15 22:06:30 +02:00
} ;
$ ( window ) . trigger ( 'action:script.load' , data ) ;
2017-01-06 15:31:02 -05:00
// Require and parse modules
2017-02-09 15:50:05 -07:00
var outstanding = data . scripts . length ;
2021-01-27 12:08:25 -05:00
if ( ! outstanding ) {
return callback ( ) ;
}
2021-01-27 12:05:26 -05:00
if ( outstanding && ! app . flags . actionScriptLoadDeprecation ) {
console . group ( 'Deprecation Notice' ) ;
console . warn ( 'The "action:script.load" event has been deprecated and will be removed in NodeBB v1.18.0. Please attach a listener to the "static:script.init" client-side hook instead' ) ;
data . scripts . forEach ( ( script ) => {
console . info ( ` Affected script: ${ typeof script === 'function' ? script . name || 'anonymous ' + script . toString ( ) : script } ` ) ;
} ) ;
console . groupEnd ( ) ;
app . flags . actionScriptLoadDeprecation = 1 ;
}
2017-02-09 15:50:05 -07:00
data . scripts . map ( function ( script ) {
if ( typeof script === 'function' ) {
return function ( next ) {
script ( ) ;
next ( ) ;
} ;
2015-07-21 11:23:16 -04:00
}
2017-02-09 15:50:05 -07:00
if ( typeof script === 'string' ) {
return function ( next ) {
require ( [ script ] , function ( script ) {
if ( script && script . init ) {
script . init ( ) ;
}
next ( ) ;
} , function ( ) {
// ignore 404 error
next ( ) ;
} ) ;
} ;
2015-07-21 11:23:16 -04:00
}
2017-02-09 15:50:05 -07:00
return null ;
} ) . filter ( Boolean ) . forEach ( function ( fn ) {
fn ( function ( ) {
outstanding -= 1 ;
if ( outstanding === 0 ) {
callback ( ) ;
}
} ) ;
2015-07-21 11:23:16 -04:00
} ) ;
} ;
2016-10-13 11:43:39 +02:00
ajaxify . loadData = function ( url , callback ) {
2015-07-21 11:23:16 -04:00
url = ajaxify . removeRelativePath ( url ) ;
2017-02-18 12:30:49 -07:00
$ ( window ) . trigger ( 'action:ajaxify.loadingData' , { url : url } ) ;
2015-07-21 11:23:16 -04:00
apiXHR = $ . ajax ( {
2018-06-02 15:56:23 -04:00
url : config . relative _path + '/api/' + url ,
2015-07-21 11:23:16 -04:00
cache : false ,
2016-10-25 16:52:03 -04:00
headers : {
2017-02-17 19:31:21 -07:00
'X-Return-To' : app . previousUrl ,
2016-10-25 16:52:03 -04:00
} ,
2017-06-30 23:38:31 -06:00
success : function ( data , textStatus , xhr ) {
2015-07-21 11:23:16 -04:00
if ( ! data ) {
return ;
}
2015-08-27 19:22:51 -04:00
2017-06-30 23:38:31 -06:00
if ( xhr . getResponseHeader ( 'X-Redirect' ) ) {
return callback ( {
data : {
status : 302 ,
responseJSON : data ,
} ,
textStatus : 'error' ,
} ) ;
}
2015-07-21 11:23:16 -04:00
ajaxify . data = data ;
2015-08-27 19:22:51 -04:00
data . config = config ;
2017-02-18 12:30:49 -07:00
$ ( window ) . trigger ( 'action:ajaxify.dataLoaded' , { url : url , data : data } ) ;
2015-07-21 11:23:16 -04:00
2015-10-15 13:00:32 -04:00
callback ( null , data ) ;
2015-07-21 11:23:16 -04:00
} ,
2016-10-13 11:43:39 +02:00
error : function ( data , textStatus ) {
2015-07-21 11:23:16 -04:00
if ( data . status === 0 && textStatus === 'error' ) {
data . status = 500 ;
2018-05-29 10:27:56 -04:00
data . responseJSON = data . responseJSON || { } ;
data . responseJSON . error = '[[error:no-connection]]' ;
2015-07-21 11:23:16 -04:00
}
callback ( {
data : data ,
2017-02-17 19:31:21 -07:00
textStatus : textStatus ,
2015-07-21 11:23:16 -04:00
} ) ;
2017-02-17 19:31:21 -07:00
} ,
2015-07-21 11:23:16 -04:00
} ) ;
} ;
2016-10-13 11:43:39 +02:00
ajaxify . loadTemplate = function ( template , callback ) {
2020-08-10 10:22:47 -04:00
require ( [ config . assetBaseUrl + '/templates/' + template + '.js' ] , callback , function ( err ) {
2017-08-24 17:26:50 -06:00
console . error ( 'Unable to load template: ' + template ) ;
throw err ;
} ) ;
2015-07-21 11:23:16 -04:00
} ;
2020-09-04 20:53:21 -04:00
require ( [ 'translator' , 'benchpress' ] , function ( translator , Benchpress ) {
translator . translate ( '[[error:no-connection]]' ) ;
2020-12-28 11:44:14 -05:00
translator . translate ( '[[error:socket-reconnect-failed]]' ) ;
2020-12-30 13:51:45 -05:00
translator . translate ( ` [[global:reconnecting-message, ${ config . siteTitle } ]] ` ) ;
2020-09-04 20:53:21 -04:00
Benchpress . registerLoader ( ajaxify . loadTemplate ) ;
2020-09-06 21:55:31 -04:00
Benchpress . setGlobal ( 'config' , config ) ;
2020-09-04 20:53:21 -04:00
} ) ;
} ( ) ) ;
$ ( document ) . ready ( function ( ) {
$ ( window ) . on ( 'popstate' , function ( ev ) {
ev = ev . originalEvent ;
if ( ev !== null && ev . state ) {
2020-11-26 12:23:41 -05:00
if ( ev . state . url === null && ev . state . returnPath !== undefined ) {
2020-09-04 20:53:21 -04:00
window . history . replaceState ( {
url : ev . state . returnPath ,
} , ev . state . returnPath , config . relative _path + '/' + ev . state . returnPath ) ;
2020-11-26 12:23:41 -05:00
} else if ( ev . state . url !== undefined ) {
ajaxify . go ( ev . state . url , function ( ) {
$ ( window ) . trigger ( 'action:popstate' , { url : ev . state . url } ) ;
} , true ) ;
2020-09-04 20:53:21 -04:00
}
}
} ) ;
2015-07-21 11:23:16 -04:00
function ajaxifyAnchors ( ) {
function hrefEmpty ( href ) {
return href === undefined || href === '' || href === 'javascript:;' ;
}
2020-09-04 20:53:21 -04:00
var location = document . location || window . location ;
var rootUrl = location . protocol + '//' + ( location . hostname || location . host ) + ( location . port ? ':' + location . port : '' ) ;
2016-09-02 14:16:46 -04:00
var contentEl = document . getElementById ( 'content' ) ;
2015-07-21 11:23:16 -04:00
// Enhancing all anchors to ajaxify...
$ ( document . body ) . on ( 'click' , 'a' , function ( e ) {
2016-07-28 11:51:06 -04:00
var _self = this ;
2017-02-18 18:55:33 -07:00
if ( this . target !== '' || ( this . protocol !== 'http:' && this . protocol !== 'https:' ) ) {
return ;
}
2020-09-17 23:11:04 -04:00
var $this = $ ( this ) ;
var href = $this . attr ( 'href' ) ;
2018-06-02 15:56:23 -04:00
var internalLink = utils . isInternalURI ( this , window . location , config . relative _path ) ;
2017-02-18 18:55:33 -07:00
2016-10-13 11:43:39 +02:00
var process = function ( ) {
2016-07-28 11:51:06 -04:00
if ( ! e . ctrlKey && ! e . shiftKey && ! e . metaKey && e . which === 1 ) {
if ( internalLink ) {
2018-06-02 15:56:23 -04:00
var pathname = this . href . replace ( rootUrl + config . relative _path + '/' , '' ) ;
2016-07-28 11:51:06 -04:00
// Special handling for urls with hashes
if ( window . location . pathname === this . pathname && this . hash . length ) {
window . location . hash = this . hash ;
2017-02-18 14:32:35 -07:00
} else if ( ajaxify . go ( pathname ) ) {
e . preventDefault ( ) ;
2016-07-28 11:51:06 -04:00
}
2018-06-02 15:56:23 -04:00
} else if ( window . location . pathname !== config . relative _path + '/outgoing' ) {
2016-09-02 14:16:46 -04:00
if ( config . openOutgoingLinksInNewTab && $ . contains ( contentEl , this ) ) {
2018-06-03 21:59:58 -04:00
var externalTab = window . open ( ) ;
externalTab . opener = null ;
externalTab . location = this . href ;
2016-07-28 11:51:06 -04:00
e . preventDefault ( ) ;
} else if ( config . useOutgoingLinksPage ) {
2017-11-02 13:35:49 -04:00
var safeUrls = config . outgoingLinksWhitelist . trim ( ) . split ( /[\s,]+/g ) . filter ( Boolean ) ;
2017-03-10 14:03:07 -05:00
var href = this . href ;
2017-11-02 13:35:49 -04:00
if ( ! safeUrls . length || ! safeUrls . some ( function ( url ) { return href . indexOf ( url ) !== - 1 ; } ) ) {
2017-03-10 14:03:07 -05:00
ajaxify . go ( 'outgoing?url=' + encodeURIComponent ( href ) ) ;
e . preventDefault ( ) ;
}
2016-07-28 11:51:06 -04:00
}
}
}
} ;
2020-09-17 23:11:04 -04:00
if ( $this . attr ( 'data-ajaxify' ) === 'false' ) {
2016-03-07 15:37:14 -05:00
if ( ! internalLink ) {
return ;
}
2017-02-18 14:27:26 -07:00
return e . preventDefault ( ) ;
2016-03-07 15:37:14 -05:00
}
2016-07-28 11:51:06 -04:00
// Default behaviour for rss feeds
2020-09-17 23:11:04 -04:00
if ( internalLink && href && href . endsWith ( '.rss' ) ) {
2016-06-09 14:28:37 -04:00
return ;
}
2019-07-12 14:48:30 -04:00
// Default behaviour for sitemap
2020-09-17 23:11:04 -04:00
if ( internalLink && href && String ( _self . pathname ) . startsWith ( config . relative _path + '/sitemap' ) && href . endsWith ( '.xml' ) ) {
2019-07-12 14:48:30 -04:00
return ;
}
2018-04-19 14:24:01 -04:00
// Default behaviour for uploads and direct links to API urls
2018-04-20 10:35:49 -04:00
if ( internalLink && [ '/uploads' , '/assets/uploads/' , '/api/' ] . some ( function ( prefix ) {
2018-04-19 14:24:01 -04:00
return String ( _self . pathname ) . startsWith ( config . relative _path + prefix ) ;
} ) ) {
return ;
}
2020-09-17 23:11:04 -04:00
if ( hrefEmpty ( this . href ) || this . protocol === 'javascript:' || href === '#' || href === '' ) {
2015-07-21 11:23:16 -04:00
return e . preventDefault ( ) ;
}
2016-07-28 11:51:06 -04:00
if ( app . flags && app . flags . hasOwnProperty ( '_unsaved' ) && app . flags . _unsaved === true ) {
2017-05-29 15:34:09 -04:00
if ( e . ctrlKey ) {
return ;
}
2020-09-04 20:53:21 -04:00
bootbox . confirm ( '[[global:unsaved-changes]]' , function ( navigate ) {
if ( navigate ) {
app . flags . _unsaved = false ;
process . call ( _self ) ;
}
2016-07-28 11:51:06 -04:00
} ) ;
return e . preventDefault ( ) ;
2015-07-21 11:23:16 -04:00
}
2016-07-28 11:51:06 -04:00
process . call ( _self ) ;
2015-07-21 11:23:16 -04:00
} ) ;
}
if ( window . history && window . history . pushState ) {
// Progressive Enhancement, ajaxify available only to modern browsers
ajaxifyAnchors ( ) ;
}
2017-02-18 02:30:48 -07:00
} ) ;