typecho-core
Version:
An element-drived engine working well with XHR-intensive front-end. Originally designed for Typecho.
1 lines • 12.7 kB
JavaScript
!function(t,e){"object"==typeof exports&&"object"==typeof module?module.exports=e():"function"==typeof define&&define.amd?define([],e):"object"==typeof exports?exports.Seamless=e():t.Seamless=e()}(self,(()=>(()=>{"use strict";var t={d:(e,o)=>{for(var n in o)t.o(o,n)&&!t.o(e,n)&&Object.defineProperty(e,n,{enumerable:!0,get:o[n]})},o:(t,e)=>Object.prototype.hasOwnProperty.call(t,e)},e={};t.d(e,{default:()=>g});const o=function(t,e,o){return t instanceof HTMLCollection||t instanceof NodeList||t instanceof Array?Array.prototype.forEach.call(t,e,o):e.call(o,t)},n=function(t,e,n,s){(e="string"==typeof e?e.split(" "):e).forEach((function(e){o(t,(function(t){t.addEventListener(e,n,s)}))}))},s=function(t,e,n){(e="string"==typeof e?e.split(" "):e).forEach((function(e){const s=document.createEvent("HTMLEvents");s.initEvent(e,!0,!0),s.eventName=e,n&&Object.keys(n).forEach((function(t){s[t]=n[t]})),o(t,(function(t){let e=!1;t.parentNode||t===document||t===window||(e=!0,document.body.appendChild(t)),t.dispatchEvent(s),e&&t.parentNode.removeChild(t)}))}))},i=function(t){const e=t.text||t.textContent||t.innerHTML||"",o=t.src||"",n=t.parentNode||document.querySelector("head")||document.documentElement,s=document.createElement("script");if(e.match("document.write"))return console&&console.log&&console.log("因脚本代码包含 document.write 操作,故其无法被执行,已跳过。",t),!1;if(s.type="text/javascript",s.id=t.id,""!==o&&(s.src=o,s.async=!1),""!==e)try{s.appendChild(document.createTextNode(e))}catch(t){s.text=e}return n.appendChild(s),(n instanceof HTMLHeadElement||n instanceof HTMLBodyElement)&&n.contains(s)&&n.removeChild(s),!0},r=function(t,e,n,s){s=s||document,t.forEach((function(t){o(s.querySelectorAll(t),e,n)}))},c={outerHTML:function(t,e){t.outerHTML=e.outerHTML,this.onSwitch()},innerHTML:function(t,e){t.innerHTML=e.innerHTML,""===e.className?t.removeAttribute("class"):t.className=e.className,this.onSwitch()},switchElementsAlt:function(t,e){if(t.innerHTML=e.innerHTML,e.hasAttributes()){const o=e.attributes;for(let e=0;e<o.length;e++)t.attributes.setNamedItem(o[e].cloneNode())}this.onSwitch()},replaceNode:function(t,e){t.parentNode.replaceChild(e,t),this.onSwitch()}},l=function(t,e,n,s,i,r){const l=[];n.forEach((function(n){const a=s.querySelectorAll(n),u=i.querySelectorAll(n);if(a.length!==u.length)throw`新旧页面元素不匹配。选择器:${n};新元素数量:${a.length},旧元素数量:${u.length}`;o(a,(function(o,s){const i=u[s],a=t[n]?t[n].bind(this,i,o,r,e[n]):c.outerHTML.bind(this,i,o,r);l.push(a)}),this)}),this),this.state.numPendingSwitches=l.length,l.forEach((function(t){t()}))},a=function(){},u=function(){let t=0;return function(){const e="seamless"+(new Date).getTime()+"_"+t;return t++,e}}(),h=function(t){if(null===t||"object"!=typeof t)return t;const e=t.constructor();for(const o in t)t.hasOwnProperty(o)&&(e[o]=t[o]);return e},d="data-seamless-state",f=function(t,e){if(function(t){return t.defaultPrevented||!1===t.returnValue}(e))return;const o=h(this.options);o.requestOptions={requestUrl:t.getAttribute("action")||window.location.href,requestMethod:t.getAttribute("method")||"GET"};const n=document.createElement("a");n.setAttribute("href",o.requestOptions.requestUrl);const s=function(t,e){return t.protocol!==window.location.protocol||t.host!==window.location.host?"external":t.hash&&t.href.replace(t.hash,"")===window.location.href.replace(location.hash,"")?"anchor":t.href===window.location.href.split("#")[0]+"#"?"anchor-empty":e.currentUrlFullReload&&t.href===window.location.href.split("#")[0]?"reload":void 0}(n,o);s?t.setAttribute(d,s):(e.preventDefault(),"multipart/form-data"===t.enctype?o.requestOptions.formData=new FormData(t):o.requestOptions.requestParams=function(t){const e=[],o=t.elements;for(let t=0;t<o.length;t++){const n=o[t],s=n.tagName.toLowerCase();if(n.name&&void 0!==n.attributes&&"button"!==s){const t=n.attributes.type;if(!t||"checkbox"!==t.value&&"radio"!==t.value||n.checked){const t=[];if("select"===s){let e;for(let o=0;o<n.options.length;o++)e=n.options[o],e.selected&&!e.disabled&&t.push(e.hasAttribute("value")?e.value:e.text)}else t.push(n.value);for(let o=0;o<t.length;o++)e.push({name:encodeURIComponent(n.name),value:encodeURIComponent(t[o])})}}}return e}(t),t.setAttribute(d,"submit"),o.triggerElement=t,this.loadUrl(n.href,o))},p="data-seamless-state",m=function(t,e){if(function(t){return t.defaultPrevented||!1===t.returnValue}(e))return;const o=h(this.options),n=function(t,e){return e.which>1||e.metaKey||e.ctrlKey||e.shiftKey||e.altKey?"modifier":t.protocol!==window.location.protocol||t.host!==window.location.host?"external":t.hash&&t.href.replace(t.hash,"")===window.location.href.replace(location.hash,"")?"anchor":t.href===window.location.href.split("#")[0]+"#"?"anchor-empty":void 0}(t,e);if(n)t.setAttribute(p,n);else{if(e.preventDefault(),this.options.currentUrlFullReload&&t.href===window.location.href.split("#")[0])return t.setAttribute(p,"reload"),void this.reload();t.setAttribute(p,"load"),o.triggerElement=t,this.loadUrl(t.href,o)}},w="data-seamless-state",y=function(t){switch(t.tagName.toLowerCase()){case"a":t.hasAttribute(w)||this.attachLink(t);break;case"form":t.hasAttribute(w)||this.attachForm(t);break;default:throw"Seamless 只能应用至 <a> 或 <form> 表单提交控件。"}},b=function(t){this.state={numPendingSwitches:0,href:null,options:null},this.options=function(t){return(t=t||{}).elements=t.elements||"a[href], form[action]",t.selectors=t.selectors||["title",".js-Pjax"],t.switches=t.switches||{},t.switchesOptions=t.switchesOptions||{},t.history=void 0===t.history||t.history,t.analytics="function"==typeof t.analytics||!1===t.analytics?t.analytics:function(){},t.scrollTo=void 0===t.scrollTo?0:t.scrollTo,t.scrollRestoration=void 0===t.scrollRestoration||t.scrollRestoration,t.cacheBust=void 0===t.cacheBust||t.cacheBust,t.timeout=t.timeout||0,t.currentUrlFullReload=void 0!==t.currentUrlFullReload&&t.currentUrlFullReload,t.switches.head||(t.switches.head=c.switchElementsAlt),t.switches.body||(t.switches.body=c.switchElementsAlt),t}(t),this.options.scrollRestoration&&"scrollRestoration"in history&&(history.scrollRestoration="manual"),this.maxUid=this.lastUid=u(),this.parseDOM(document),n(window,"popstate",function(t){if(t.state){const e=h(this.options);e.url=t.state.url,e.title=t.state.title,e.history=!1,e.scrollPos=t.state.scrollPos,t.state.uid<this.lastUid?e.backward=!0:e.forward=!0,this.lastUid=t.state.uid,this.loadUrl(t.state.url,e)}}.bind(this))};b.switches=c,b.prototype={getElements:function(t){return t.querySelectorAll(this.options.elements)},parseDOM:function(t){o(this.getElements(t),y,this)},refresh:function(t){this.parseDOM(t||document)},reload:function(){window.location.reload()},attachLink:function(t){const e=this;t.setAttribute(p,""),n(t,"click",(function(o){m.call(e,t,o)})),n(t,"keyup",function(o){13===o.keyCode&&m.call(e,t,o)}.bind(this))},attachForm:function(t){const e=this;t.setAttribute(d,""),n(t,"submit",(function(o){f.call(e,t,o)}))},forEachSelectors:function(t,e,o){return r.bind(this)(this.options.selectors,t,e,o)},switchSelectors:function(t,e,o,n){return l.bind(this)(this.options.switches,this.options.switchesOptions,t,e,o,n)},latestChance:function(t){window.location=t},onSwitch:function(){s(window,"resize scroll"),this.state.numPendingSwitches--,0===this.state.numPendingSwitches&&this.afterAllSwitches()},loadContent:function(t,e){if("string"!=typeof t)return s(document,"seamless:complete seamless:error",e);const o=document.implementation.createHTMLDocument("seamless");let n=t.match(/<html[^>]+>/gi);if(n&&n.length&&(n=n[0].match(/\s?[a-z:]+(?:=['"][^'">]+['"])*/gi),n.length&&(n.shift(),n.forEach((function(t){const e=t.trim().split("=");1===e.length?o.documentElement.setAttribute(e[0],!0):o.documentElement.setAttribute(e[0],e[1].slice(1,-1))})))),o.documentElement.innerHTML=t,document.activeElement&&function(t,e,o){for(let n=0;n<e.length;n++){const s=t.querySelectorAll(e[n]);for(let t=0;t<s.length;t++)if(s[t].contains(o))return!0}return!1}(document,this.options.selectors,document.activeElement))try{document.activeElement.blur()}catch(t){}!function(t,e="forward"){"function"==typeof t&&(document.startViewTransition?document.startViewTransition({update:t,types:[e]}):t())}((()=>{this.switchSelectors(this.options.selectors,o,document,e)}),e.backward?"backward":"forward")},abortRequest:function(t){t&&t.readyState<4&&(t.onreadystatechange=a,t.abort())},doRequest:function(t,e,o){let n;const s=(e=e||{}).requestOptions||{},i=(s.requestMethod||"GET").toUpperCase(),r=s.requestParams||null,c=s.formData||null;let l=null;const a=new XMLHttpRequest,u=e.timeout||0;if(a.onreadystatechange=function(){4===a.readyState&&(200===a.status?o(a.responseText,a,t,e):0!==a.status&&o(null,a,t,e))},a.onerror=function(n){console.log(n),o(null,a,t,e)},a.ontimeout=function(){o(null,a,t,e)},r&&r.length)switch(n=r.map((function(t){return t.name+"="+t.value})).join("&"),i){case"GET":t=t.split("?")[0],t+="?"+n;break;case"POST":l=n}else c&&(l=c);return e.cacheBust&&(t=function(t,e,o){const[n,s]=t.split(/#/,2),i=new RegExp("([?&])"+encodeURIComponent(e)+"=.*?(&|$)","i"),r=n.includes("?")?"&":"?";let c=n.replace(i,"$1"+encodeURIComponent(e)+"="+encodeURIComponent(o)+"$2");return i.test(n)||(c=n+r+encodeURIComponent(e)+"="+encodeURIComponent(o)),s?c+"#"+s:c}(t,"t",Date.now())),a.open(i,t,!0),a.timeout=u,a.setRequestHeader("X-Requested-With","XMLHttpRequest"),a.setRequestHeader("X-PJAX","true"),a.setRequestHeader("X-PJAX-Selectors",JSON.stringify(e.selectors)),l&&"POST"===i&&!c&&a.setRequestHeader("Content-Type","application/x-www-form-urlencoded"),a.send(l),a},handleResponse:function(t,e,o,n){if((n=h(n||this.options)).request=e,!1===t)return void s(document,"seamless:complete seamless:error",n);const i=window.history.state||{};window.history.replaceState({url:i.url||window.location.href,title:i.title||document.title,uid:i.uid||u(),scrollPos:[document.documentElement.scrollLeft||document.body.scrollLeft,document.documentElement.scrollTop||document.body.scrollTop]},document.title,window.location.href);const r=o;e.responseURL?o!==e.responseURL&&(o=e.responseURL):e.getResponseHeader("X-PJAX-URL")?o=e.getResponseHeader("X-PJAX-URL"):e.getResponseHeader("X-XHR-Redirected-To")&&(o=e.getResponseHeader("X-XHR-Redirected-To"));const c=document.createElement("a");c.href=r;const l=c.hash;c.href=o,l&&!c.hash&&(c.hash=l,o=c.href),this.state.href=o,this.state.options=n;try{this.loadContent(t,n)}catch(t){return s(document,"seamless:error",n),console&&console.error&&console.error("Seamless 元素交换失败:",t),this.latestChance(o)}},loadUrl:function(t,e){e="object"==typeof e?function(t){if(null==t)return null;const e=Object(t);for(let t=1;t<arguments.length;t++){const o=arguments[t];if(null!=o)for(const t in o)Object.prototype.hasOwnProperty.call(o,t)&&(e[t]=o[t])}return e}({},this.options,e):h(this.options),this.abortRequest(this.request),s(document,"seamless:send",e),this.request=this.doRequest(t,e,this.handleResponse.bind(this))},afterAllSwitches:function(){const t=Array.prototype.slice.call(document.querySelectorAll("[autofocus]")).pop();t&&document.activeElement!==t&&t.focus(),this.options.selectors.forEach((function(t){o(document.querySelectorAll(t),(function(t){var e;"script"===(e=t).tagName.toLowerCase()&&i(e),o(e.querySelectorAll("script"),(function(t){t.type&&"text/javascript"!==t.type.toLowerCase()||(t.parentNode&&t.parentNode.removeChild(t),i(t))}))}))}));const e=this.state;if(e.options.history&&(window.history.state||(this.lastUid=this.maxUid=u(),window.history.replaceState({url:window.location.href,title:document.title,uid:this.maxUid,scrollPos:[0,0]},document.title)),this.lastUid=this.maxUid=u(),window.history.pushState({url:e.href,title:e.options.title,uid:this.maxUid,scrollPos:[0,0]},e.options.title,e.href)),this.forEachSelectors((function(t){this.parseDOM(t)}),this),s(document,"seamless:complete seamless:success",e.options),"function"==typeof e.options.analytics&&e.options.analytics(),e.options.history){const t=document.createElement("a");if(t.href=this.state.href,t.hash){let e=t.hash.slice(1);e=decodeURIComponent(e);let o=0,n=document.getElementById(e)||document.getElementsByName(e)[0];if(n&&n.offsetParent)do{o+=n.offsetTop,n=n.offsetParent}while(n);window.scrollTo(0,o)}else!1!==e.options.scrollTo&&(e.options.scrollTo.length>1?window.scrollTo(e.options.scrollTo[0],e.options.scrollTo[1]):window.scrollTo(0,e.options.scrollTo))}else e.options.scrollRestoration&&e.options.scrollPos&&window.scrollTo(e.options.scrollPos[0],e.options.scrollPos[1]);this.state={numPendingSwitches:0,href:null,options:null}}};const g=b;return e.default})()));